sys-con books rich internet applications with adobe flex & java

701
RIA WITH ADOBE FLEX AND JAVA iii WRITTEN BY YAKOV FAIN, DR. VICTOR RASPUTNIS & ANATOLE TARTAKOVSKY SYS-CON Media Woodcliff Lake, NJ 07677 First Edition FLEX & JAVA WITH ADOBE RICH INTERNET APPLICATIONS SECRETS OF THE MASTERS ® TM TM

Upload: pepon2014

Post on 24-Dec-2015

216 views

Category:

Documents


0 download

DESCRIPTION

SYS-CON Books Rich Internet Applications With Adobe Flex & Java

TRANSCRIPT

Page 1: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA iii

WRITTEN BY YAKOV FAIN, DR. VICTOR RASPUTNIS & ANATOLE TARTAKOVSKY

SYS-CON MediaWoodcliff Lake, NJ 07677

First Edition

FLEX & JAVAWITH ADOBE

RICH INTERNET APPLICATIONS

SECRETS OF THE MASTERS

®

TM TM

Page 2: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

iv RIA WITH ADOBE FLEX AND JAVA

Rich Internet Applications with Adobe® Flex™ & Java™

Secrets of the Masters

1st Edition

SYS-CON Books/2007

All rights reserved.

Copyright © 2007 by SYS-CON Media

Managing Editor: Nancy Valentine

Cover Design: Abraham Addo

Layout and Design: Abraham Addo

Produced by SYS-CON Media

This book may not be reproduced in whole or in part in any form or by any means,

electronic or mechanical including photocopy or any information storage or

retrieval system, without written permission. For further information

about SYS-CON Books and magazines write:

SYS-CON Media, 577 Chestnut Ridge Road, Woodcliff Lake, NJ 07677.

Adobe and Adobe products are either registered trademark or trademarks of Adobe

Systems Incorporated in the United States and/or other countries.

Java and Java-based marks are trademarks or registered trademarks of Sun

Microsystems, Inc., in the United States and other countries

Throughout this book, tradenames and trademarks of some companies and products

have been used, and no such uses are intended to convey endorsement of or other

affiliations with the book.

SYS-CON, the editors, and the authors of this book specifically disclaim all other

warranties, express or implied, including but not limited to implied warranties of

merchantability and fitness for a particular purpose with respect to the sample code,

applications or programs, the program listings in the book, and/or the techniques

described in the book, and in no event shall SYS-CON, the editors and/or the authors

be liable for any loss of profit or any other commercial damage, including but not

limited to special, incidental, consequential, or other damages.

ISBN 0-9777622-2-X

Published and printed in the United States of America

0 9 8 7 6 5 4 3 2

Page 3: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA vii

About the Authors

Yakov Fain is a Managing Principal of Farata Systems. He’s responsible for the enterprise archi-tecture and emerging technologies. Yakov has authored several Java books, dozens of technical articles, and his blog is hugely popular. Sun Microsystems has awarded Yakov with the title Java Champion. He leads the Princeton Java Users Group. Yakov holds a BS and an MS in Applied Math and is an Adobe Certified Flex Instructor. You can reach him at [email protected].

Dr. Victor Rasputnis is a Managing Principal of Farata Systems. He’s responsible for providing ar-chitectural design, implementation management, and mentoring to companies migrating to XML Internet technologies. He holds a PhD in computer science from the Moscow Institute of Robotics. You can reach him at [email protected].

Anatole Tartakovsky is a Managing Principal of Farata Systems. He’s responsible for the creation of frameworks and reusable components. Anatole has authored a number of books and articles on AJAX, XML, the Internet, and client/server technologies. He holds an MS in mathematics. You can reach him at [email protected].

Ben Stucki is a software engineer at Atellis, a Washington, D.C., based firm that specializes in de-veloping rich Internet applications and products. As part of the core development team at Atellis, Ben mixes his knowledge of back-end application development and interactive user interfaces to engineer next-generation Flex applications and components. Ben is also an active member of the Flash and Flex communities and manages a number of open source projects.

The authors frequently blog at the following locations:• Flex Blog: http://flexblog.faratasystems.com/ • Yakov Fain’s blog: http://yakovfain.javadevelopersjournal.com/• Ben Stucki’s blog: http://blog.benstucki.net/

ABOUT THE AUTHORS

Page 4: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA ix

TABLE OF CONTENTS

CONTENTS

ACKNOWLEDGMENTS xix

FOREWORDS xxi

CHAPTER 1 1

Architecture of Rich Internet Applications 1

RIA Platforms: The Major Choices 4

Adobe Flex 2 4

Java 6

WPF 7

AJAX 7

AJAX Shortcomings 8

Other RIA Solutions 10

OpenLaszlo 10

GWT 10

Nexaweb 11

Canoo 11

Backbase 11

Apollo, Desktop 2.0, and the Bright Future 12

Some Pragmatic Flex/Java Considerations 13

The Learning Curve: Reuse of Skills and Code 13

Page 5: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

x RIA WITH ADOBE FLEX AND JAVA

TABLE OF CONTENTSTABLE OF CONTENTS

Application Security 13

Flex GUI Performance 14

Shorter Development Cycle 14

Room for Improvement 14

Connection Management 14

Flex and Agile Development 15

Summary 16

CHAPTER 2 19

Getting Familiar with Flex 19

Free Flex 2 Ingredients 20

Downloading the Flex 2 Framework 21

Hello World in MXML 22

Specifying Compiler Options 24

Building and Deploying Applications with Ant 25

Building HelloWorld with Ant 25

Frame Rate 27

Namespaces in MXML 29

From Hello World to a Calculator 30

Adding Some ActionScript 33

The First Date with Events 34

Why ActionScript 3.0? 36

Comparing ActionScript 3.0 and Java 5 37

Flex Framework API Documentation 42

Separating Design and Development 43

Working with Display Objects in ActionScript 45

The Application Loading Process 45

Summary 47

Endnotes 47

CHAPTER 3 49

Flex Builder Development Environment 49

Installing and Configuring Flex Builder 50

Yet Another Hello World 50

Working with the Source Panel 55

Running HelloWorld 56

Building the Project 57

Flex Builder-Generated Files 57

Running Applications in Flex Builder 58

Simple Event Processing 59

Page 6: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA xi

TABLE OF CONTENTSTABLE OF CONTENTS

A Pure MXML Version 59

Specifying Events Handlers 60

Layouts 61

View States and Transitions 63

Fine Tuning Flex Builder 65

Debugging with Flex Builder 66

Project Types and Data Access 68

Generated ActionScript 69

Flex Builder Tips and Keyboard Shortcuts 70

Using the Subversion Version Control System 73

Summary 76

CHAPTER 4 79

Learning Flex Through Applications 79

ActionScript Dynamic Classes 80

Methods, Functions, and Closures 83

Function Parameters 84

Getters and Setters 85

Functions as Objects 86

Closures 86

Asynchronous Programming 91

Data Binding 92

Binding in MXML 93

Binding Expressions 94

What’s Under the Hood? 95

Binding in ActionScript 97

Binding Inside a String and Application Parameters 97

Is Data Binding a Silver Bullet? 99

Program in Style or an Elevator Pitch 100

From COBOL to Flex 101

From Java to Flex 101

From Smalltalk to Flex 103

Object-Oriented ActionScript 104

Program Design with Interfaces and Polymorphism 106

Polymorphism Without Interfaces 110

Namespaces in ActionScript 112

Using Flex with JavaServer Pages 115

Retrieving Data from JSP 115

Sending Data from Flex to JSP 118

E4X, Data Binding, and Regular Expressions 122

Page 7: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

xii RIA WITH ADOBE FLEX AND JAVA

Collections, Filters, and Master-Detail 126

Adding XMLListCollection 130

Filtering 130

Master-Detail Relationships 132

Adding a Data Feed 134

Events 138

Event Flow 139

Event Propagation or Bubbling 140

Custom Events 146

Let’s Go Through the Sequence of Events 150

Sending Data Using Custom Events 150

Summary 153

Endnotes 153

CHAPTER 5 155

A Complete Application with RPC Communications and JMS 155

Multi-Tier Application Development with Flex 156

Designing a Stock Portfolio Application 156

Adding a Data Grid 159

Adding the Charting Component 167

Chart/DataGrid Toggling 171

Dealing with Financial News 178

Configuring the Server-Side Destination and Proxy 181

Processing the News Feed 181

Introducing Item Renderers 183

Programming Master-Detail Relationships 185

Adding the JMS Feed to the Stock Portfolio 189

Introduction to the Java Naming and Directory Interface 189

Introduction to the Java Messaging Service 190

Two Modes of Message Delivery 190

JMS Classes and Terms 191

Types of Messages 191

How to Publish a Message 191

How to Subscribe for a Topic 192

Integrating Flex and Java Messaging Services 193

Configuring Flex Messaging Destination 193

Configuring ActiveMQ JMS 195

Writing the TickerFeed Java Program 196

Modifying the Flex Client to Consume Messages 199

Summary 202

TABLE OF CONTENTSTABLE OF CONTENTS

Page 8: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA xiii

TABLE OF CONTENTSTABLE OF CONTENTS

CHAPTER 6 205

End-to-End Rapid Application Development with Flex Data Management Services 205

Flex Data Management Services: Flex Remoting on Steroids 206

Flex Data Services and Automation: Problem Statement and Solution 206

A “Manual” FDS Application 207

Building the Client Application 208

Creating Assembler and DTO Classes 212

Implementing the Fill-Method of the DataServices Data Access Object 215

Implementing the Sync-Method of FDS Data Access Object 217

Implementing Update, Delete and Insert Methods 218

Introducing Metadata 221

Introducing Templates 222

Metadata for Input Parameters 224

Templates for Implementing the Fill Method 224

Completing the Fill Method 226

Setting JDBC Statement Parameters 229

Reading the Result Set Record 229

Templates for Implementing Sync-Method 232

Completing the Sync Method 235

The Template for the doCreate() Method 238

Who Owns the DAOFlex Templates? 242

Rapid Application Development with DAOFlex 242

DAOFlex Directory Structure and Configuration Files 243

DAOFlex Project Setup 245

Running the DAOFlex Code Generator 246

Testing and Using DAOFlex Output 248

Summary 248

Endnote 249

CHAPTER 7 251

How to Write Your Own Data Management Services 251

Setting the Scene 252

Introducing Destination-Aware Collections 253

Making a Destination-Aware Collection 255

Sensing Collection Changes 259

Anatomy of Managed ActionScript Objects 262

The Two Faces of ChangedObject 266

Tracking Collection Changes 267

Making Collection Updateable 278

Page 9: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

xiv RIA WITH ADOBE FLEX AND JAVA

Taking Care of Business: Transactions 285

Java Batch Gateway: Many Remote Calls in One Shot 287

BatchMember: Order Matters 292

BatchService 293

A Sample Application – An Order Entry 297

OrderEntryDemo Pre-requisites 298

OrderEntryDemo: OrdersPanel 300

OrderEntryDemo: OrderItemsPanel 302

OrderEntryDemo: OrderManager 304

Conclusion 307

Endnotes 308

CHAPTER 8 311

Enhancing and Extending Flex Controls 311

ComboBox Challenges 312

Making the Value Property Writeable 314

Adding a dataField Property 318

ComboBox with a Multi-Column List 321

Populating a ComboBox with Server-Side Data 325

Encapsulating a Remote Object Inside the ComboBox 332

Adding Auto- complete Support to TextInput 339

Integrating DataBase Search and ComboBox with Autocomplete 343

Separating Business Resources from Generic Component Code 353

Building a Business-Specific ComboBox 354

Building and Using the Resource Classes 356

Summary 361

Endnotes 362

CHAPTER 9 365

Trees with Dynamic Data Population 365

Basics of Tree Control 366

The Role of dataDescriptor 369

Moving to the Real Asynchronous World 370

No More Fake Remoting! 374

Design Patterns in Our Life 374

Data Transfer Object 375

Data Access Object 376

Asynchronous Completion Token 377

Assembler Design Pattern 377

Façade Design Pattern 378

TABLE OF CONTENTSTABLE OF CONTENTS

Page 10: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA xv

TABLE OF CONTENTSTABLE OF CONTENTS

Working with Employees and Departments 379

The Destination-Aware Tree 381

Adding Checkboxes to a Tree 385

Customizing the TreeItemRenderer 387

The Data Binding of Checkboxes 393

Summary 396

CHAPTER 10 399

Working with Large Applications 399

Deployment Scenarios 400

Application Domains 101 401

Runtime Shared Libraries 101 401

SWFs and SWCs: What’s Under the Hood 403

Making the FlexLibrary.swc 404

Making a FlexApplication Application 405

Static versus Dynamic Linking: Development Perspective 408

So, You Say Dynamic Linking? 409

Self-Initializing Libraries – Applications 412

Recap of the Technique 416

RSL versus Custom Loading of the Dynamic Code 421

The Custom Loading Example 422

Embedded Applications and the SWFLoader Object 428

Modules and ModuleLoaders 433

When Size Matters 438

Summary 438

Endnotes 439

CHAPTER 11 441

Advanced DataGrid 441

Making DataGrid Destination-Aware 442

Formatting with labelFunction 444

Formatting with Extended DataGridColumn 446

Introducing a Component Manifest File 447

More on Customizing the DataGridColumn 449

Improving FormattingManager 450

CheckBox as a Drop-In Renderer 457

DataGridColumn as ItemRenderer’s Knowledge Base 458

Nitpicking CheckBox 461

RadioButtonGroupBox as Drop-In Renderer 467

Computed Column Color 476

Page 11: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

xvi RIA WITH ADOBE FLEX AND JAVA

Computed Column Background 479

Runtime Column Styles Unleashed 481

Masked Input and Numeric Input 487

DataGrid with Automatic Item Editors 494

Data-Driven Programming Unleashed 498

Pivoted DataGrid or Property Bag 503

Summary 515

Endnotes 515

CHAPTER 12 519

Logging and Debugging Flex and Java Applications 519

Logging 520

Configuring Web Application (Server) Logging 520

Client-Side Logging 522

Using the Logging API 523

Server Log Target 524

Server Log Target Using Plain HTTP 526

The Client-Side Target 528

Creating a Tracing Console for LocalConnection 530

The RIA Aspect of Flex Logging 532

Debugging 534

Remote Debugging 534

Using Eclipse WTP for Debugging Java EE Servers 537

Configuring Tomcat 538

Deploying a Web Application Locally 541

Summary 541

CHAPTER 13 543

Building a SlideShow Application 543

Application Overview 544

Developing the SlideShow Component 545

Loading SlideShow Data 547

Animating the SlideShow 550

Adding Interactive Thumbnail Navigation 552

Developing the SlideShow Player Application 559

Developing the SlideShow Creator 560

Integrating with Flickr Web Services 561

Editing SlideShow Data 563

Developing the SlideShow Preview 566

Summary 570

TABLE OF CONTENTSTABLE OF CONTENTS

Page 12: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA xvii

TABLE OF CONTENTSTABLE OF CONTENTS

CHAPTER 14 573

Developing Custom Charts 573

How to Develop a Chart from Scratch 575

Working with Larger Data Sets 580

Adding a Vertical Line 582

Non-Typical Use of Skinning 583

Adding Scrolling and Zooming 592

Summary 601

CHAPTER 15 603

Integration with External Applications 603

Overview of Flash External Interfacing Methods 604

Using External API 605

Two-Way Flex – OWC Spreadsheet Integration 613

Flex Application for OWC Spreadsheet Integration 615

HTML Template Embedding Our ActiveX Components 621

Making Flex and Spreadsheet Talk 626

Handling Flex Metadata Notification 630

Synchronizing Spreadsheet OWC with Flex Data Changes 633

Synchronizing Flex with Spreadsheet Data Changes 636

One-Way Flex-Excel Integration via the System Clipboard 643

Flex Embedded in Microsoft Excel 649

XML Format of External API 655

LiveLink: Standalone Flex - Excel 658

LocalConnection 101 658

Flex Excel Agent Application 662

Flex Standalone Application 668

Excel Worksheet – The Flex Agent Host 674

Getting in the Nitpicking Mood 690

Summary 693

Endnotes 695

INDEX 697

Page 13: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA xix

Acknowledgments

Writing a technical book requires dedication, discipline and wish power. But most important, it requires support from family members, and we’d like to thank our families for understanding and especially our children, who got used to the fact that their fathers were glued to their computers, even in the evenings.

We’d like to thank all members of the vibrant and very active online Flex community for their drive, energy, and eagerness to contribute to the success of the tool of their choice.

We’d like to thank the top-notch Flex professional Valery Silaev for his valuable input to the chapter on integration with external applications, which is one of the most complex chapters in this book.

We’d like to thank a charting guru Vadim Sokolovsky from GreenPoint for his help in writing the chapter about developing custom charting components.

We’d like to thank our contributing author Ben Stucki for creating a very good slideshow compo-nent and writing a chapter for our book.

And mainly, we thank you, our readers, for considering this book.

— Yakov Fain, Victor Rasputnis, and Anatole Tartakovsky

ACKNOWLEDGMENTS

Page 14: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA xxi

It’s risky to try to define Web 2.0 (which may or may not be a trademark of O’Reilly Media Inc.), but the description I’ve heard that makes the most sense is that Web 1.0 always required a visit to a server to make any changes to what the user sees, whereas Web 2.0 downloads some code to the client so that, for some transactions, data can be sent to the client instead of a whole page. The result is a richer and faster user experience. I like this definition because it gives me something concrete to evaluate.

This is a logical next step now that the primitive, page-at-a-time Web has proven its success. At the same time, it’s ridiculous that we should have to do this all over again. After all, before the Web there was a lot of brouhaha about client/server, where the server runs code that makes sense on the server, and the client runs code that makes sense on the client. The idea was a lot simpler than all the marketing noise would have you believe, but it was still a good idea.

If the definition above is valid, then one could argue that the genesis of Web 2.0 is AJAX (Asynchro-nous JavaScript And XML). Of course, JavaScript has been around since, effectively, the beginning of the Web, but the browser wars made JavaScript inconsistent and thus painful to use. A key part of AJAX is that someone has gone to the trouble of figuring out cross-platform JavaScript issues so that you can ignore differences between browsers.

There are two problems with this approach. The first is that JavaScript is limited in what it can do. AJAX is an excellent hack that gets the last bit of mileage out of JavaScript, but it’s a hack nonethe-less, and the end is in sight. The second problem is that you’re relying on AJAX libraries to handle cross-browser issues, and if you want to write your own code, you have to become an expert on these issues, and at that point AJAX’s leverage goes out the door. It’s too much trouble and a waste of time to have to know all the gratuitous differences between different versions of what is sup-posed to be the same programming language.

You could argue that the solution is Java, because Java is designed to be the same on all platforms. In theory this is true. In theory, Java should be the ubiquitous client-side platform. In theory, Lear Jets and bumblebees shouldn’t be able to fly, and lots of other things should be true as well, but for various reasons Java applets, after 10 years, don’t dominate client-side automation, and it seems unlikely that they will anytime soon.

FOREWORD

FOREWORD

Page 15: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

xxii RIA WITH ADOBE FLEX AND JAVA

In practice, Java dominates on the server, so this book takes the practical attitude that most people will want to continue writing the server side using Java. But for a rich client, this book assumes that you’ve seen that other technologies will either run out of gas or involve an installation process that consumers resist (regardless of how “simple” that installation process claims to be). Because the Flash player is installed on some 98% of all machines and is transparently cross-platform, and because people are comfortable with the idea of Flash and don’t resist installing or upgrading it, this book assumes that you want to create your rich client interfaces in Flash.

The only drawback to this approach is that, historically, Flash applications have been built with tools that make more sense to artists and Web designers than they do to programmers. This is where Flex comes in. Flex is a programming system that produces compiled Flash applications. The programming language in Flex, called ActionScript, is based on ECMAScript, the ECMA-stan-dard version of JavaScript, so any JavaScript knowledge you might have isn’t lost. You can learn a single programming language and ignore cross-platform issues.

Flex comes with a library of UI widgets, with a potential marketplace for third-party widgets. As you’ll see if you look at the Flex demos and tours on www.adobe.com, these widgets can be very functional as well as appealing, so it’s possible to assemble a very attractive and professional rich client without much effort.

One of the main reasons that you might not have tried an earlier version of Flex is that the original pricing model meant that you couldn’t experiment with the tool without paying a hefty fee upfront. A big change has occurred since then, because with Flex 2 and beyond, you can use any plain text editor along with the free Adobe compiler and Flex Framework components to create your rich In-ternet applications. This includes the ability to host static Flash applications on your server. Adobe makes its money on the more powerful dynamic application server (which dynamically creates and delivers Flex applications) and the Eclipse-based Flex Builder environment, which simplifies the creation of Flex applications.

I’d like to think that I had something to do with the migration to the free command-line compiler for Flex, since I spent an hour or so on the phone trying to convince a Flash VP (before Adobe bought Macromedia) that to become popular, programmers needed to be able to experiment with the platform freely. However, the result is more than I hoped for.

I think that Flex for rich Internet client applications can become a major player. Its easy cross-platform support removes many programmer headaches, the component model offers powerful library reuse, and the result produces a very comfortable and appealing interface for the client to use. Because this book teaches you how to use Flex along with the dominant server-side develop-ment tool (Java), it’s an ideal introduction if you want to learn how to leverage these technologies.

Bruce EckelAuthor, Thinking in Java 4th Editionwww.MindView.net

FOREWORD

Page 16: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA xxiii

At the beginning of the decade Macromedia coined the term rich Internet application (RIA) to describe the future of applications. An RIA is a Web experience that’s engaging, interactive, light-weight, and flexible. RIAs offer the flexibility and ease of use of an intelligent desktop application and add the broad reach of traditional Web applications. Adobe Flex 2 has established itself as the premiere platform for delivering these experiences.

The Flash Player is an ideal runtime for a rich Internet application. Installed on 98% of Web-enabled desktops, the Player has a reach far beyond any individual Web browser or other client technology. For years, developers took advantage of this by using the Macromedia Flash tool to build com-pelling, data-driven applications. But the Flash programming model wasn’t for everyone. When building what would become Flex 1.0, we geared the programming model towards developers who had more of a programming background, especially Java Web application developers. We designed a tag-based language called MXML and influenced the evolution of ActionScript 2.0 to appeal to developers used to object-oriented programming. Flex 1.0 and Flex 1.5 were very successful for Macromedia, reaching hundreds of customers in 18 months, impressive for a v1 product with an enterprise price tag.

But we realized that to reach our goal of a million Flex developers we needed to make some drastic changes in the platform. Our first change was to rewrite the virtual machine in the Flash Player from scratch and improve the language at the same time. The AVM2 inside Flash Player 9 is orders of magnitude faster than the previous virtual machine and uses less memory. The ActionScript 3.0 language is now on a par with other enterprise development languages like Java and C#. We then re-architected the Flex Framework to take advantage of the improvements our underlying founda-tion exposed.

Next, as Adobe, we decided to make the Flex SDK completely free. Developers can now create, debug, and deploy Flex 2 applications at no cost. The Flex SDK provides tools for compiling MXML and ActionScript into deployable applications, building reusable libraries, a command-line debug-ger, and a documentation generator. We made this move to remove all the pricing barriers to be-coming a Flex developer. We hope that this jump-starts the growth of the Flex community, and we know it’s already paying off as we watch the increase in traffic on our forums, blog postings, and mailing list participation.

FOREWORD

FOREWORD

Page 17: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

xxiv RIA WITH ADOBE FLEX AND JAVA

Of course any development experience is improved if you have a robust development environment, and we built the Flex Builder IDE for that purpose. We decided to leverage the Eclipse platform for Flex Builder because it’s familiar to many of the developers we envision using Flex, and also gave us a great starting point to building an enterprise-quality IDE. Flex Builder provides project manage-ment, code hinting, compilation, visual debugging, and perhaps, most important, a Design View for rapid development of your user interface. While using Flex Builder is certainly not required to build your Flex applications, we think your experience will be more enjoyable if you do.

All of the enhancements mentioned so far address the development of the Flex application, but don’t mention one of the true innovations in the Flex 2 release, Flex Data Services. Flex Data Ser-vices is designed so you can easily integrate a Flex application into a new or existing J2EE system. The remoting service lets your Flex application make calls to POJOs or EJBs. The messaging service lets your Flex application connect to JMS message queues and topics and lets those systems push messages to all Flex clients. The Data Management Service manages data synchronization between data on the server and your Flex applications. When you make a change to data in one Flex ap-plication, it’s immediately reflected in other applications and persisted to your server. The system is scalable, highly configurable, and integrates well into any transaction management you might have, making this system a must for data-intensive applications.

This was certainly the largest project I’ve ever worked on. We had engineers on both coasts of the U.S. as well as Bangalore, India, working together on this immense system. At any hour during a 24-hour day someone on our team was actively developing. Of course it’s not just the engineers, or even just Adobe employees who contributed to the development of Flex 2. From the moment we released our public alpha we’ve been getting great feedback from the larger community.

The principals of Farata Systems have been key contributors to Flex’s success via their participa-tion in our beta programs, posts to community forums, public presentations, and blog postings. And now, Yakov Fain, Victor Rasputnis, and Anatole Tartakovsky are capping those contributions with Rich Internet Applications with Adobe Flex and Java: Secrets of the Masters. This may not be the only resource for learning how to experiment with Flex, but it will be an important one for building real-world Flex applications.

The Flex product team spent almost two years taking what we learned from Flex 1 to build a tech-nology that would revolutionize the way developers create applications that engage with their us-ers and with information. Now that technology is here in the form of Flex 2 and this book is here to help you take advantage of all that power. There’s a lot to learn, but Yakov, Victor, and Anatole have done an excellent job introducing you to everything you need to know to build a robust applica-tion. We at Adobe can’t wait to see what you create.

Matt ChotinProduct ManagerAdobe Systems, Inc.October 2006

FOREWORD

Page 18: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 1

Architecture of Rich Internet Applications

1CHAPTER

Page 19: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

2 RIA WITH ADOBE FLEX AND JAVA

What is a rich Internet application?

Historically there have been major shifts in the software industry. We moved from mainframes with dumb terminals to client/server. Users gained in convenience and productivity, and mainframe systems were patronizingly labeled as legacy. With the availability of the World Wide Web industry, visionaries turned the tables; vendors and corporate IT had been eager to get rid of the complexity of client/server version management and technologists were sold on multi-tier computing. This time client/server was called legacy. And to rub it in, good old desktop applications were labeled “fat.” Excited with server multi-threading, messaging, persistence, and similar toys, we pretend not to think that, at the end of the day, we’d have to trade user experience and productivity for the transparency of application deployment. And to make us feel better, we proudly called the new breed of applications “thin client.”

Meet the new challenge: we’re entering an era of rich Internet applications (RIA), which restores the power of desktop applications…inside downloadable Web page. RIAs run in a virtual machine (i.e., Adobe Flash Player or Java VM) and have the potential of becoming a full-featured desktop applica-tion soon. As opposed to just simply displaying Web pages delivered from some server machine, RIA really run on the client. Many of the data manipulation tasks (sorting, grouping, and filtering) are done locally like in the old client/server days. Déjà vu indeed! Industry analysts predict that in three or four years most newly developed projects will include RIA technologies.

A rich Internet application combines the benefits of using the Web as a low-cost deployment model with a rich user experience that’s at least as good as today’s desktop applications. And, since RIAs don’t require that the entire page be refreshed to update their data, the response time is much faster and the network load much lower. Think of a globally available client/server application.

Let’s illustrate the difference between “legacy” Web and RIA with a shopping cart example. Non-RIA Web applications are page-based. Since HTTP is a stateless protocol, when the user moves from one page to another, a Web browser doesn’t “remember” the user’s actions on the previous page. As a common treatment of this “amnesia,” a user state is stored on the server side in the form of the HTTP session.

Architecture of Rich Internet ApplicationsCHAPTER 1

Architecture of Rich Internet Applications

Page 20: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 3

Consider the case of an online shopping session. It can go as follows:

1. The user initiates a search for an item on Web page #1.2. The server processes this request and returns page #2 that may (or may not) contain the re-

quired item.3. The user adds an item to a shopping cart that takes yet another trip to the server to create the

shopping cart and store it on the server side. Then the server responds with page #3 so the user can either continue shopping (repeating the first three steps) or proceed to the checkout – page #4.

At the checkout the server retrieves selected items from the session object and sends page #5 to the user for shipping info. The data entered travels back to the server for storage, and the client gets back page #6 for billing information. After that page #7 will confirm the order and only then goes to the order completion page.

This simplest of online purchases consisted of seven roundtrips to the server. In a striking dif-ference to desktop applications, a few-seconds-per-page refresh is considered fast(!) for a typical Web application, and the commonly acceptable delay is up to eight seconds. Is the user motivated enough to complete the purchase? Think again, because your system gave him a chance to re-consider seven times in a row. Now assume that the network and/or server are slow. Result? Your potential buyer went elsewhere.

Rich Internet applications eliminate the roundtrips and substantially improve system performance by doing a lot more of the processing on the client than a thin client Web application. Besides, RIAs are stateful: they accumulate the information right on the client! To put it simply, RIA isn’t a set of pages controlled by the server; they are actual applications running on the client’s computer and communicating with servers primarily to process and exchange data.

Both consumer-facing and enterprise applications benefit from being RIAs. It’s a well-known fact that e-commerce Web sites such as online ticket reservation systems and online retailers are losing revenue because users abandon shopping carts on non-responsive Web sites during the checkout process. Such Web sites result in lots of calls to the call center, a major operational expense in and of itself. The performance of any system operated by employees is critical to company productiv-ity and RIAs provide a performance boost over HTML applications, while reducing operating and infrastructure costs. Finally, introducing well-designed RIAs lets companies build better “mouse-traps.”

Macromedia introduced the expression rich Internet applications back in 2002 in contrast to the bleak condition of the “legacy” Web, known today as Web 1.0. And yet the first RIA applications were born as early as 1995 when Java was created. Java’s initial popularity manifested itself in a wave of small downloadable programs called Java applets. Applets, created with Java AWT (and later Swing) libraries, were run by the browsers’ Java Virtual Machine (JVM). Ironically, the very technology that made Java popular wasn’t exploited and today Java shines mostly on the server side and in mobile devices.

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 21: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

4 RIA WITH ADOBE FLEX AND JAVA

In 2004 Tim O’Reilly coined the catchy term Web 2.0. Which sites qualify for Web 2.0 status has never been clearly defined. If someone sees a cool-looking Web site, it’s invariably called Web 2.0. Labeling better-looking Web products “2.0” is pretty popular these days. Often it refers to social engineering sites that let people collaborate and build the site’s content themselves. Wikipedia has a lengthy article on Web 2.0 that may give you some idea what this term means. See http://en.wikipedia.org/wiki/Web_2.0.

There’s now Web 3.0 too, which refers to the Semantic Web…but let’s stop playing this name game and return to the main acronym of this book: RIA.

RIA Platforms: The Major ChoicesIt’s not hard to guess that there’s more than one way to create RIAs. As we said earlier, RIAs run in the client’s browser with the help of some kind of client engine.

For example, these are the most popular products or technologies:

Using Flex you can create an ActionScript application for the ubiquitous Flash Player, a high-per-formance multimedia virtual machine that runs bytecode files in the SWF format (pronounced swif). The player’s JIT compiler converts the SWF bytecode to native machine code for fast perfor-mance. The later facility is specific to Flex 2, available since 2006. Although early versions of Flex were out in 2004, they didn’t support just-in-time compilation.

A Java programmer can create Java applets. As mentioned, this solution has been available since 1995.

Windows Presentation Foundation (WPF) was released as part of .NET 3.0 in November of 2006 and can be used to create both Internet and desktop applications.

Finally, there’s AJAX, aka DHTML, circa 1998. This solution was recently boosted with XMLHttpRe-quest API support for all major browsers. AJAX served as a wake-up call for the user and developer communities. It is often the first step on the migration path from the legacy Web to the world of RIA despite being seriously handicapped by having to support browser incompatibilities and a poor programming model.

Adobe Flex 2Flex 2 applications run cross-platform in a ubiquitous Flash Player 9 that’s a lightweight virtual machine. The platform includes:• An XML-based language called MXML that supports the declarative programming of GUI

components targeting designers• The standard object-oriented programming language, ActionScript 3.0, based on the latest

ECMAScript specification• Server-side integration via Flex Data Services (FDS) giving client applications transparent ac-

cess to the world of J2EE

CHAPTER 1

Page 22: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 5

• Charting components, access to multimedia controls, etc.• An Eclipse-based full-featured IDE with automated deployment, debugging, and tracing

facilities

The Flex 2 platform is easily extendable and integrates well with server-side Java, ColdFusion, PHP, Ruby, ASP, and the like. The upcoming release of Adobe Apollo will allow the creation of a desktop application based on Flash Player, Flex, PDF, and HTML.

The SWF file format is open, and there are third-party open source products that offer tools for creating RIAs delivered by Flash Player like OpenLaszlo from Laszlo Systems.

As opposed to the last version, Flex 2 offers a way to create RIAs without incurring hefty licensing fees. This is what comes at no cost:

• MXML: An XML-based declarative programming language for creating a GUI• ActionScript 3.0: An object-oriented language similar to Java • Flash Player 9: A virtual machine with a tiny footprint that lives inside a Web browser and

runs your compiled bytecode (.SWF)• Command-line compilers and debugger• Flex Framework: Includes a library of well-designed GUI component: buttons, tab folders,

data grids, tree controls, animated effects, and more• Flex Data Services Express: Template Web application deployed in a J2EE server to commu-

nicate with an ActionScript client application run by Flash Player. FDS Express is limited to a single CPU and is not supposed to be used in a high-availability (24x7) configuration

The following Flex tools require a purchased license:

• Flex Builder – the Eclipse-based IDE • Charting component • Flex Data Services Departmental, 24x7, 100 concurrent users • Flex Data Services Enterprise, 24x7, unlimited users

The process of creating a basic Flex 2 application consists of the following steps:

1. Design the application by adding MXML components like this button:

<mx:Button label=”Place Order” click=”processOrder(event)”/>

If you use the Flex Builder IDE, you can apply drag-and-drop techniques. Alternatively, you can write the MXML as text.

2. Write the code in ActionScript per your functional specification, for example:

private function processOrder (event:Event):void{

Architecture of Rich Internet Applications

Page 23: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

6 RIA WITH ADOBE FLEX AND JAVA

//The business logic goes here

}

3. Compile the code. Flex compiler automatically converts MXML into ActionScript and creates bytecode output in the form of an SWF file to be run in Flash Player 9 or above. You’ll enjoy a fully automatic compilation process if you use the Flex Builder IDE.

4 Deploy the SWF file and the wrapping HTML page in the Web server of your choice. The de-ployment process and creating the wrapped can be completely transparent if you use the Flex Builder IDE.

There is a Flex Online Compiler Web site where you can try writing Flex code online without install-ing anything on your computer. There are even code snippets ready to be modified and run. See http://try.flex.org/.

More advanced Flex applications can include interaction with the server-side systems through Flex Data Services, which provides remote access to server-side Java objects and Java EE components, extensive messaging support (including JMS integration), synchronization with persisted data, and integration with other persistent technologies.

Figure 1.1 Flash Player and Flex Data Services

Java Even though the Java programming language became popular largely because of applets and the famous dancing Duke (http://java.com/en/download/help/testvm.xml ), applets haven’t become Java’s main use pattern. The main reason: the large footprint of the required JVM (currently 16MB). And there are other drawbacks. For instance, although Java Swing was meant for a platform-inde-pendent look-and-feel, absent any good-looking off-the-shelf GUI widgets it was hard selling it to the public. In this regard the Flash and Flex creators did a much better job with their eye-candy components. Or take audio and video integration. Today people are used to having streaming au-

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 24: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 7

dio and video components embedded in Web pages. But the multimedia Java API remains rudi-mentary, to say the least.

There are some efforts to minimize the size of the JVM used by Web browsers and the Java Browser Edition project now needs “only” about 3MB to run a primitive Hello World applet. But this can’t compete with Flash Player 9, which managed to accommodate two virtual machines in a 1.2MB download that can run any RIA, however complex.

Another issue with Java applets is that they don’t offer a seamless download of the proper version of the JVM along with the applet. Flash Player’s express install does precisely that.

Having said that, we must acknowledge that Java Swing is a very mature and robust technology for creating GUI applications delivered either over the Web or installed on the desktop. You can do literally anything with Java Swing – if you can afford it. No, you don’t pay licensing fees, but because of the longer development cycle and the need to engage expert programmers, industrial-size Swing projects are usually quite expensive to build and maintain.

WPFMicrosoft’s Windows Foundation Platform, or WPF, become available with the release of Vista (http://msdn2.microsoft.com/en-us/netframework/aa663326.aspx ). It uses an XML-based de-clarative programming language called XAML to create GUIs and C# as a general-purpose pro-gramming language. WPF is suitable for creating both RIA and desktop applications. XBAP stands for XAML Browser Application and it’s a WPF way of creating RIAs that run in Internet Explorer. Microsoft is planning to release a version called WPF/E that will run on some non-Windows plat-forms. While living in a sandbox, XBAP will have access to all .NET 3.0 functionality but WPF/E won’t. WPF/E uses XAML and JavaScript (no C#). Common Language Runtime (CLR) is the client’s WPF engine.

To create WPF applications, developers can use Microsoft’s Visual Studio 2005 IDE with installed .NET 3.0 extensions. The next version of this IDE, called Orcas, will include a visual GUI designer. WPF developers use the same code base for writing XBAP and desktop applications: they just en-close the code sections that aren’t allowed in XBAP into the ifdef blocks.

Microsoft XAML code resembles Adobe’s MXML. Even though today’s Flex 2 is a lot more mature than WPF, Microsoft has an established developer base, while Adobe traditionally catered to de-signers. Its main goal today is to convince enterprise developers (particularly the Java camp) that Flex can be a tool of choice for creating business RIAs.

AJAXWhile the term AJAX was coined by Jesse James Garret in February of 2005 and is partly rooted in the asynchronous XmlHttpRequest implemented by Mozilla, lots of developers have used Microsoft’s version of XMLHttpRequest and alternative techniques like IFrame since 1999. These techniques

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 25: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

8 RIA WITH ADOBE FLEX AND JAVA

facilitate synchronous and asynchronous communications between the script in a page and serv-er-side code. The main problem with AJAX is that despite its popularity, it has no technical founda-tion. While the other solutions we mention here are based on rock-solid virtual machines, there’s no standard VM for AJAX. Each browser implements AJAX building blocks differently. There’s a chance that a deployed AJAX application will require code changes with each new browser release. Wait, let’s rephrase that: there’s a chance that deployed AJAX apps may run as is on a new browser release. Do you want to take chances with your business?

That said, Internet giants like Google, Yahoo, and Amazon are building AJAX apps on top of their own abstraction layers such as Google Web Toolkit (GWT). Because of the immature level of the technology, these abstract layers need constant vendor attention as soon as changes appear.

AJAX ShortcomingsAn ability to create flicker-free Web apps without buying more software is AJAX’s big appeal. You may have heard the chant, “AJAX is free.” Here’s the simple translation: no commercial AJAX tool is worth paying for. There are hundreds of libraries, toolkits, and control sets that give you the im-pression that AJAX applications are cheap to develop and strategically safe since there’s no vendor lock-in. Actually, there is vendor locking because you won’t manually write JavaScript code and will have to pick an AJAX library of some vendor. Now think about it: starting from the ground up you need a communication layer, messaging and remoting mechanisms, an HTTP sniffer, a library of UI components with shared objects and event models, a visual IDE that understands these compo-nents in design time, and a debugger that accommodates all this stuff. On top of that, there’s inter-nationalization support, accessibility for the disabled, and support for automated testing tools.

You really think you’re safe with mix-and-match from different vendors? If the answer is yes, you must be working for a software company in the RIA business. Coming to reality; a long develop-ment cycle; lack of free, quality GUI components; and the shortcomings listed below make AJAX less appealing and, actually, the most expensive way of creating RIAs.

These are some of AJAX’s current drawbacks:

• JavaScript development tools are limited due to the dynamic nature of the language, and debugging any DHTML/JavaScript mix is a pain. Yes, Google’s GWT can spare you from writ-ing JavaScript manually, but at the end of the day, it’s still JavaScript that has to be deployed in production. When the system isn’t working and time is limited, what are you going to use to debug it – the real page of Java mock-up?

• Tons of JavaScript source code has to go over the wire to the client to be interpreted by the browser. We’re talking about business applications, not some proof-of-concept demo.

• Web browsers will happily display your application even if a piece of JavaScript didn’t arrive at the client. You won’t know if a problem exists until you execute the particular use case.

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 26: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 9

• A simple right-click followed by the “View Source code” menu option would reveal your busi-ness application code. Better yet, all this code resides as plain text in the browser cache on disk. Because of this, you have to drop all the code comments and use obfuscators to protect your code from being stolen.

• HTML rendering is slow: think of a data grid that contains 5,000 records. Where’s your mutual fund report?

• Any data manipulation by JavaScript is inherently slow because JavaScript is an interpreted, not a compiled language. We’re talking thousand of times slow.

• The code is more vulnerable to hacker attack, a fact that was proved recently by a worm that stole a bunch of mail addresses from Yahoo address books.

• AJAX doesn’t support server push. The server-side application can’t publish the data directly to the client. AJAX applications have to poll the data from the server at specified time intervals without knowing if the data is there or not.

Dion Hinchcliffe, the editor-in-chief of AJAXWorld Magazine, listed some of the important things every AJAX developer should know (see http://web2.wsj2.com/seven_things_every_software_proj-ect_needs_to_know_about_AJAX.htm ):

• Browsers were never meant to handle AJAX. • You won’t need as many Web Services as you think.• AJAX is harder than traditional Web design and development.• AJAX tooling and components are immature and there is no clear leader today.• Good AJAX programmers are hard to find.• It takes real work to make up for AJAX’s shortcomings. • AJAX is only one element of a successful RIA strategy.

Hinchcliffe says, “The addition of RIA platforms such as Flex, OpenLaszlo, and WPF/E to a RIA strategy is virtually required to properly exploit the range of capabilities you’ll want capable online applications to have. This is particularly true around rich media support such as audio and video – which AJAX is virtually incapable of – but even such mundane things as good printing support. These are all things that the more sophisticated Flash-based RIA platforms really shine at and are easier to program in to boot. AJAX will increasingly get a serious run for its money from these plat-forms, particularly as they provide back-end server support for things like server-side push, formal Web Services, enterprise environments, and more.”

Interestingly enough, the future of AJAX in the enterprise application space may not be as bleak as it appears, and we’ll tell you why a little bit later in this chapter.

Finally, the browser’s golden days are almost over. All leading software vendors are looking beyond the browser for the next Web application platform.

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 27: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

10 RIA WITH ADOBE FLEX AND JAVA

To summarize, if you’re developing a new enterprise RIA from scratch, AJAX may not be the way to go. Choose a solid application development environment that offers a virtual machine at runtime like Flex/Flash, Java, or WPF. Any of these environments is more productive than AJAX. If you al-ready have AJAX applications, you can nicely integrate new Flex RIAs in existing AJAX applications using tools like FABridge from Adobe for communicating between AJAX and Flex.

During AJAX’s first year of life, every article or book on the subject mentioned Google Maps and Gmail, and various type-ahead samples: you enter the first zip code digit in a text field, and it sug-gests your possible choices based on your input without a page refresh. Today, you can read about a number of AJAX applications, including ones that work with photo images loaded from the popular flickr.com Web site. If you run into one of these samples, compare it with the slideshow application in Chapter 13. This is probably the easiest way to see the difference between the rich and just a little richer Internet applications.

Last, we’d like to make it clear that popular comparisons of Flex versus AJAX are simply wrong, since Flex is a framework and a complete development platform, while AJAX is a set of techniques. To compare apples to apples, you should compare products like Flex against GWT (Google) and the like.

Other RIA SolutionsIn this section we’ve included short descriptions of several other players in the RIA space.

OpenLaszloOpenLaszlo (http://www.openlaszlo.org ) from Laszlo Systems is an open source product that lets you create applications that can be deployed as DHTML or Flash Player files (check out the project called Legals at http://www.openlaszlo.org/legals). The ability to generate DHTML code made it a good candidate for developing applications for mobile devices, and Sun Microsystems has recently partnered with Laszlo Systems to bring this technology to the Java mobile space. This is direct com-petition for Adobe Flash Lite.

Flash Player is becoming a de-facto standard in the delivery of multimedia applications, and Open-Laszlo is already used by thousands of RIA developers. Like Flex and WPF, you define the GUI wid-gets in a declarative XML-based language called LZX. The processing logic is coded in JavaScript.

One of OpenLaszlo’s selling points, besides being an open source solution, is that you can create applications for versions of Flash Player as old as 6.0, which you can’t do with Flex 2. But the high speed of penetration and adoption of the latest version of Flash Player diminishes the value of this benefit.

GWTGWT stands for Google Web Toolkit (http://code.google.com/webtoolkit/). It lets you write pro-grams in Java, which are automatically converted to JavaScript so they can be delivered as AJAX

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 28: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 11

Web applications. This isn’t the first attempt to offer a tool that converts Java to JavaScript. Take the open source project Java2Script (http://j2s.sourceforge.net/), an Eclipse plug-in that implements an Eclipse SWT library in JavaScript. But GWT’s success is another illustration of how important it is to have support from a commercial software vendor, such as Google, for any AJAX initiative. GWT is not an open source product, but it is free.

An interesting GWT feature is that it compiles Java into various versions of JavaScript to accom-modate the specific needs of different Web browsers. GWT comes with a library of extensible com-ponents. When this chapter was written, the number of these components was limited, but that will probably change soon, because it’s Google. Its UI components are Java classes (i.e., MenuBar, HiperLink, Tree, and FileUpload), which can be combined into containers (i.e., HTMLTable, Sim-plePanel, and ComplexPanel).

GWT’s hosted Web browser lets Java developers create and test their applications without convert-ing them to JavaScript until the application is ready for real-world deployment. So, if a Java devel-oper for whatever reason has to work on AJAX applications, he should definitely consider using the GWT framework.

NexawebNexaweb (http://www.nexaweb.com ) offers a Java-based thin client that doesn’t require any ad-ditional installation on the user’s desktop. The application’s state is controlled by a small Java ap-plet running on the client. This applet communicates with the Nexaweb Java EE application as needed. To avoid issues related to the version of the Java Runtime Environment installed with the Web browser, Nexaweb uses JRE 1.1 for its applet. It’s supported by every major browser. This ap-plet partially updates the Web page when the state of the application changes. The user interface is defined using a declarative XML-based language, and the application logic is programmed in Java. It comes with an Eclipse-based visual editor.

CanooCanoo (http://www.canoo.com/) offers a so-called UltraLightClient, which is a Java library of serv-er-side proxy classes similar to the Swing API, providing, for example, ULCTable instead of JTable. Basically each of these classes has two peers – one for the server and the other for the client’s JRE. The library takes care of the split between client and server, including synchronization and com-munication of these halves using a proprietary protocol. A small presentation engine runs on the client, while the application runs on the server. The client’s presentation layer can be deployed as a Java applet or application using Java Web Start technology. The server-side application can be deployed as a Java servlet or a stateful session bean.

BackbaseBackbase (http://www.backbase.com ) includes a very light engine written entirely in JavaScript. It loads seamlessly at the beginning of the client session and brings in the client-side presentation framework, which includes tools to maintain state on the client, client/server synchronization, and incremental data load. The presentation framework supports drag-and-drop, data binding, styling, skinning, and multimedia. Backbase also steps away from typical page-based Web applications

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 29: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

12 RIA WITH ADOBE FLEX AND JAVA

and lets you create a so-called Single-Page Interface. It comes with a library of more than 50 off-the-shelf GUI components written using DHTML, JavaScript, CSS, and XML.

Apollo, Desktop 2.0, and the Bright FutureWe’ve been closely following the progress of Adobe’s Apollo project (http://labs.adobe.com/wiki/index.php/Apollo), which will make people switch from pure Web applications to disconnected or partially disconnected ones. Apollo will integrate Flash Player, Flex, HTML, JavaScript, and eventu-ally PDF, and it’ll provide access to all desktop resources (file system, ports, et al). This means that pretty soon Web applications will spill from the Web browser right on your desktop. Yes, we’re mov-ing to desktop applications again – this time “Desktop 2.0.”

As history repeats itself, let’s recap.

Desktop 1.0 arrived more that 20 years ago as an intelligent and jazzy replacement for dumb main-frame terminals. For the first few years, one of its main functions was terminal emulation. A few killer apps (Lotus 1-2-3 and WordStar) changed the way people envisioned using computers. They realized that they could have their own private data store saved on a home computer! After a while a set of client/server technologies emerged. The main selling point of client/server was the ease in getting server data to the end user in an online mode as opposed to the batch mode, which deliv-ered the data requested the next day or later. Mainframe terminals with their green-on-black-text-only user interface were sentenced to oblivion.

Visual Basic, Visual C++, and PowerBuilder were the tools of choice during the rapid growth of the client/server system. As the world’s appetite for computer applications grew, deploying and ver-sioning became more and more costly.

Then the Web with its Web 1.0 thin client started reminding us of the dumb terminal again, but this time the background was white. A semi-intelligent Web browser provided online access to a pletho-ra of J2EE, LAMP, or .NET servers located around the world. The applications were all one big shop-ping card and users were reduced to the OK and Cancel buttons, but that was an affordable way for everyone to sell their services on the Web. Later on, businesses began creating more sophisticated Web applications and even converted the complete client/server system into DHTML/XmlHttpRe-quest, albeit not labeled AJAX yet. (Seriously what took Jesse James Garrett so long?) And now we’re approaching the “client/Web” revolution – this time making all the data in the world available and personal at the same time with the eye-candy look-and-feel of rich Internet applications.

The world should now come up with new killer apps and revisit old ones but in a Web/Desktop 2.0 context. In the case of word processor or spreadsheet collaboration, privacy can be more impor-tant than the ability to do a spell check.

Making application development simpler will get us halfway there. Small teams of developers will be able to create Office-like components in Flex and Apollo. We see another trend here: putting the end user in charge of the GUI experience. For example, many browser shortcomings were “fixed”

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 30: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 13

by adding toolbars and components. Creating a RIA platform and applications capable of cross-integration is going to be vital.

Porting Google Maps into a desktop Apollo application was demo’d at the MAX 2006 conference. Expect to see a number of “webified” desktop applications by mid-2007. The authors of this book are working hard on Flex/Apollo applications and, hopefully, you’ll see the result at www.faratasys-tems.com when Apollo is officially released.

The birth of Apollo has an interesting twist: both Flex and HTML/JavaScript applications can be ported to Apollo, which means that AJAX applications will find their new home in Apollo too. Not only they will be ported, they’ll be upgraded and turned into desktop systems. Basically, Apollo will offer AJAX a lifeline.

Some Pragmatic Flex/Java ConsiderationsCool technologies come and go. Only some of them settle down in the toolbox of a professional programmer working on enterprise business applications. The technical excellence of any software is important, but it’s not the only component in its success.

The Learning Curve: Reuse of Skills and Code One important concern of any development manager is the availability of a large pool of people who know a particular software. There are plenty of Java programmers with the skills required to develop Web applications, and the good news is that enterprise Java developers with servlet/Java EE programming skills or Java Swing experience will find the Flex path very easy. Java and Action-Script 3.0 are very similar; Eclipse is a familiar environment for many Java developers. Since Flex piggybacks on Java EE and browser technologies, a server-side Java programmer should be able to correlate his current skills with the development process in Flex. Java Swing developers will in-stantly spot similarities to Java event and layout models. In our experience, the typical ramp-up time for motivated Java developer is two weeks. The best part is that Flex seamlessly integrates with existing Java server code of any flavor – POJO, EJB, Hibernate/Spring, or JMS, so the server-side part can literally remain unchanged.

Application SecurityAnother important consideration is the security of Flex/Java applications. Flex server-side security management is quite extensive, and it lets you use either container offerings or custom security providers via declarative XML binding. There are a couple of challenges since Flex supports mul-tiple protocols, so Java EE security that relies only on HTTP sessions has to be extended, but it’s a simple and well-documented process. On the client side, it builds on Flash Player security that’s known for its lack of serious security flaws. You have a built-in security manager that has all the standard protection for cross-domain and zone access. Corporations can further restrict the code they get from third parties by wrapping the code loaders in additional security managers.

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 31: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

14 RIA WITH ADOBE FLEX AND JAVA

Flex GUI PerformanceJava Swing is a tried and true tool for developing responsive GUIs in demanding applications like stock trading and online auctions. Flex is capable of providing near-real-time data rendering to the GUI and very high refresh rates on large data sets. Flash Player 9 is the high-performance modern virtual machine with precompiled optimized code and a just-in-time (JIT) machine code com-piler.

Shorter Development CycleIt’s very possible to build the client side of a real-time portfolio display integrated with news feeds and graphs in about 200 lines of Flex 2 code. A similar Swing program (even if it’s created with com-mercial IDEs) would be several times larger.

The real selling point becomes obvious on the second or third day of the proof-of-concept phase. A decent Flex developer should be able to prototype (short of server processing) most of the UI for a specific trading system by the end of the first week. If you’re lucky and system integration went okay, you can add the collaboration features and multimedia – with the total client code base for the whole project coming within 1,000-1,500 lines. This is definitely not possible with Swing. Of course, some people will say modern Java IDEs generate lots of boilerplate code automatically, but this is still the code that someone has to read, understand, and fix if need be.

Room for ImprovementWhile many programmers who dealt with previous versions of Flex are really happy now that they have a professional Eclipse-based IDE, Java programmers are spoiled by the variety of excellent and responsive IDEs. Flex Builder is a truly RAD tool, and it helps tremendously in writing Flex code, but when this was written it worked a bit slower than the Eclipse IDE for Java. One of the reasons Flex seems slower than Java is that it does so much more than the Java compiler. It uses a number of code generators behind the scenes, a multi-pass compiler/linker, and a deployment optimizer. It also embeds a full-fledged Flash “projector” in Eclipse.

Connection ManagementFlex provides an extensive infrastructure for managing connections between the Web browser and the server. It lets you specify all the protocol details for the Real Time Messaging Protocol (RTMP), HTTP, HTTPS, as well as plain socket connections. You can then specify the order in which the pro-tocols should be tried due to availability/firewall issues. Flex also automatically bundles multiple requests going over the same transport, striving for maximum performance out-of-the-box. Since any Web browser, by default, maintains only two connections with the server, Flex gives you much better use of these two connections if you limit yourself to HTTP. You also get alternative real-time push or pull connections on additional protocols, a direct socket API, and tons of low-level hooks that let you build any protocol you want.

We worked on AJAX-like frameworks during 1999-2005 and can say that Flex is a lot more robust

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 32: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 15

in code and data delivery fields than any cross-browser AJAX solution known today. Robustness is an even bigger showstopper for large AJAX applications than performance. Flex/Flash really gives back the control over communications that is very important for enterprise applications.

Flex and Agile Development In the mid-’90s PowerBuilder and Visual Basic were the tools of choice in the client/server field. Software developers didn’t really worry about what was under the hood, but more importantly, they were business users’ best friends. They could do stuff quickly, or using the modern jargon, they were agile programmers without even knowing it. They’d ask the business user Joe, “How do you usually do your business? What would you like to have on this screen? Describe the steps of your business process.” Most likely Joe wouldn’t know all the answers, but this was okay, because developers could come back to the user the next day with a working prototype. This was easy with PowerBuilder’s DataWindow. When Joe-the-user saw the prototype, his glassy look all of a sudden would become friendly and understanding. Now Joe was back in control: “No, you did this part wrong, I wanted it different.” No problem, developers would return with the changes the next day (not next month, but the next day!).

Developers didn’t really know how the DataWindow worked, but they trusted this component. PowerBuilder used an event-driven programming model, which was clean and simple. Object A triggers event XYZ on object B, and this event can carry a payload – the data that object B needs to operate. Using modern jargon it’s called Inversion of Control or Dependency Injection design pat-tern. What’s important is that object B doesn’t know about object A. On the same note, if object B needs to return the result of some internal function back, it would broadcast this result “to whom it may concern” by triggering one of its own events (we’ll explain this in the section on custom events in Chapter 4). This is loose coupling in action.

The mentality of many Java programmers was different. They’d assign lower priority to the user’s windows and spend most of their time designing a multi-tier system that didn’t depend on any spe-cific look-and-feel and could be universal. Meetings with Joe would be rare because they couldn’t create a decent prototype fast. Fancy IDEs with GUI designers like Matisse weren’t in the picture yet and, more importantly, Java programmers were thinking big: UML, design patterns, applica-tion servers, and clustering. They enjoyed the programming process for itself. They didn’t like the mindset of old-fashioned PowerBuilder or Visual Basic programmers who were thinking windows and screens.

With Flex you started to care about the business users again. You can change the prototype twice a day, and Joe can do the same with his business requirements. No pile of project documentation is needed. The napkin is back and it works. Flex architects can give the server-side Java team the final okay only after Joe is 100% happy.

Besides, with Flex we can have the best of both worlds: the source code of the Flex framework is available, we can learn how it works inside, and override some functionality with what suits our needs better.

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 33: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

16 RIA WITH ADOBE FLEX AND JAVA

Working with Flex promotes agile development, where the people we build the software for are the driving force. Agile development methodology suggests minimizing each development cycle to mitigate the risk of delivering something to the users that they don’t want. Agile development encourages frequent face-to-face meetings with the end users as opposed to preparing and send-ing documents back and forth.

The main page of the Manifesto for Agile Software Development (http://www.agilemanifesto.org/) prioritizes:

• Individuals and interactions over processes and tools • Working software over comprehensive documentation • Customer collaboration over contract negotiation • Responding to change over following a plan

In this book we tried to use agile development methodologies. When you follow the examples, do a “reverse analysis” of the code you’ve written in the past to see if it could be done simpler using the techniques we describe.

SummaryIn this chapter we’ve presented you with a quick overview of major RIA-enabling technologies and various approaches to making Web applications richer. It’s by no means a complete list: new com-mercial and open source RIA frameworks and components arise every day. However, given the overall business and technical value, there’s not a single RIA vendor that the authors of this book would put anywhere near Adobe’s position with Flex. Adobe Flex 2 is an excellent tool for creating commercial-strength rich Internet applications. It integrates with various back ends and elegantly accommodates the complexities of an enterprise technology setup. If you’re thinking strategically, if you’d like to keep your business competitive, you need to innovate.

Architecture of Rich Internet ApplicationsCHAPTER 1

Page 34: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 19

Getting Familiar with Flex

2CHAPTER

Page 35: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

20 RIA WITH ADOBE FLEX AND JAVA

Even though this book is not a Flex tutorial, we need to spend some time introducing this RIA tool. While Flex comes with an Integrated Development Environment (IDE) called Flex Builder (see Chapter 3), in this chapter we’ll gently introduce you to Flex programming by writing a couple of simple Flex programs using only the tools and components that are available for free. We’ve also included a brief comparison of the Java and ActionScript languages and some good-to-know top-ics: frame rates, the application initialization process, and display lists.

Working with text editors and command-line compilers is not the most productive way of develop-ing with Flex, but command-line compilers definitely have their use. In a typical real-world sce-nario, developers working on a decent-size project would use both – Flex Builder IDE to create and test their applications, and then build tools like Ant or Maven to invoke Flex command-line compilers to create and deploy the application. This is especially important because Flex applica-tions are often used in a mixed environment where an application build consists of multiple steps of compiling and deploying modules written in more than one language, for example, Flex for the GUI part and Java for the server-side components.

Free Flex 2 IngredientsThis is a brief overview of what comprises Flex and what’s available for free:

MXML: An XML-based markup language that lets you define GUI components using declarative programming. You add tags to your program that represent visible and invisible Flex objects. For example, the MXML tag <mx:Label text=”Enter password:” x=”20” y=”40” /> will display the text “Enter password:” in 20 pixels to the right from the top left corner of the Flash Player’s window and 40 pixels down.

ActionScript 3.0: An object-oriented language with a syntax similar to Java’s. ActionScript classes and MXML tags are interchangeable. Most ActionScript classes can be represented by MXML tags and vice versa. Even if you create your application only in MXML, it’ll be converted to ActionScript and then compiled into bytecode for execution in Flash Player.

Getting Familiar with Flex CHAPTER 2

Getting Familiar with Flex

Page 36: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 21

Flex framework: A library of extensible components located in multiple packages. You can create your own classes and new MXML tags as needed. Just write a piece of MXML code, save it in a file called MyCode.mxml and you’ve got a new tag: <MyCode>. Or create the new class MyCode.as and use it either with other ActionScript classes or as an MXML tag.

mxmlc: A program that launches MXML and the ActionScript compiler located in a Java archive called mxmlc.jar. This command-line compiler generates Flex applications as .swf (pronounced swif) files.

compc1: A program that generates Flex component libraries as .swc (pronounced swick) files. An swc file contains an swf, catalog.xml, and asset files if needed. We’ll discuss the use of component libraries in Chapter 10.

fdb: A command-line Flex debugging utility that you can start from a command line. It requires the debugger version of Flash Player that comes with the Flex framework.

asdoc: A documentation tool that runs through files containing Flex source code and creates on-line documentation similar to the one that comes with the standard Flex API.

Flex Data Services 2 Express: Deployed as a standard Java EE application, it provides high-per-formance connectivity with existing server-side data and business logic. Based on messaging ar-chitecture, Flex Data Services integrates with existing common middleware and provides services that automatically synchronize data between client and server, supports real-time data push and publish/subscribe messaging, and facilitates the creation of collaborative applications.

Flash Player 9: A runtime environment for your Flex 2 applications (.swf and .swc files). A debug version of Flash Player can output a trace message to a console or into a file.

Downloading the Flex 2 FrameworkAll Flex 2 components can be downloaded from the URL: http://www.adobe.com/cfusion/tdrc/in-dex.cfm?product=flex.

Flex Builder IDE comes with command-line compilers and the Flex framework. But since this chapter is about working with free Flex components, we’ll assume for a moment that you’re not planning to use Flex Builder and will just get the Flex SDK from the section called the Free Flex 2 SDK and Language Pack.

After the download is complete, unzip the archive file into a separate directory. For example, in Windows create a folder c:\FreeFlex and add c:\FreeFlex\bin to the system variable PATH on your computer. Open the command window and type mxmlc –version. If you did everything right, your screen may look like Figure 2.1:

Getting Familiar with Flex CHAPTER 2

Page 37: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

22 RIA WITH ADOBE FLEX AND JAVA

Figure 2.1 Checking the version of mxmlc compiler

Since the mxmlc compiler is written in Java, it uses the Java Runtime Environment (JRE) during the compilation process. Flex Framework comes with its own JRE (when this was written version 1.4.2). You can also download a more current version of JRE at http://www.java.com/en/down-load/manual.jsp. Having the latest JRE on your PC may increase the speed of the compilation process.

After installing the Flex Framework, you’ll find the configuration file jvm.config and, if you have a Windows machine, you can edit this file to use the latest JRE2. If you’re not using Windows, you can modify the compiler’s shell script to point it to the right JRE.

If you don’t have Flash Player 9 installed, get it from the same Web page. The debug version of Flash Player 9 is located under your Flex install in the directory Player.

We are now ready to write our first Flex program. Guess what its name will be.

Hello World in MXMLIt says in Wikipedia that “Hello World is a software program that prints Hello World on a display device. It is used in many introductory tutorials for teaching a programming language and many students use it as their first programming experience in a language.” (See http://en.wikipedia.org/wiki/Hello_world.)

After seeing these two magic words “Hello World” on the system console or a graphical window, you’ll get this warm feeling of knowing that your software is installed and configured properly, compilers work, the runtime environment operates, and you’re ready to learn the tool, which in our case is Flex.

Ladies and gentlemen, please welcome, Hello World in MXML.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”>

Getting Familiar with Flex CHAPTER 2

Page 38: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 23

<mx:Label text=”Hello World”/>

</mx:Application>

Listing 2.1 Hello World program: HelloWorld.mxml

Type this code into your favorite plain text editor and save it in a separate directory. In a command window, switch to this directory and compile this application by entering mxmlc HelloWorld.mxml. It should create the new file HelloWorld.swf.

By default, .swf files are associated with Flash Player – just double-click on HelloWorld.swf in Win-dows Explorer and it’ll run our HelloWorld program:

Figure 2.2 HelloWorld in Flash Player

The MXML file has translated HelloWorld into ActionScript and compiled it into the .swf file. Dur-ing the first pass, the name of your MXML file becomes the name of the main generated Action-Script class, for example, with the virtual wave of a magic wand the MXML tag <mx:Application> turns into the ActionScript code:

class HelloWorld extends Application {…}

All other MXML tags will undergo a similar procedure. For example, the markup code

<mx:Label text=”Hello World”/>

leads to the generation of an instance of the ActionScript class Label and calls its setter function to assign the text “Hello World” to an attribute called text.

<mx:Label text=”Hello World”/ >

Typically, you deploy a swf file on your Web server as a part of an HTML page3. If you’re using a command-line Flex compiler, you’ll have to create such an HTML file manually, as opposed to Flex Builder, which automatically creates these wrappers for you. But even the process of manually

Getting Familiar with Flex CHAPTER 2

Page 39: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

24 RIA WITH ADOBE FLEX AND JAVA

creating HTML wrappers is greatly simplified by using the so-called HTML templates that come with Flex located in the folder resources. Currently, there are six HTML templates of various flavors that can detect the client’s version of Flash Player, offer an express install of the player, and support history management. The process of creating HTML wrappers is described in the product manual, “Building and Deploying Flex 2 Applications.”

Specifying Compiler OptionsThe Flex compiler comes with a number of useful options. For example, you can request that the generated ActionScript code not be deleted after compilation. To see which compiler options are available, type the following at the command prompt:

mxmlc –help list advanced

One option is keep-generated-actionscript. Let’s recompile HelloWorld with this option:

mxmlc -keep-generated-actionscript HelloWorld.mxml

Now the Flex compiler will not only produce the .swf file, but will also create a subdirecory called generated and you will be able to browse the source code of more than two-dozen ActionScript HelloWorld-supporting files.

The speed of the command-line compilation may be improved if you use the option -incremental=true (in Flex Builder this option is on by default).

You can read more about Flex compiler options in the section “Using the Flex Compilers” in the product manual “Building and Deploying Flex 2 Applications.”

You can also specify compiler options in a special configuration file named flex-config.xml or in a custom configuration file whose name should be specified in the load-config compiler’s option. Just keep in mind that the compilation options from flex-config.xml file affects all your projects.

Another way of specifying some Flex compiler options from an ActionScript class, namely width, height, frameRate, and backgroundColor, is through a special meta tag SWF that you put above the class declaration line. For example:

[SWF(frameRate=”50”, backgroundColor=”#FFFFFF”)]

public class SomeApp {

}

If compilation fails, you’ll see a short error description, but if you’re still not sure what this er-ror means, consult http://livedocs.adobe.com/labs/as3preview/langref/compilerErrors.html for a more detailed description of the compilation errors. This Web page also has a link to a page de-scribing runtime errors.

Getting Familiar with Flex CHAPTER 2

Page 40: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 25

Building and Deploying Applications with AntUnless your project is as simple as the HelloWorld application, compilation and deployment has to be automated. Creating a build script for any decent-size application (i.e., hundreds of files in Flex and server-side Java) is a project on its own. Java programmers typically use an open source build tool such as Ant or Maven to formally describe and execute all the steps required to build and deploy their projects.

If you haven’t had a chance to use any of the build tools before, this section is for you. This is a mini-primer on how to use Ant, the popular Java-based build tool from Apache.

Even deploying such a simple application as HelloWorld consists of several steps. For example:

1. Use mxmlc to compile HelloWorld.mxml into a swf file.2. Create an HTML wrapper file(s) from one of the HTML templates.3. Create or clean the directory in your Web server where HelloWorld files will be deployed.4. Copy or ftp HelloWorld.swf and supporting files to this directory.

Now imagine that your HelloWorld application is part of a larger application that includes Java modules that also have to be compiled and deployed in some server location. The deployment of even a small project usually involves at least a dozen dependent tasks that have to execute in a particular order. Creating and maintaining a build script is well worth the effort.

Building HelloWorld with AntWith Ant, you describe your project in a build file written in XML (it’s typically called build.xml). An Ant project consists of targets (i.e., compile) that, in turn, consist of tasks (i.e., mkdir, exec). Listing 2.2 shows a build.xml that will create an output directory bin and compile our HelloWorld applica-tion into this directory. This Ant build file has two targets: init and compile.

<project name=”HelloWorld” default=”compile”>

<property name=”flex.mxmlc” location=”C:\Program Files\Adobe\Flex Builder 2 Plug-in\

Flex SDK 2\bin\mxmlc.exe” />

<property name=”dest.dir” value=”bin” />

<target name=”init”>

<delete dir=”${dest.dir}” />

<mkdir dir=”${dest.dir}” />

<attrib file=”${dest.dir}” readonly=”false”/>

</target>

<target name=”compile” depends=”init”>

<exec executable=”${flex.mxmlc}” failonerror=”true”>

<arg line=”-output ‘${dest.dir}/HelloWorld.swf’”/>

<arg line=”HelloWorld.mxml”/>

Getting Familiar with Flex CHAPTER 2

Page 41: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

26 RIA WITH ADOBE FLEX AND JAVA

</exec>

</target>

</project>

Listing 2.2 Ant project - build.xml

The init target consists of two tasks: delete and create the directory called bin.

The compile target will compile HelloWorld.mxml, producing bin\HelloWorld.swf. If we have to write a command manually that compiles HelloWorld.mxml into the bin directory, it would look like this:

mxmlc -output bin/HelloWorld.swf HelloWorld.mxml

Besides targets and tasks, our build.xml contains two property tags. One of them defines a property called dest.dir that has a value bin that is the name of the output directory for our compiled Hel-loWorld.swf:

<property name=”dest.dir” value=”bin” />

This means that wherever Ant encounters dest.dir surrounded with ${ and }, it’ll substitute it with the value of this property. In our case this is bin.

The project in Listing 2.2 also defines a property called flex.mxmlc that tells Ant where the mxmlc compiler is located. The target compile uses Ant’s task exec, which will run the mxmlc compiler, passing it the arguments specified in the <arg> tags.

Let’s download Ant4 from http://ant.apache.org/. Installation is simple: just unzip the archive and add the path to its bin directory (i.e. C:\apache-ant-1.6.5\bin) to the environment variable path of your operating system.

Now we can open a command window, get into the directory where HelloWorld.mxml and build.xml are located, and enter the command ant that will start executing the default target of our proj-ect, which is compile. But since compile target is marked as a dependent on init, Ant will execute the init target first.

Figure 2.3 shows how the Windows XP command window may look after running this project:

Getting Familiar with Flex CHAPTER 2

Page 42: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 27

Figure 2.3 Running an Ant Build

Ant has an extensive set of predefined tasks like copy, zip, jar, War, FTP, Mail, Condition, and many more described at http://ant.apache.org/manual/.

For example, to make our build file work in both Windows and Unix environments, we can specify the location of the mxmlc compiler as follows:

<condition property=”flex.mxmlc” value=”${sdkdir}/bin/mxmlc”>

<os family=”unix”/>

</condition>

<condition property=”flex.mxmlc” value=”${sdkdir}/bin/mxmlc.exe”>

<os family=”windows”/>

</condition>

You can pass values of the properties from the Ant command line using the –Dpropery=value op-tion. For example, if we use the code snippet above, the property sdkdir can be passed from a com-mand line:

ant –Dsdkdir=/opt/usr/freeflex

You can write more sophisticated Ant scripts. For example, you can automate the creation of an HTML wrapper for swf files with a replace regex task applied to one of the HTML templates provid-ed. You can write build scripts that will merge Flex-generated artifacts with Java Web applications. Automating builds with Ant can get you pretty far.

There are lots of online articles and books on Ant, but don’t miss Ant in Anger by Steve Loughran in which he discusses some Ant core practices, naming conventions, and team development pro-cesses. (See http://ant.apache.org/ant_in_anger.html.)

Frame Rate Flash Player renders its GUI objects (display list) at a specified frame rate. As opposed to Flash movies that consist of multiple frames displayed over a timeline, Flex applications don’t have mul-

Getting Familiar with Flex CHAPTER 2

Page 43: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

28 RIA WITH ADOBE FLEX AND JAVA

tiple frames, but the frame rate plays an important role in Flex programs. The display list (your GUI components) is rendered at the specified frame rate and applications with higher frame rates can have a better performing GUI, but it doesn’t come free.

The Flex compiler builds an swf file with a default frame rate of 24 frames a second, unless another value is specified during compilation. For example:

mxmlc –default-frame-rate 50 HelloWorld.mxml

This command will build an swf file with the frame rate of 50 frames a second. The display list rendering and ActionScript execution take turns, hence the frame rate can affect your application performance.

Setting the frame rate to 50 doesn’t mean that each frame will be displayed in exactly 20 millisec-onds (1,000 divided by 50), since there is some OS/browser overhead. Besides, some browsers may impose restrictions on plugins to lower CPU utilization on the user’s machine.

To see how the compile time setting of the default-frame-rate option affects program execution, consider the following program that on each enterFrame event makes a simple calculation of the actual frame rate.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

enterFrame=”enterFrameHandler()”>

<mx:Script>

<![CDATA[

var lastTime:int;

function enterFrameHandler():void{

trace(int(1000/(getTimer()-lastTime)));

lastTime=getTimer();

}

]]>

</mx:Script>

<mx:Button label=”Button”/>

<mx:TextInput/>

</mx:Application>

Listing 2.3 Testing frame rate

We ran this program in the Internet Explorer and Firefox browsers on a Windows XP laptop with a single 1.8GHz CPU. Tables 2.1 and 2.2 show the difference between requested and actual frame rates and how hard the CPU works based on the frame rate settings.

Getting Familiar with Flex CHAPTER 2

Page 44: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 29

Default-frame-rate 10 24 50 100

Actual frame rate 9-10 18-26 35-52 50-110

CPU utilization 15-17% 15-20% 18-20% 26-28%

Table 2.1 Frame rates with Internet Explorer 6

Default-frame-rate 10 24 50 100

Actual frame rate 9-10 16-22 41-50 70-100

CPU utilization 16-20% 18-20% 22-25% 30-33%

Table 2.2 Frame rates with Firefox 1.5

While results vary slightly, they are self-explanatory – lower frame rates translate into lower CPU utilization. You should experiment with frame rates in your application to find the right balance between GUI performance and CPU use. Remember that your users may be running several pro-grams at the same time, and you don’t want to bring their CPUs to their knees just because you’ve enjoyed super-smooth graphics rendering. If you’re not creating a movie, keep the frame rate as low as you can.

Namespaces in MXMLIn XML, namespaces are used to avoid potential naming conflicts with other components with the same names. So far we’ve only seen the xmlns property in our MXML sample programs:

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”>

The namespace mx:xmlns refers to the URI http://www.adobe.com/2006/mxml that lists valid MXML tags. Open the file flex-config.xml and you’ll find an XML element there that links this URI to the file mxml-manifest.mxl that lists all the MXML components. Here’s an extract from this man-ifest file:

<component id=”ButtonBar” class=”mx.controls.ButtonBar”/>

<component id=”Canvas” class=”mx.containers.Canvas”/>

<component id=”CheckBox” class=”mx.controls.CheckBox”/>

<component id=”ColorPicker” class=”mx.controls.ColorPicker”/>

<component id=”ComboBox” class=”mx.controls.ComboBox”/>

If you want to use one of the standard MXML components, specify the prefix mx for each of them. For example, to use an MXML Label, you write the following:

<mx:Label x=”111” y=”81” text=”Hello World”/>

Getting Familiar with Flex CHAPTER 2

Page 45: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

30 RIA WITH ADOBE FLEX AND JAVA

Later in this book, we’ll show you how to create custom components and keep them in a separate namespace to avoid naming conflicts. We’ll introduce another namespace with the URI com.the-riabook.controls.*. For example, in Chapter 9 we’ll be developing a custom Tree component and Listing 9.10 includes the following MXML code:

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=”com.theriabook.controls.*” >

<lib:Tree id=”tree” width=”50%” height=”100%”…>

</lib:Tree>

</mx:Application>

This sample defines two namespaces: mx and lib. The <lib:Tree> notation means that we’re plan-ning to use a Tree component from the namespace called lib. As you can guess, we’re going to pro-gram this Tree component in the ActionScript’s package com.theriabook.controls and will have to provide either the code of our Tree component or the SWC library that includes this Tree.

The namespace URI tells Flex where to look for the file implementing this component. You can either create a com/theriabook/controls subdirectory in the application’s directory or – prefer-ably – keep it in a separate location included in the classpath of your application (read about the flex-config.xml file and the source-path tag in the Flex documentation). Since we’ve defined two namespaces here, we can use components available in any of them.

You can also specify a so-called local namespace using notations like xmlns=”*” or xmlns:mylo-cal=”*” that tells Flex to look for components that are located in the same directory as the MXML file or, in case of Flex Data Services, in the /WEB-INF/flex/user-classes directory.

Chapter 4 includes a section that shows how to use namespaces in ActionScript.

From Hello World to a CalculatorThe Hello World application uses just a Label tag put directly inside the parent application tag. But typical MXML code has a hierarchical structure. The application may include containers that in turn can consist of other containers or individual components. For example, a calculator can consist of two panels: the top one will be used for displaying results and the bottom one will hold the buttons with digits. We’ll put both of these panels inside the tag VBox – the vertical box (Java programmers can think of it as a Swing BoxLayout).

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”absolute”>

<mx:VBox x=”23” y=”22” height=”350”>

<mx:Panel width=”250” height=”70” layout=”absolute”>

</mx:Panel>

Getting Familiar with Flex CHAPTER 2

Page 46: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 31

<mx:Panel width=”250” height=”220” layout=”absolute”>

</mx:Panel>

</mx:VBox>

</mx:Application>

Listing 2.4 Calculator1.mxml - A VBox with two panels

After saving the code above in the file Calculator1.mxml, we’ll compile it with mxmlc and run it in Flash Player:

Figure 2.4 Running Calculator1.mxml in Flash Player

The calculator’s panels are ready and after adding a text tag to the top panel and several button tags to the bottom one, the code will looks like this:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”absolute”>

<mx:VBox x=”23” y=”22” height=”350”>

<mx:Panel width=”250” height=”70” layout=”absolute”>

<mx:Text x=”0” y=”-14” text=”Text” width=”144”/>

</mx:Panel>

<mx:Panel width=”250” height=”220” layout=”absolute”>

<mx:Button x=”0” y=”0” label=”1” width=”36”/>

<mx:Button x=”40” y=”0” label=”2” width=”36”/>

<mx:Button x=”80” y=”0” label=”3” width=”36”/>

<mx:Button x=”0” y=”30” label=”4” width=”36”/>

Getting Familiar with Flex CHAPTER 2

Page 47: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

32 RIA WITH ADOBE FLEX AND JAVA

<mx:Button x=”40” y=”30” label=”5” width=”36”/>

<mx:Button x=”80” y=”30” label=”6” width=”36”/>

</mx:Panel>

</mx:VBox>

</mx:Application>

Listing 2.5 Calculator2.mxml – Adding controls

Compile and run this code and you’ll see the following screen:

Figure 2.5 Running Calculator2.mxml in Flash Player

It’s not the prettiest calculator you’ve ever seen, but it’s not bad for two minutes of coding. The structure of the program in Listing 2.5 is simple, readable, and elegant.

The panels can include either standard Flex containers or custom components that you or other developers created. Table 2.3 provides the widgets that come with the Flex 2 framework.

Getting Familiar with Flex CHAPTER 2

Page 48: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 33

Layouts ApplicationControlBar, Canvas, ControlBar, Form, FormHeading, FormItem, Grid, HBox, HDividedBox, HRule, Panel, Spacer, Tile, TitleWindow, VBox, VDividedBox , VRule

Navigators Accordion, ButtonBar, LinkBar, MenuBar, TabBar, TabNavigator, ToggleButtonBar, ViewStack

Controls Button, CheckBox, ColorPicker, ComboBox, DataGrid, DateChooser, DateField, HSlider, HorizontalList, Image, Label, Link, List, Loader, NumericStepper, PopUpButton, ProgressBar, RadioButton, RadioButtonGroup, RichTextEditor, Text, TextArea, TextInput, TileList, Tree, VSlider, VideoDisplay

Table 2.3 Flex widgets

You can create custom components by combining these widgets. For example, you can create a custom login component with two Label controls for ID and password, two TextInputs, two But-tons, and a Link for the “Forgot Password?” function. Such custom components can be reused by multiple applications. Starting in Chapter 6 we’ll show you how to create custom controls.

Adding Some ActionScriptMXML takes care of the GUI, but where’s the processing logic? What will happen when a user press-es a button on our calculator? Nothing will happen at this point. The processing logic has to be written using ActionScript 3, which is an object-oriented language with a syntax similar to Java’s, and both of these languages can be integrated quite nicely in distributed Internet applications.

ActionScript was first introduced in August of 2000 as a programming language for Flash Player 5. ActionScript 2 came into the picture in September of 2003 with Flash Player 7. ActionScript 3 (June 2006) is a full rewrite of this language that can be used for creating applications (even without using MXML) for Flash Player 9. ActionScript 3 is based on the third edition of ECMAScript, which is an international standardized programming language for scripting.

Flex Framework is written in ActionScript 3. Readers familiar with previous versions of this lan-guage may want to check out “ActionScript 3: Learning Tips” at http://www.adobe.com/devnet/ac-tionscript/articles/actionscript_tips.html.

We’ll be using ActionScript 3 in every chapter of this book, but for detailed coverage of this lan-guage please consult the product manual “Programming ActionScript 3.0” available for down-load at www.flex.org.

In Flex, you link MXML components with the ActionScript processing logic using one of the fol-lowing methods:

• Embed ActionScript code in mxml files using the tag <mx:Script>• Include external ActionScript files into mxml, for example :

<mx:Script source=”calculateTax.as”>

Getting Familiar with Flex CHAPTER 2

Page 49: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

34 RIA WITH ADOBE FLEX AND JAVA

• Import ActionScript classes• Import ActionScript compiled components

A typical Flex project consists of .mxml files with declared components since the files containing ActionScript code, .swc files with compiled components, .css files with presentation style info, and other assets like image files, if needed. All of these components can be compiled into one .swf file, or a number of swc libraries, and run by Flash Player 9. Only one MXML file should have the <mx:Application> tag; the rest of the MXML files usually start with other tags.

The First Date with EventsCalculator2 displays GUI components using MXML. For the processing logic we’ll use ActionScript, and the only missing link is how these components can initiate this processing logic. In Flex this is done using events. For example:

<mx:Button x=”0” y=”0” label=”1” width=”36” click=”displayDigit();”/>

The displayDigit() is a function written in ActionScript that can contain some processing logic and displays the digit. Chapter 4 will offer more details on using standard and custom events.

Event-driven programming is about identifying important events that may happen to your appli-cation and writing event handlers to process these events. If a particular event never happened, the code from the corresponding event handler (aka listener) isn’t called.

Not all events are triggered by the user’s actions. Some system events may be triggered by your ap-plication code or other processes such as the arrival of the database query result set or the return of the Web Services call. Your applications can define its own events and trigger them from MXML or ActionScript code. Writing event listeners that contain code-handling events is automated in Flex, but you have a means of writing such listeners on your ActionScript code.

For example, adding a simple MXML attribute to process a mouseOver event

<mx:Label text=”Hello World” mouseOver=”changeTextColor()” />

requires the compiler to work harder as now it needs to do the following:

• Generate an event listener for the Label object• Create an object to process the mouseOver event in the user-defined function changeText-

Color()• Wire this listener with the instance of the Label

Java compilers work faster because their job is a lot easier – a Java programmer creates all these instances and listeners manually so the javac compiler spends most of its time merely translating the Java source code into bytecode.

Getting Familiar with Flex CHAPTER 2

Page 50: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 35

The third version of the calculator adds several samples of processing the button-click event. In Listing 2.6 the b1 button uses inline ActionScript to concatenate digit 1 to the text field result:

click=”result.text+=’1’”

A click on button 2 calls an ActionScript displayDigit() function defined in the section <mx:Script>.

A click on button 3 concatenates the label of the button to the result field.

In the fourth button, we just fool around to illustrate using more than one ActionScript inline state-ments – first replace the label of the button to 9 and then display it. Even though it’s not recom-mended, you can use multi-line inline ActionScript code by surrounding it with curly braces.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”>

<mx:VBox x=”23” y=”22” height=”350”>

<mx:Panel width=”250” height=”70” layout=”absolute”>

<mx:Text id=”result” x=”0” y=”0” width=”230” height=”30” textAlign=”right”/>

</mx:Panel>

<mx:Panel width=”250” height=”220” layout=”absolute”>

<mx:Button id=”b1” x=”0” y=”0” label=”1” width=”36” click=”result.text+=’1’” />

<mx:Button id=”b2” x=”40” y=”0” label=”2” width=”36”

click=”displayDigit(b2.label)”/>

<mx:Button id=”b3” x=”80” y=”0” label=”3” width=”36”

click=”result.text+=b3.label”/>

<mx:Button id=”b4” x=”0” y=”30” label=”4” width=”36”

click=”b4.label=’9’;result.text+=b4.label”/>

<mx:Button id=”b5” x=”40” y=”30” label=”5” width=”36”

click=”mx.controls.Alert.show(‘Someone clicked on 5’);”/>

<mx:Button id=”b6” x=”80” y=”30” label=”6” width=”36”/>

</mx:Panel>

</mx:VBox>

<mx:Script>

<![CDATA[

import mx.controls.Alert;

private function displayDigit(digit:String):void{

result.text+=digit;

}

]]>

</mx:Script>

</mx:Application>

Listing 2.6 Calculator3.mxml – Adding event processing

Getting Familiar with Flex CHAPTER 2

Page 51: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

36 RIA WITH ADOBE FLEX AND JAVA

A click on the fifth button shows how to display a message box, and Figure 2.6 depicts a snapshot of Calculator 3’s program after pressing the first five buttons.

Figure 2.6: Calculator 3 after pressing 1, 2, 3, 4, and 5

Why ActionScript 3.0?If you have any experience with other programming languages, the natural question is why would you start programming Web applications in ActionScript 3 as opposed to Java or JavaScript?

The simple answer is that an object-oriented language like ActionScript 3 combined with MXML makes GUI programming easier. Java Enterprise Edition (Java EE) with its multi-threading fea-tures, plus reliable and scalable application servers, is a great choice for server-side applications. Java Swing, the library for GUI development is powerful but complicated. Dealing with event-dis-patch thread and synchronization issues, lack of data binding, and the need to program multiple event listeners manually makes programming in Java Swing a lot less productive.

ActionScript uses a single-threaded model (at least from the developer’s perspective) and avoids many of the Java Swing issues by making all client/server communications asynchronous. A client program sends a request to the server specifying the name of the callback handler, and the user can

Getting Familiar with Flex CHAPTER 2

Page 52: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 37

immediately continue working with the screen, which never “freezes.” When the result comes back, this handler will process it.

One of the ways to categorize programming languages is by how strict they are with variable types: some languages use static and others dynamic data typing. In statically typed languages (Java, C++, C#) you must declare a variable of a particular type, the compiler will ensure that your program uses this variable properly, and you should not expect any data type-related surprises at runtime. In dynamic languages (Perl, Python, ECMAScript, JavaScript) you don’t have to declare the vari-ables upfront.

ActionScript offers you a balance between static and dynamic languages by allowing you to pro-gram in both Java and JavaScript styles. But the freedom of not declaring variables comes with a price tag: any runtime environment can more efficiently allocate required resources if the types of program elements are known in advance. ActionScript 3 gives you the choice of writing highly ef-ficient, statically typed code, highly dynamic and loosely typed code, or a combination of the two.

ActionScript 3 is a compiled object-oriented language and its performance has been substantially improved compared to previous versions of the language. Its code is compiled into bytecode and runs in the ActionScript Virtual Machine (AVM) in Flash Player 9. Strictly speaking, Flash Player 9 includes two AVMs: AVM1 executes the code written in the older version of the language, while AVM2 runs the code produced by ActionScript 3. Having two VMs in Flash Player has increased the size of Flash Player to about 1.2MB, but it ensures that existing applications won’t break and users won’t need to keep the older version of Flash Player.

In AVM2, most of the code goes through a Just-in-Time compiler that converts the bytecode into machine code and substantially improves performance.

Another notable feature of AS3 (and MXML) is their support of ECMAScript for XML (E4X), which makes working with XML a breeze. You’ll see many examples of E4X usage in this book starting with Chapter 4.

Comparing ActionScript 3.0 and Java 5We can’t provide a detailed description of either language in this book, but a short comparison table of the major elements/concepts of these two languages may serve as a quick reference. You can read Table 2.4 either left-to-right or right-to-left depending on what your primary program-ming language is.

Concept/Language Construct

Java 5.0 ActionScript 3.0

Class library packaging .jar .swc

Inheritance Class Employee extends Person{…}

Class Employee extends Person{…}

Getting Familiar with Flex CHAPTER 2

Page 53: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

38 RIA WITH ADOBE FLEX AND JAVA

Variable declaration & initialization

String firstName=”John”;Date shipDate=new Date();int i;int a, b=10;double salary;

Var firstName:String=”John”;var shipDate:Date=new Date();var i:int;var a:int, b:int=10;var salary:Number;

Undeclared variables n/a It’s the equivalent of wild card-type notation *. If you declare a variable but don’t specify its type, the * type will apply

A default value is: undefined var myVar:*;

Variable scopes block: declared in curly braces, local: declared in a method or block

member: declared on the class level

no global variables

No block scope: the minimal scope is a function

local: declared within a function

member: declared on the class level

If a variable is declared outside of any function or class definition, it has a global scope.

Strings Immutable, store sequences of two-byte Unicode char-acters

Immutable, store sequences of two-byte Uni-code characters

Terminating statements with semicolons

A must If you write one statement per line you can omit it

Strict equality operator (comparing objects without the type conversion)

n/a ===for strict non-equality use !==

Constant qualifier The keyword final

final int STATE=”NY”;

The keyword const

const STATE:int =”NY”;

Type checking Static (checked at compile time)

Dynamic (checked at runtime) and static (it’s so-called ‘strict mode,’ which is the default in Flex Builder)

Type check operator instanceof is – checks data type, i.e., if (myVar is String){…}

The is operator is a replacement for older instanceof

The as operator n/a Similar to is operator, but returns not Boolean, but the result of expression:

var orderId:String=”123”;var orderIdN:Number=orderId as Number;trace(orderIdN);//prints 123

Getting Familiar with Flex CHAPTER 2

Page 54: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 39

Primitives byte, int, long, float, double,short, boolean, char

all primitives in ActionScript are objects. Boolean, int, uint, Number, String

The following lines are equivalent;var age:int = 25;var age:int = new int(25);

Complex types n/a Array, Date, Error, Function, RegExp, XML, and XMLList

Array declaration and instantiation

int quarterResults[];quarterResults = new int[4];

int quarterRe-sults[]={25,33,56,84};

Arrays in ActionScript resemble Java ArrayList:

var quarterResults:Array =new Array();orvar quarterResults:Array=[];

var quarterResults:Array= [25, 33, 56, 84];AS3 also has associative arrays that use named elements instead of numeric indexes (similar to Hashtable).

The top class in the inheri-tance tree

Object Object

Casting syntax: cast the class Object to Person:

Person p=(Person) myObject;

var p:Person= Person(myObject);or var p:Person= myObject as Person;

Upcasting class Xyz extends Abc{}Abc myObj = new Xyz();

class Xyz extends Abc{}var myObj:Abc=new Xyz();

Un-typed variable n/a var myObject:*var myObject:

Packages package com.xyz;class myClass {…}

package com.xyz{ class myClass{…}}ActionScript packages can include not only classes, but separate functions as well

Class access levels public, private, protectedif none is specified, classes have package access level

public, private, protected

if none is specified, classes have an internal access level (similar to the package access level in Java)

Getting Familiar with Flex CHAPTER 2

Page 55: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

40 RIA WITH ADOBE FLEX AND JAVA

Custom access levels: namespaces

n/a Similar to XML namespaces.namespace abc;abc function myCalc(){}

or

abc::myCalc(){}

use namespace abc;

Console output System.out.println(); // in debug mode onlytrace();

Imports import com.abc.*;import com.abc.MyClass;

import com.abc.*;import com.abc.MyClass;

packages must be imported even if the class names are fully qualified in the code.

Unordered key-value pairs Hashtable, Map

Hashtable friends = new Hashtable();

friends.put(“good”, “Mary”);friends.put(“best”, “Bill”);friends.put(“bad”, “Masha”);

String bestFriend= friends.get(“best”);// bestFriend is Bill

Associative Arrays

Allows referencing its elements by names instead of indexes.var friends:Array=new Array(); friends[“good”]=”Mary”;friends[“best”]=”Bill”; friends[“bad”]=”Masha”;

var bestFriend:String= friends[“best”];

friends.best=”Alex”;

Another syntax:var car:Object = {make:”Toyota”, model:”Camry”};trace (car[“make”], car.model); // Output: Toyota Camry

Hoisting n/a The compiler moves all variable declarations to the top of the function so you can use a variable name even before it’s been explicitly declared in the code

Instantiation objects from classes

Customer cmr = new Cus-tomer();

Class cls = Class.forName(“Customer”);Object myObj= cls.newInstance();

var cmr:Customer = new Customer();

var cls:Class = flash.util.getClassByName(“Customer”); var myObj:Object = new cls();

Getting Familiar with Flex CHAPTER 2

Page 56: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 41

Private classes private class myClass{…} There are no private classes in AS3.

Private constructors Supported. Typical use: singleton classes

Not available. The implementation of private constructors is postponed since they aren’t part of the ECMAScript standard yet.To create a Singleton use the public static method getInstance(). In the public constructor check if the instance already exists, throw an error.

Class and file names A file can have multiple class declarations, but only one of them can be public, and the file must have the same name as this class.

A file can have multiple class declarations, but only one of them can be put inside the package declaration, and the file must have the same name as this class.

What can be put in a package

Classes and interfaces Classes, interfaces, variables, functions, namespaces, and executable statements

Dynamic classes (define an object that can be al-tered at runtime by adding or changing properties and methods).

n/a dynamic class Person { var name:String;}//Dynamically add a variable // and a functionPerson p= new Person();p.name=”Joe”;p.age=25;p.printMe = function () { trace (p.name, p.age);}p.printMe(); // Joe 25

Function closures n/a. Closures support is a proposed addition to Java 7

myButton.addEventListener(“click,” my-Method); A closure is an object that represents a snapshot of a function with its lexical context (variable’s values, objects in the scope). A func-tion closure can be passed as an argument and executed without being a part of any object

Abstract classes Supported There is no mechanism to mark class methods with the abstract keyword that would en-force implementation of these methods in the descendents

Function overriding Supported Supported. You must use the override qualifier

Getting Familiar with Flex CHAPTER 2

Page 57: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

42 RIA WITH ADOBE FLEX AND JAVA

Table 2.4 Java/ActionScript cheat sheet

Method/constructor over-loading

Supported Not supported. You can use a workaround with …rest parameter:

public function MyCLass(…args) { switch (args.length) { case 0: constructor1(); return; case 1: constructor2(args[0]); return; case 2: constructor3(args[0], args[1]); return; … } }This sample covers the case of constructors having a different number of parameters. To make it work with functions having the same number of parameters but different types, you’d have to add the type check to each of the cases above, i.e., if(args[0] is String) { //do one thing }else if (args[0] is Number){ // do another thing }

Interfaces class A implements B{…}

interfaces can contain method declarations and final variables

class A implements B{…}

interfaces can contain only function declara-tions

Exception handling Keywords: try, catch, throw, finally, throws

Uncaught exceptions are propagated to the calling method

Keywords: try, catch, throw, finally

A method doesn’t have to declare exceptions.Can throw not only Error objects, but also numbers:

throw 25.3;

Flash Player terminates the script in case of uncaught exception

Regular expressions Supported Supported

Flex Framework API DocumentationFlex Framework comes with a fully documented API in a format similar to Java’s javadoc. We rec-ommend you download and install the Flex Framework documentation and create a bookmark in your Web browser to have it handy. For example, if you’ve unzipped the document files in the direc-

Getting Familiar with Flex CHAPTER 2

Page 58: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 43

tory C:\Flex2\doc\flex2docs\langref, bookmark the following URI: file:///C:/Flex2/doc/flex2docs/langref/index.html.

The Web browser’s screen will look like Figure 2.7.

Figure 2.7 Documentation on the Flex 2 API

To create similarly looking documentation for your own classes and components, get the ASDoc tool at: http://labs.adobe.com/wiki/index.php/ASDoc:Using_ASDoc. For more advanced docu-menting functionality, check the Flex2Doc utility at myflex.org.

Separating Design and DevelopmentWe’ve already learned that a typical Flex program consists of MXML, a markup language for creat-ing a GUI, and the programming language ActionScript. This opens up an opportunity to separate design and development responsibilities between different people, at least in enterprise project development. Mid-size and large organizations can have a group of UI designers that know how to prototype good-looking and intuitive Web sites.

In a perfect world, professional designers would create such prototypes using MXML; professional software developers would just write programs in ActionScript. In reality though your MXML files will have some amount of ActionScript. Try to keep it to a minimum but don’t kill yourself trying to achieve the perfect separation of responsibilities.

Getting Familiar with Flex CHAPTER 2

Page 59: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

44 RIA WITH ADOBE FLEX AND JAVA

Besides MXML, Web designers will find themselves at home with Cascading Style Sheets (CSS), which in Flex play the same role and have the same syntax as CSS in HTML. Your firm-specific fonts, coloring, and other styles should be done with CSS.

There is a slight difference in CSS use: in HTML pages, you can change the CSS file, press the Re-fresh button in your browser, and the appearance of the Web page will change accordingly. In Flex, if you assign CSS statically either by providing the name of a .css file or by using an <mx:Style> object, they are embedded into SWF, so if you decide to change the look of your GUI components, you have to modify the CSS file and recompile the application.

You can also create and assign styles to Flex components dynamically from within your Action-Script code by creating an instance of the StyleSheet object, assigning values to its properties, and then calling the setStyle method on the required UI component.

Flex Style Explorer is a handy tool that automates the process of writing CSS (see http://examples.adobe.com/flex2/consulting/styleexplorer/Flex2StyleExplorer.html). Select the kind of compo-nent or container that you want to make “stylish,” pick applicable parameters on the left, see how it looks in the middle, and copy the CSS from the right into your <mx:style> tag:

Figure 2.8 Flex-style Explorer

Getting Familiar with Flex CHAPTER 2

Page 60: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 45

Working with Display Objects in ActionScriptFlash Player shows your application’s objects in a display area that is known as “stage” and is rep-resented by the class Stage. When a program renders display objects to the stage, Flash Player uses a display list to manage them. In other words, your ActionScript program has to add required sub-classes of the DisplayObject to one of the containers so they can be rendered and displayed. Since the DisplayObject class is inherited from EventDispatcher, all GUI components are capable of pro-cessing events.

Each Flex application has at least one default container that is represented by a class Application that corresponds to the mxml tag <mx:Application>.

If you’re going to program in ActionScript, you should be aware of the fact that you can’t instantiate DisplayObject, but you can create instances of its subclasses (Shape, SimpleButton, and others) and add them to the display list. Flash Player renders changes to DisplayObject to the stage for each video frame (also called keyframe).

You can add several display objects to a display list of one of the subclasses of DisplayObjectCon-tainer. For example, the class Sprite is a direct descendant of DisplayObjectContainer, and flash.display.MovieClip is a subclass of Sprite.

While MovieClip is a suitable class for Flash programs that display a sequence of frames over a timeline, for business applications it’s the Sprite class, a lighter container that doesn’t include the timeline support. In ActionScript, Sprite represents a display object in a display list node. As an example, you can create an ActionScript class that extends Sprite, and add one or more display ob-jects to your class with a series of the method called addChild(). Flash Player finds classes through the root of the display list. Only after calling addChild() will a component be added to the display list and the SystemManager assigned to it.

The Application Loading Process Flex defines a number of so-called manager classes located in the package mx.managers. These classes control the mouse cursor, drag-and-drop operations, history (similar to the Web browsers’ Back/Forward buttons), pop-up windows, tool tips, and other things.

When Flash Player loads and starts your Flex application, the SystemManager controls the applica-tion window; creates and parents the Application instance, pop-ups, and cursors; and manages the classes in the ApplicationDomain. The SystemManager is the first class instantiated by Flash Player for your application. It stores the size and position of the main application window and keeps track of its children, such as floating pop-ups and modal windows. Using SystemManager you can access embedded fonts, styles, and the document object.

SystemManager also controls application domains, which are used to partition classes by the se-curity domains (see the description of the ApplicationDomain class in the Flex Language Refer-ence).

Getting Familiar with Flex CHAPTER 2

Page 61: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

46 RIA WITH ADOBE FLEX AND JAVA

If you’ll be developing custom visual components (descendents of the UIComponent class), keep in mind that initially such components aren’t connected to any display list and SystemManager=null. Only after the first call of the addChild() will a SystemManager be assigned. You shouldn’t access SystemManager from the constructor of your component, because it can still be null at the mo-ment.

In general, when the Application object is created, the following steps are being performed:

1. Instantiation of the application object begins.2. Initialization of the Application.systemManager property.3. The application dispatches the pre-initialize event at the beginning of the initialization pro-

cess.4. The createChildren()method is called on the application object. At this point each of the

application’s components is being constructed, and each component’s createChildren() will also be called.

5. The application dispatches the initialize event, which indicates that all the application’s com-ponents have been initialized.

6. A creationComplete event is being dispatched.7. The application object is added to the display list.8. The applicationComplete event is being dispatched.

In most cases, you’ll be using the MXML tag <mx:Application> to create the application object, but if you need to create it in ActionScript, don’t create components in its constructor – use createChil-dren() for this (for performance reasons).

Flex SWF files have only two frames. The SystemManager, Preloader, DownloadProgressBar, and a handful of other helper classes live in the first frame. The rest of the Flex Framework, your ap-plication code, and embedded assets like fonts and images reside in the second frame. When Flash Player initially starts downloading your SWF, as soon as enough bytes come for the first frame, it in-stantiates a SystemManager that creates a Preloader, which in turn creates a DownloadProgressBar and these two objects are watching the rest of the byte streaming-in process.

When all bytes for the first frame are in, SystemManager sends the enterFrame to the second frame and then dispatches other events. Eventually, the application object dispatches the application-Complete event.

You may by wondering why you need to know what’s happening before the application loads. This knowledge may come in handy especially if your application is large and users experience unpleas-ant delays during the initial load. You can use this time productively, for example, to let the user log on to this application. This will also give the user a perception that your application loads faster. Farata Systems has created an open source logon component that can be used in Preloader (see http://flexblog.faratasystems.com/?p=88).

You can also create a splash screen that fades away with an image of your choice by substitut-

Getting Familiar with Flex CHAPTER 2

Page 62: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 47

ing the standard Flex Preloader and the DownloadProgressBar objects with the custom ones. Ted Patrick from Adobe has provided sample applications that display a splash screen using an image from PNG, SWF, or GIF files (see http://www.onflex.org/ted/2006/07/flex-2-preloaders-swf-png-gif-examples.php ). While your splash screen is displayed, you can download some other system resources and/or libraries and then the splash screen fades away.

Here’s another idea. During the loading process, your ActionScript code may interact with the “outside world” aka Web browser. The class ExternalInterface has a method addCallback() that lets you register an ActionScript method as callable from the container. After a successful invocation of addCallBack(), the registered function in Flash Player can be called by the JavaScript or ActiveX code in the container. You’ll find more on the ExternalInterface in Chapter 15.

One more suggestion: your application may need to load another application(s) from an SWF. If you have to do it in ActionScript, you can use the SWFLoader class from the applicationComplete event (see more on SWFLoader in Chapter 10).

SummaryIn this chapter, we first created simple applications and peeked under Flex’s hood. The most im-portant feature of Flex 2 is that it’s an open and extendable framework. If you are accustomed to being in complete control with Java, Flex doesn’t tie your hands either. In the next chapter we’ll get familiar with the Eclipse-based IDE called Flex Builder.

Getting Familiar with Flex CHAPTER 2

Endnotes

1. Besides mxmlc and compc Flex has one more compiler called the Web tier compiler. This compiler comes

with Flex Data Services and lets you create swf files during the runtime on the server side. This compiler

runs in a Java EE servlet container.

2 . Flex compiler itself is just a wrapper that internally calls the Java archive called mxmlc.jar (or compc.

jar). This compilation process can be initiated from a Java program if needed. See http://livedocs.

macromedia.com/flex/2/docs/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_

Parts&file=00001489.html.

3. If you’re using a Web-tier compiler, your user can specify a URL of the remote MXML file, which will be

compiled into the swf file and delivered without an HTML wrapper.

4 . You can skip this step if you have Eclipse installed – Ant is located in its plug-ins directory.

Page 63: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 49

Flex Builder Development Environment

3CHAPTER

Page 64: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

50 RIA WITH ADOBE FLEX AND JAVA

Installing and Configuring Flex BuilderThe Flex Builder IDE is based on Eclipse and it’s a more productive environment compared to programming with Flex in a text editor. This IDE comes in two flavors: a standalone version and an Eclipse plug-in. You’ll have to choose one of these versions during the installation process. But the standalone version of Flex Builder 2.0 doesn’t contain Java Development plug-ins, also known as JDT (Java Development Tools). You need JDT to support Java projects, perspectives, etc., so we recommend installing the plug-in version of Flex Builder on top of the pre-installed Eclipse 3.2. During the Flex Builder install process, you’ll have to specify the directory where your Eclipse re-sides (the one that has a sub-directory called plugins).

After Flex Builder is installed, start Eclipse, select the menus Window | Open Perspective | Other, and open the Flex Development perspective. The Flex Debugging perspective will be automatically added to the Eclipse IDE. The Flex Builder comes with great help content, which in the plug-in ver-sion is located under the menu Help | Help Contents | Adobe Flex 2 Help or Help>Flex Start Page.

Yet Another Hello WorldWe’re not going to bore you with all the little details of the Eclipse-based Flex Builder, because they’re covered pretty well in the product manual Using Flex Builder 2. You can also read the section on Flex Builder using its help menu. We’d rather walk you through the process of creating a simple Flex project with several basic applications. While building these applications, we’ll touch on vari-ous areas of the Flex Builder’s workbench to give you a quick feel for the functionality.

Always start by creating a new project. Select the File>Flex Project menu item to open the New Project wizard. The first decision you’ll have to make is to choose the way your application (in fact, all the applications in this project) is going to access remote data:

Flex Builder Development EnvironmentCHAPTER 3

Flex Builder Development Environment

Page 65: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 51

Figure 3.1 Creating the Flex project – project type selection

We’ll discuss Flex Builder’s project types later in this chapter in the section “Project Types and Data Access.” Since we’re not going to use any server-side technology in this chapter, the Basic type is our choice so just press the Next button. Now you’ll have to pick the name and location of the project:

Figure 3.2 Naming the project

Once you’re done, click Next and, in the following screen, specify the name of the main application file and output folder:

Flex Builder Development EnvironmentCHAPTER 3

Page 66: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

52 RIA WITH ADOBE FLEX AND JAVA

Figure 3.3 Naming the main application file

Enter HelloWorld.mxml as the name of the main application file. The compiled application will be saved in the bin directory unless you specify another output folder. Click Finish to complete the project creation.

After the project is created, you’ll see a screen similar to Figure 3.4 with the pre-generated file Hel-loWorld.mxml.

Flex Builder Development EnvironmentCHAPTER 3

Page 67: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 53

Figure 3.4 A new project has been created

The application may also include other MXML and ActionScript files with application logic as well as images and other resources commonly referred to as “assets.”

Have you noticed the two tabs above the code editor? By default, FlexBuilder displays the source code view, but since we have to design our HelloWorld screen, we’ll start working in the design mode (as you become more experienced, you’ll be using the design mode mostly for quickly proto-typing your screen), so click on the Design tab and let the fun begin.

Figure 3.5 A design mode tab

Flex Builder Development EnvironmentCHAPTER 3

Page 68: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

54 RIA WITH ADOBE FLEX AND JAVA

In the middle of the screen in Figure 3.5 you’ll see how your future window will look. All folders with predefined Flex components are located on the left: layouts, navigators, standard and custom controls. Select the Label control, then drag-and-drop it onto the screen.

In the middle of the screen you can see canvas of the Application container. Available Flex frame-work components (as well as custom ones that you develop for your project) are presented in the tree view in the Components panel in the lower left. Select the Label control parented by Controls, then drag-and-drop it onto the screen. Change the label’s text to “Hello, World!” Now, switch to the Category View in the Flex Properties panel, located in the lower right corner of the screen. In the Styles category set the fontSize to 22 and fontWeight to bold.

If you didn’t select a specific control prior to changing the font attributes, it would affect all the controls of this window.

Figure 3.6 Drop the label component on the design panel

Now take a peek at the source tab. Flex Builder has generated a line similar to this one:

<mx:Label x=”41” y=”51” text=”Hello, World!” fontWeight=”bold” fontSize=”22”/>

and the entire application now looks like:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”>

Flex Builder Development EnvironmentCHAPTER 3

Page 69: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 55

<mx:Label x=”41” y=”51” text=”Hello, World!” fontWeight=”bold” fontSize=”22”/>

</mx:Application>

Listing 3.1 HelloWorld.mxml

Working with the Source PanelType the space anywhere inside the <mx:Label> tag and enjoy instant access to all the attributes including fontSize and fontWeight:

Figure 3.7 Tag attributes code hinting

In fact, you could have just typed the letter “f” and the list of attributes would be conveniently lim-ited to the ones starting with “f.”

Similarly, were you to type “<mx:Bu”, you’d be prompted with all the MXML components that start with “Bu”:

Figure 3.8 Code completion

Flex Builder Development EnvironmentCHAPTER 3

Page 70: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

56 RIA WITH ADOBE FLEX AND JAVA

As you can see, the Source Panel is as effective in creating and modifying MXML elements as the Design Panel.

Running HelloWorldPress the right mouse on the HelloWorld.mxml in the Navigator Panel on the upper left portion of the screen. Select RunAs…>Flex Application and you should see a screen like this:

Figure 3.9 Running HelloWorld in a Web browser

Congratulations! Your first Flex Builder program is up and running.

Right-click inside this window. You’ll see the pop-up menu without the View Page Source option. No wonder, your application has been compiled and launched inside the virtual machine called Flash Player, that’s why you can’t see its source code. Actually, at the end of this chapter we’ll show you how you can enable this View Source option and make your project’s source code available to the general public.

But you can also open the bin directory, double-click on the Flash movie SWF file, and your Flash player will run your program without the browser. Now the Flash Player will run your application independently of the Web browser:

Figure 3.10 Running HelloWorld in the Flash player

Flex Builder Development EnvironmentCHAPTER 3

Page 71: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 57

Building the Project To build simple applications like ours, the code is compiled into a single compressed archive with an .swf extension, which is saved in the output directory based on the Flex Builder settings of your project, which is bin by default. You can change this using the menus Project | Preferences | Flex Build Path. During the project build, Flex Builder also creates a handy HTML wrapper for your .swf file, so you can test it in a Web browser.

Your project can be built either automatically (every time you save code changes) or manually based on the Build Automatically setting in the menu Project. Typically, builds are done incremen-tally based on the data you’ve modified since the last build. But if you’d like to rebuild the entire project from scratch, just use the menu item Project | Clean.

If you specified that your project depends on other ones (see the Project References screen in the Properties menu), Flex Builder will figure out the order of the project builds, or you can specify the order using the menus Windows | Preferences | General | Workspace | Build Order.

For more sophisticated projects, builds may consist of multiple steps including invocating addi-tional programs, copying files, etc. Flex Builder supports using the Apache Ant tool, and we’ll use Ant in Chapter 10.

Flex Builder-Generated FilesWhen your application, say, HelloWorld.mxml, is compiled for the first time, you’ll find the follow-ing freshly generated application-specific files in your output folder:

HelloWorld.swf : The byte code (113Kb) ready to run in the Flash PlayerHelloWorld.html: An HTML wrapper (5Kb) to run in the Web browser HelloWorld-debug.swf: The byte code with debug information (180Kb) to run in the debug ver-sion of Flash Player (this version of the player is installed automatically with Flex Builder). HelloWorld-debug.html: An HTML wrapper (5Kb) to run the debug version of HelloWorld.swf in your Web browser

In the output directory you’ll also find some files that are shared by all the applications from your project:

AC_OETags.js: A bunch of JavaScript functions used by the HTML wrapper, i.e., the user’s Flash Player’s version detection playerProductInstall.swf: A small (1Kb) used by the HTML wrapper to upgrade the user’s Flash Player plugin to version 9, if neededhistory.* : The Flex’s implementation of the history functionality (7Kb total), similar to the history in Web browsers

To deploy your application under a Web server, you’ll need to copy there all of the above files except the debug ones and provide your user with the URL of the file HelloWorld.html.

Flex Builder Development EnvironmentCHAPTER 3

Page 72: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

58 RIA WITH ADOBE FLEX AND JAVA

While building a wrapper is an automated process, chances are you’ll have to tweak it to set certain options in the browser/application integration like history management or setting the DHTML objects for Flex/JavaScript interaction. You will need to review the files in the html-template used to generate the HTML wrapper and customize them to fit your needs.

Your resulting SWF file may use some of the external resources, for example, image files, which may either be embedded in your SWF files with an [EMBED] tag, or remain external. In the latter case you can replace the image files without needing to recompile your SWF. Just make sure that the op-tion Copy non-embedded files to output directory is selected in the project properties screen below. You can also turn off the generation of the HTML wrapper.

Figure 3.11 Configuring compiler properties

Running Applications in Flex BuilderA typical project consists of multiple MXML and ActionScript files and only one of them should be created as an application by including the <mx:Application> tag as opposed to MXML components that don’t need it. On a similar note, Java projects consist of multiple classes, but only one of them should have the method main() that makes the class executable. During the development stage, though, you might want to have more than one MXML file with the <mx:Application> tag, which raises the question: which one of these files will be considered the main one when you run your project? To set the default application file of the project, right-click on the file and select the option Set as the Default Application from a pop-up menu.

You can also run non-default MXML files from your project as long as they have the Application tag. Just right-click on the file and select the menu option Run as a Flex Application. You can always

Flex Builder Development EnvironmentCHAPTER 3

Page 73: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 59

check which MXML files can run by selecting the Project Properties menu and the option Flex Applications. You’ll see a pop-up screen that will list all current runnable files and will let you add (remove) more MXML files to this list.

Simple Event ProcessingThe next goal is to start getting familiar with Flex event processing and understand the relations between MXML and ActionScript languages.

We’re going to create a new application called HelloBasil that will contain three components: a text field, a button, and a label. The user will enter a name in the text field (i.e., Basil), press the button, and the label component will display today’s date and a greeting (i.e., “May 1, 2006: Hello, Basil”). This application will have to process the button-click events with the help of ActionScript.

A Pure MXML Version In the Flex Builder’s menu select File | New | Flex Application and enter HelloBasil as the application name. Using the dropdown at the bottom of the dialog, select the vertical layout and press Finish.

Drag and now drop the TextInput component from the Components view on the left onto the de-sign area. Enter input_1 as the ID of this component.

Next, add a Button component and set its ID to btn_1 and its label to “Greet Me”, then put the Text component under the button. Remove the default text and set its ID to output_1.

Switch to the Source Pane to see the generated mxml:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”>

<mx:TextInput id=”input_1” />

<mx:Button id=”btn_1” label=”Greet me” />

<mx:Text id=”output_1” />

</mx:Application>

Listing 3.2 HelloBasil.mxml

Run the program. If you press the “Greet me” button, it won’t do much since there’s no code yet for processing the button click event:

Flex Builder Development EnvironmentCHAPTER 3

Page 74: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

60 RIA WITH ADOBE FLEX AND JAVA

Figure 3.12 Running HelloBasil

Specifying Events HandlersLet’s add the processing of a button click event. Type a space before the forward slash in the <mx:Button> line to activate the list of tag attributes, and type “c” to accelerate navigation to attributes that start with “c”:

Figure 3.13 A sample of Flex Builder code hinting

Select the click attribute and press Enter. Alternatively, you could have just typed click=””. We’re going to fill in the blank value of the click attribute with the script, corresponding to the action this button will perform: we want to set the text property of output_1_ to the concatenated “Hello” and the text of input_1. Flex Builder will keep helping you by suggesting available properties of the screen components as you type. The new version of our button’s code will look like this:

<mx:Button id=”btn_1” label=”Greet me”

click=”output_1.text=’Hello,’ + input_1.text” />

Run the program, type in the name, click the button, and if your name is Basil, your screen will look like this:

Flex Builder Development EnvironmentCHAPTER 3

Page 75: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 61

Figure 3.14 After clicking the Greet Me button

As alternative to coding the script directly inside the click attribute, we could have moved this script to an application-level function and called it:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical” >

<mx:Script>

<![CDATA[

private function greetMe():void {

output_1.text=’Hello,’ + input_1.text;

}

]]>

</mx:Script>

<mx:TextInput id=”input_1” />

<mx:Button id=”btn_1” label=”Greet me” click=”greetMe()” />

<mx:Text id=”output_1” />

</mx:Application>

Listing 3.3 HelloBasil.mxml with ActionScript

Flex MXML supports embedding ActionScript as the literal value of the <mx:Script> tag. Generally speaking, it’s safer to safeguard your code in the CDATA section since it may contain symbols such as “<”, which will break the well-formness of MXML.

Layouts Flex GUI components live in so-called containers that let you arrange these components, provide a consistent view of your application regardless of the current size of the window, and navigate from one view to another.

Flex Builder Development EnvironmentCHAPTER 3

Page 76: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

62 RIA WITH ADOBE FLEX AND JAVA

Let’s look at the layouts using our sample HelloBasil.mxml. Switch to the Design View in Flex Build-er and change the layout from vertical to horizontal in the Flex Properties view. Your components will be immediately rearranged horizontally and, if you run the application, it’ll look as follows (compare with Figure 3.14):

Figure 3.15 HelloBasil with the horizontal layout

Even if you start resizing the browser’s window, the relative positioning of the components in the container will remain the same. In Java Swing, a BoxLayout class provides a similar functionality.

Change the layout to absolute, rerun the application, and resize the browser’s window. You’ll see that HelloBasil has lost its flexibility: now all the container’s components are glued to their posi-tions regardless of the size of the visible portion of the browser. You should use the absolute layout only if you’re sure that your GUI application doesn’t have to be resizable. The Flash Player renders the components for the containers with absolute layouts slightly faster than with horizontal or vertical layouts.

To relieve a bit the stiffness of the absolute layout, Flex lets you specify so-called constraints. In a con-tainer with an absolute layout, you can specify top, bottom, and center anchors (the distances from the components to the edges or the center). Instead of using x and y coordinates, you can use the right, left, top, and bottom attributes. This so-called constraint-based layout will resize the screen compo-nents if the user starts resizing the window. In Flex Builder’s design mode, select one or more of the components and you’ll be able to specify the anchors. For example, the setting shown in Figure 3.16 forces selected component(s) to be “pinned” at a distance of 150 pixels from the right edge of the con-tainer and 60 pixels from the bottom even if the browser’s window is being resized.

Flex Builder Development EnvironmentCHAPTER 3

Page 77: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 63

Figure 3.16 Specifying layout constraints

Basically we can say that Flex’s constraint-based layout is a simplified version of Java SpringLay-out.

In the real world the GUI is more complex than in HelloBasil and you’ll have to combine contain-ers with different layouts in the same application. For example, you can put horizontal (HBox) and vertical (VBox) containers in your application. Containers can be nested and arranged in other containers called Panels that also support layouts. Besides these flexible containers you can add another one called Canvas, which keeps its components in absolute positions relative to the top left corner of the Canvas.

In addition to the layout containers, Flex supports navigation containers (i.e., ViewStack, Button-Bar, and others) that let you switch from one container to another. This topic is out of the scope of this chapter, but we’ll use all of these containers in Chapter 5 in a more realistic application.

View States and TransitionsAnother noteworthy feature of Flex Builder is related to Flex View States. For example, we could have added a checkbox to the HelloBasil application so that when the checkbox is checked, the application reveals an additional Panel with some extra information (perhaps the messages that were left for Basil by the nightshift production support crew). The screen of the running application would look like Figure 3.17.

Flex Builder Development EnvironmentCHAPTER 3

Page 78: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

64 RIA WITH ADOBE FLEX AND JAVA

Figure 3.17 An advanced state of HelloBasil

To achieve this, we could have written the checkbox’s click script to manipulate the visibility of the panel. Alternatively, we can define a view state, named advanced, and prescribe that in this state the width and height of the panel will be 50% and 75%, while in the default state they will be set to 0.

Flex Builder allows visually creating multiple states and maintaining state properties via the States View, usually displayed at the top right corner of the Flex Builder workbench. If you don’t see this view, bring it up by using the menus Windows | Show View | States. Figure 3.18 is an illustration of a modified Hello Basil with the “advanced” state. Please note that using the States View you can navigate from state-to-state and instantly see the changes to the application.

Figure 3.18 Designing a state called advanced

Flex Builder Development EnvironmentCHAPTER 3

Page 79: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 65

The code of the corresponding version of HelloBasil is in Listing 3.4:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical” >

<mx:TextInput id=”input_1” />

<mx:Button id=”btn_1” label=”Greet me” click=”output_1.text=’Hello,’ + input_1.text”

/>

<mx:Text id=”output_1” />

<mx:CheckBox id=”cbx_1” label=”Advanced”

click=”currentState=cbx_1.selected?’advanced’:’’” />

<mx:transitions>

<mx:Transition fromState=”*” toState=”advanced”>

<mx:Resize target=”{advanced_panel}” duration=”400”/>

</mx:Transition>

</mx:transitions>

<mx:states>

<mx:State name=”advanced”>

<mx:SetProperty target=”{advanced_panel}” name=”width” value=”50%”/>

<mx:SetProperty target=”{advanced_panel}” name=”height” value=”75%”/>

</mx:State>

</mx:states>

<mx:Panel id=”advanced_panel” title=”Advanced Info” width=”0” height=”0”/>

</mx:Application>

Listing 3.4. HelloBasil.mxml with states and a transition

To make the switch between two states smoother, we’ve used a special Flex element called Transi-tion. In this particular example we’ve used a transition with a special effect, Resize, requesting Flex to make a transition from the base to the advanced state gradually during a time interval of 400 milliseconds.

Fine Tuning Flex BuilderFlex Builder consumes system resources and its performance depends on your system configura-tion and the amount of memory installed in your computer. As your projects grow, FlexBuilder will require more memory to build and navigate them. If it works slowly, try to reserve more heap memory. If you start Flex Builder using an icon on your desktop, request more memory in eclipse.ini (for the plug-in version) or in FlexBuilder.ini (for a standalone version). Specify the minimum and maximum of the reserved heap memory using parameters of the Java Virtual Machine (JVM):

Flex Builder Development EnvironmentCHAPTER 3

Page 80: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

66 RIA WITH ADOBE FLEX AND JAVA

• vmargs• Xms 128M• Xmx512M

In the sample above, Xms requests a minimum of 128MB of heap memory that can grow to 512MB (Xmx).

If you start Eclipse in Flex Builder from a command line, you can achieve the same effect by enter-ing these parameters manually in a command line, which in the Windows OS may look like:

“c:\eclipse\eclipse.exe” –vmargs -Xms128M -Xmx512M

These parameters may improve the responsiveness of Flex Builder and the build time of your proj-ects if you’re using the same VM for builds. The larger minimum heap delays the moment when the Java garbage collector kicks in for the first time. Other parameters to fine tune the garbage collec-tion of Flex Builder are –XX:MinHeapFreeRatio and –XX:MaxHeapFreeRatio, which will control the minimum and maximum amount of heap space after garbage collection.

Please note that these parameters only affect the performance of the Flex Builder ID; your com-piled programs will run at the same speed as before. When this chapter was written, Flex 2 did not provide tools for fine tuning garbage collection, which is done internally by the Flash Player.

Remember that by reserving too much memory for Flex Builder, you’re decreasing the amount of memory left in your system for other applications.

To monitor your Eclipse memory consumption, download the plugin from KyrSoft (http://www.kyrsoft.com/opentools/memmon.html) and unzip it in your Eclipse installation directory. The ex-cellent plugin not only allows you to monitor memory, but also request garbage collection when memory reaches specified thresholds (see Eclipse menu Window | Preferences | Java | Memory Monitor).

Some people recommend closing an unused project; some suggest replacing the JRE that comes with Flex Builder (see the JRE folder under your Flex Builder installation directory) to a newer one. While Flex Builder is a huge leap forward compared to an older Flex development environment, at the time of this writing Flex Builder is slower than Java Eclipse IDE, which will hopefully change in future releases of Flex.

Debugging with Flex BuilderFlex comes with two ways to error-proof your application. First, there’s a full-featured debugger that lets you set breakpoints and see variables and walk through the execution stack. This is great for debugging your application logic. To debug the GUI, we use tracing since it lets us dump any information we need to the console/log file without interrupting and potentially changing the ap-plication flow. The debugger provides a Console window that automatically displays the trace out-

Flex Builder Development EnvironmentCHAPTER 3

Page 81: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 67

put, so we usually trace our programs in debug mode.

Let’s illustrate how to use the Flex Builder debugger with our HelloBasil application. Let’s modify the greetMe() function to display today’s date. We’ll declare and initialize the ActionScript variable of type Date and concatenate the current date to the greeting.

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

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical” >

<mx:Script>

<![CDATA[

private function assert(condition:Boolean, text:String) :void {

if(condition) trace(text);

}

private function greetMe():void {

var dt : Date = new Date();

assert(dt.hours>20, “time to go home”);

output_1.text=’Hello,’ + input_1.text;

trace(“greetMe:” + output_1.text);

}

]]>

</mx:Script>

<mx:TextInput id=”input_1” />

<mx:Button id=”btn_1” label=”Greet me” click=”greetMe()” />

<mx:Text id=”output_1” />

</mx:Application>

Listing 3.5 Adding assertions and tracing for debugging

To toggle a breakpoint just double-click on the gray area to the left of the line where you want a program to stop, and Flex Builder will mark this line with a blue bullet. We’ll put a breakpoint at the last line of the greetMe() method. To run the program in the debug mode, select the application in the Navigator view and select “Debug As…” in the context menu.

As soon as you press the “Greet me” button, your program will reach the breakpoint and Flex Build-er will pause to give you a chance to analyze the values in the variables you’re interested in. To step through the execution of your program, you can use the little yellow arrows at the top of the debug view.

Flex Builder Development EnvironmentCHAPTER 3

Page 82: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

68 RIA WITH ADOBE FLEX AND JAVA

Figure 3.19 Debugging with Flex Builder

The Variables view lets you check and change the values of your program variables. If a variable represents a composite object, you can easily see its content by pressing the little plus sign on the right. For example, the variable this represents the object you’re in – your main application instance. In the Console window we can see the results of our trace statements. Please note the assert() method – this technique is often used to reduce the amount of trace output if the entries comply with the listed assertions.

Flex Builder allows you to perform conditional debugging instead of setting up the breakpoints. Just call the method enterDebugger():

if (myVar > 25)

enterDebugger();

You still need to run your application in the debug mode, but if myVar is not greater than 25, Flex Builder will execute your program without stopping in the debugger.

Project Types and Data AccessOne of the major differences between Flex 2 and previous versions is its ability to compile your

Flex Builder Development EnvironmentCHAPTER 3

Page 83: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 69

code on a standalone client machine without using any servers. All the samples in this chapter where compiled locally on the client machine and they didn’t rely on any data located on external servers, so our project is basic. However, your application can directly talk to a remote Web Service despite the fact that your project is basic.

On the other hand, in Chapters 5 and 6 we’ll be using Flex remote objects and data services, and we’ll select an optional Flex Data Services that assumes you already have it installed and deployed under the Java EE servlet container of your choice. For these types of projects, you’ll be asked to specify the URL of your Flex Data Services install (we’ll do this in Chapter 5), and after compiling, your project will be embedded into your SWF file.

If you select Flex Data Services as your project type, you’ll have to specify if you’re planning to com-pile your project locally using Flex Builder or on the server when the page is viewed.

Generated ActionScriptAs we’ve mentioned before, the Flex compiler converts MXML into ActionScript source code and then compiles it into an swf or swc file. If you’d like to see this ActionScript code, add an additional compiler argument – keep-generated-actionscript in the screen shown below and recompile your project. Flex Builder will create a sub-directory called generated, where you can find the source code of all the generated ActionScript files.

Figure 3.20 Asking the compiler to keep generated ActionScript files

Flex Builder Development EnvironmentCHAPTER 3

Page 84: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

70 RIA WITH ADOBE FLEX AND JAVA

After closing the dialog, you’ll also see the new “generated” folder in Flex Builder’s project tree containing about 50 ActionScript files. Those files are used to initialize a framework, provide CSS support, and divide our application into resources, classes that have to be initialized on the appli-cations load, etc. We highly recommend that you spend some time reviewing the generated code after learning a new feature/control to get familiar with the dependencies and intercommunica-tions with the Flex framework.

Figure 3.21 An auto-generated ActionScript for HelloWorld.mxml

Flex Builder Tips and Keyboard ShortcutsFlex Builder has many convenient keyboard commands that make your programming faster. For the complete list of all available shortcuts, press Ctrl-Shift-L. We’ve listed some of the Flex Builder tips and the keyboard shortcuts here that we use on a regular basis:

• If you see a little asterisk in the tab with the file name, this means that this file has some un-saved code changes.

Flex Builder Development EnvironmentCHAPTER 3

Page 85: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 71

• Highlight the name of the class or a function and press the button F3 on your keyboard. This will take you to the source code of this class.

• If some of the lines are marked with red error circles, move the mouse over the circle and Flex Builder will display the text describing this error.

• Press Ctrl-Space to get context-sensitive help.

• Alt + / will complete the name of the variable or a class after you typed in a couple of its first letters.

• Press Ctrl-F11 to run the last launched program again. Hit the F11 key alone to run the pro-gram in debug mode.

• Place the cursor after a curly brace and Eclipse will mark the matching brace.

• Highlight a line or a block of code and press Alt-ArrowUp or Alt-ArrowDown. The selected code will move up or down.

• Ctrl-H will open a popup to search on multiple files, and Ctrl-F will let you find text in the open file.

• Highlight the MXML tag or ActionScript class and press Shift-F2. This will open the screen with the language reference help on the selected element.

• Highlight a block of code and press Ctrl-Shift-C to comment out the selected block of code.

• To select surrounding containers, select a GUI control and press F4.

• Keep the Control key down while moving the mouse over MXML tags. Flex Builder will show a tool tip with the name of the corresponding ActionScript class. If the tag turns into a hyperlink and the source code is available, click on the link to open the source code of the class.

• As you change your code in the editor, Flex Builder puts a vertical purple bar on the left of each modified line. This is quite handy if you’d like to have a quick look at the new code before pressing the button Save.

• If you want to test your application in different browsers installed on your computer, select the menu Windows | Preferences | General | Web Browser and select the browser to be used by Flex Builder for launching your application.

• If you see an error message that reads “…Check the Error Log file”, this log file is located in your workspace, in directory called .metadata.

Flex Builder Development EnvironmentCHAPTER 3

Page 86: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

72 RIA WITH ADOBE FLEX AND JAVA

Publishing the Application Source CodeIf you’d like to make the source code available to the users of your deployed applications (similar to the View Source option in the Web browsers in thin clients), use the menu Project | Publish Ap-plication Source.

This menu item will quickly build a set of files in the sub-folder srcview in the output folder with the source code of your project ready to be published on the Web as in Figure 3.23.

Figure 3.22 Selecting the source code to be published

Besides preparing the source files for publication, Flex Builder will also add to the Application the attribute viewSourceURL pointing at the location of the source code:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”

viewSourceURL=”srcview/index.html”>

<mx:Label x=”41” y=”51” text=”Hello, World!” fontWeight=”bold” fontSize=”22”/>

</mx:Application>

Listing 3.6 Enabling the View Source option

Flex Builder Development EnvironmentCHAPTER 3

Page 87: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 73

Figure 3.23 Published source code in the folder bin/srcview

Next time you run this application, right-clicking the mouse will open up a menu with one extra item: View Source. Something you got accustomed to in the HTML/JSP-based application, but a lot better organized.

Figure 3.24 The run-time menu displayed on right-click

Using the Subversion Version Control System Eclipse and hence Flex Builder come with a pre-installed CVS plug-in that you can use as your ver-sion control system without any additional client installations. Open the menus Window | Open Perspective | Other and you’ll see the CVS Repository Exploring (see Figure 3.25).

But CVS isn’t your only choice. Subversion is another popular open source version control system that’s easier to administer, allows versioning support for directories, and has some other advan-tages. Subversion is available for free at http://subversion.tigris.org/. You can also download and

Flex Builder Development EnvironmentCHAPTER 3

Page 88: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

74 RIA WITH ADOBE FLEX AND JAVA

install one of the plug-ins for Eclipse (i.e., Subversive or Subclipse), which puts your repository just one click away from your Flex Builder projects. A free online version of the Subversion book is available at http://svnbook.red-bean.com/ and here we’ll just provide the information relevant for configuring the Flex Builder client to be used with Subversion repositories.

We assume that you already have the Subversion server installed, the source code repository for your project configured, and the proper permissions to access this repository.

Start by downloading and installing the Subversion client, which will let you to do all the version control operations from the command line. While most of the time you may be using some Subver-sion plug-in from Flex Builder, this client software will become handy if you decide to automate your application’s build process. For example, you may write an Ant build script that will start by running Subversion commands cleanly, checking out all the files from the repository.

You can download and install prepackaged binaries of Subversion at http://subversion.tigris.org/project_packages.html.

After installing your client make sure that the system variable APR_ICONV_PATH is defined and points at the iconv directory in your Subversion client directory. If your Subversion server is located on the Unix box accessed using ssh, also define a variable SVN_SSH that points to the location of your ssh client. For example:

SVN_SSH=C:\\my_ssh_client\\ssh2.exe

The next step is to install the Subversion plug-in (we use Subclipse here) so you can use the Sub-version repository right from your Flex Builder projects. Select the menu Help | Software Updates | Find and Install, and in the pop-up screen pick the option Search for new features to install. In the next screen enter Subclipse in the name field and the following URL in http://subclipse.tigris.org/update_1.0.x.

Figure 3.25 Eclipse perspectives including the Subversion plug-in

Flex Builder Development EnvironmentCHAPTER 3

Page 89: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 75

Check the Subclipse box in the next screen and finish the install.

After a successful install, you’ll find a new perspective called SVN Repository Exploring as in Figure 3.25. Now the Eclipse Help menu will have a new section on using Subclipse.

Finally, you need to specify the location of the Subversion repository of your project: the system administrator who installed and configured the repository will give you the parameters of the Sub-version server. After getting this information, right-click in the Navigator view, select New | SVN Repository, and enter its URL. The Eclipse help menu has a section on it.

If you’re connected to your repository, right-click on your project and select the Team item from the menu. It should open a pop-up window with the typical version control options: Checkout, Update, Commit, Diff, etc.

If you‘d like to add an existing project to the repository, use the menu Team | Share Project. To get the project from repository into Flex Builder, open your SVN Repository Exploring perspective, right-click on a project, and select the option Check Out as Project. The rest of the commands are pretty intuitive (don’t forget about committing your changes) and are well described in the Subver-sion documentation.

If for any reason you’re not happy with the Subclipse plug-in, try the Subversive client, which is available at http://www.polarion.org/.

Figure 3.26 Select Perspective with Subclipse plug-in

Flex Builder Development EnvironmentCHAPTER 3

Page 90: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

76 RIA WITH ADOBE FLEX AND JAVA

SummaryIn this chapter we’ve introduced you to a developer-friendly Flex Builder tool. Even though our sample applications were very basic, you’ve had a chance to see and enjoy the ease of developing in Flex Builder. You can find a detailed description of all Flex Builder features in the manual using Flex Builder 2, which is available for download at www.flex.org. In the following chapters we’ll gradually increase the complexity of the examples, so fasten your seat belts, please.

Flex Builder Development EnvironmentCHAPTER 3

Page 91: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 79

Learning Flex Through Applications

4CHAPTER

Page 92: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

80 RIA WITH ADOBE FLEX AND JAVA

In this chapter we’ll roll up our sleeves and build several mini-applications to illustrate the use of various elements and techniques of both ActionScript 3.0 (AS3) and MXML that you’ll be using in real-world projects. But this is not a tutorial and we’ll be introducing these elements on an as-need-ed basis. Our goal is to give you the big picture, but you should study the product documentation for detailed coverage of each of these elements.

In particular, this chapter contains:

• A description of some of the AS3 features that may seem unusual to Java programmers• A review of object-oriented programming techniques in AS3 • A short description of ECMAScript for XML, which offers simplified processing of the XML

sources• An overview of the Flex event model• An application that uses namespaces in AS3• A program that uses Flex data binding and regular expressions• An application illustrating the communication between Flex and JavaServer Pages• A sample application showing different ways of achieving the same results by people with dif-

ferent programming backgrounds• An application illustrating the use of collections, filters, and timers

ActionScript Dynamic ClassesLike ActionScript 2 and JavaScript, AS3 lets you add and remove properties on-the-fly. In particular, it’s possible to add properties programmatically and add or redefine methods during runtime.

Let’s consider the following Person class:

dynamic class Person {

public var firstName:String=”Joe”;

public var lastName:String=”Doe”;

}

Learning Flex Through ApplicationsCHAPTER 4

Learning Flex Through Applications

Page 93: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 81

We can create an instance of Person, add the property age, and override the default method to-String():

var p:Person = new Person();

p.age=25; // dynamic property

p.toString = function():String { // overridden method

return p.lastName + “,” + p.firstName + “-” + age;

}

trace(p); // Doe,Joe-25

Note that dynamically added or redefined functions can’t access private members of the dynamic class: had we declared Person’s lastName as private, trace(p) would result in “undefined,Joe-25.”

Adding or redefining a method isn’t any different from that of any different property type. After all, a function is just a property with a type Function.

Suppose the JavaProgrammer class is defined as:

public class JavaProgrammer {

public var lastName:String=”Mr. Interface”;

public function learnFlex():void {

trace(lastName + “ is ready for RIA work!”);

}

}

Then an instance of Person can “attach” the method learnFlex():

var p:Person=new Person();

var jp:JavaProgrammer = new JavaProgrammer();

p.boostCareer = jp.learnFlex;

and then invoke the method:

p.boostCareer(); // Mr. Interface is ready for RIA work!

The attached function retains the scope of the original instance of JavaProgrammer: it prints “Mr. Interface is ready for RIA work!” –not “Doe is ready for RIA work.”

Most importantly, however, is that the definition of Person starts with the keyword dynamic. What makes AS3, as a scripting language, stand out from its peers is the optional strong typing and, first of all, the just-in-time (JIT) compilation of strongly typed code blocks into native machine code. Having bet on performance, AS3 changed the rules of engagement: now instead of assuming that

Learning Flex Through ApplicationsCHAPTER 4

Page 94: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

82 RIA WITH ADOBE FLEX AND JAVA

the class allows dynamic properties or methods, it defaults to the opposite. Regular classes, as they are known in Java, are called sealed in AS3. To take advantage of dynamic properties and methods you have to prefix the class definition with the magic word dynamic.

What happens if you don’t? For starters, the default mode of the Flex Builder compiler is strict and unless you turn it into a so-called standard (strict=”false”), you won’t be able to compile the above snippet. Then, having turned off the compile-time type checking of AS3, you’d get hit by runtime type checking.

In full compliance with AS2 and JavaScript, it’s also possible to add/modify the property or method on the entire dynamic class via its prototype object:

Person.prototype.age = 46;

Person.prototype.fullName = function():String {

return this.lastName + “,” + this.firstName ;

}

trace(p.fullName()); // Doe,Joe46

Notice how the value of age has been effectively modified on the instance p that’s already been created.

The delete operator destroys the property of the dynamic object and makes it eligible for garbage collection:

delete p.age;

trace(p); // Doe,Joe-undefined

Some of the Flex classes are intentionally defined as dynamic, those being Object, Array, MovieClip, NetConnection, and AsyncToken. When this chapter was written, the subclasses of the dynamic classes were not dynamic by default. Because of this, you may run into a runtime error: imagine a sealed class S that extends a dynamic class D. If you create an object as

var dynObj:D = new S(),

an attempt to add a property to dynObj will produce a runtime error because the variable dynObj points to a sealed object. So,

dynObj.favoriteBand=”Beatles”;

would cause

ReferenceError: Error #1056 Can’t create a property. favoriteBand…

Learning Flex Through ApplicationsCHAPTER 4

Page 95: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 83

You should be aware of the tradeoff: natively compiled code performs faster in the order of magnitude; it’s also significantly more efficient in terms of memory consumption. On the other hand, dynamic classes bring lots of flexibility to programming. For instance, they help you get away from marshaling incompatibilities during remote calls. Compare this with Java RMI. For the successful serialization of a class between two JVMs, the definition of the class has to be present on each end1. Importantly it has to be exactly the same definition. But with dynamic classes it’s not a must. And, as far as object-oriented terms go, you could say that dynamic classes offer “O-O on steroids” (use at your own risk).

Methods, Functions, and ClosuresIn addition to the methods of a particular class, ActionScript supports user-defined functions, such as application-level functions and package-level functions. It’s a bit unusual for Java programmers that not all user-defined functions should be parts of some class definition, but, hey, in scripting languages a function is an object of itself!

You can define a function using either function statements or function expressions. A function statement declaration of the function calcTax() that returns a number may look like:

public function calcTax(income:Number):Number{

return income*0.28;

}

var myTax:Number=calcTax(70000);

Alternatively you may use a function expression. One typical use case for a function expression, aka an anonymous function, is assigning it to some property of an object. That’s exactly what we’ve done in the prior section when we defined the fullName method:

Person.prototype.fullName = function():String {

return this.lastName + “,” + this.firstName ;

}

Here’s another example: var calcFunc:Function = function (income:Number):Number{

return income*0.28;

}

var myTax:Number=calcFunc(88000);

There’s no fundamental difference between the latter use cases and a regular function statement, because a function declaration is equivalent to defining a variable of the type Function and assign-ing it a function expression. There’s a nuisance, however, when an anonymous function is attached to a property of an dynamic object: when the property gets deleted (fullName), the lifespan of the function is over2.

Learning Flex Through ApplicationsCHAPTER 4

Page 96: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

84 RIA WITH ADOBE FLEX AND JAVA

Another popular use case for anonymous functions is the event handler function:

remoteObject.addEventListener(

“fault”,

function(event:FaultEvent):void {

// .. .. ..

}

)

Note that the syntax above can cause memory leaks since it requires the VM to make a reference to this object and place it inside a global anonymous function that makes garbage collection unavail-able on “this.” Proper application techniques say to use a bound method and a weak reference/re-moveEventListener method to simplify memory de-allocation.

Function ParametersIn AS3, primitive parameters are passed by values, and non-primitives are passed by reference. You can include default values for the function parameters. For example:

public function calcTax(income:Number, dependents:int=1):Number{

return (income*0.28 – dependents*500); //Give 500 credit for each dependent

}

var tax=calcTax(10000); // tax is equal to 2300

Within the function body AS3 supports special objects called arguments. These are arrays that in-clude actual arguments passed to the function. The length of these arrays may be different from the number arguments the function declares. The additional property arguments.callee contains a reference to the function itself.

Also, the AS3-specific way of supporting a variable number of function arguments is the so-called … (rest) parameter. Ellipses followed by the name represent an array that accommodates any num-ber of arguments that you pass in excess of the declared parameters, which always precede the rest parameter:

public static function calcTax(… taxParams):Number{

for (uint i=0; i< taxParams.length; i++){

trace(taxParams[i]);

}

}

Java programmers may find the … (rest) similar to a varargs notation. You can mix the … (rest) with other function parameters so long as it’s the last parameter of the function.

Learning Flex Through ApplicationsCHAPTER 4

Page 97: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 85

Here’s an idea for using the … (rest) parameter to overcome the absence of overloaded functions and constructors in AS3: use a switch case operator to branch on the number of actual arguments:

Class MyClass {

public function MyCLass(...args) {

switch (args.length) {

case 0: constructor1(); return;

case 1: constructor2(args[0]); return;

case 2: constructor3(args[0], args[1]); return;

...

}}

To ensure proper processing in cases where the number of arguments is the same but the types are different, you’d have to complement this with type checks: if(args[0] is String) {

// process as String

} else if (args[0] is Number){

// process as Number

}

Getters and SettersAS3 getters and setters form an alternative mechanism for defining object properties. Unlike Java, where getters and setters are part of the JavaBeans specification, AS3 getters and setters are first-class language citizens. Accordingly, rather than relying on the con-vention of set/get prefixes for function names, AS3 offers set-and-get modifiers to follow the function keyword. Here is an example of the class Tax with the read/write property income:

public class Tax extends EventDispatcher {

private var myIncome:Number;

public function set income(inc:Number):void{

myIncome=inc;

// perform related actions, dispatchEvents, etc.

}

public function get income():Number{

return myIncome;

}

}

Listing 4.1 Class Tax

Learning Flex Through ApplicationsCHAPTER 4

Page 98: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

86 RIA WITH ADOBE FLEX AND JAVA

The code snippet below instantiates the class Tax and assigns and prints the income using the get-ter and setter of the class Tax:

var tx:Tax=new Tax();

tx.income=5000.00;

trace(“The income is “ + tx.income);

Functions as ObjectsJava programmers may find this concept a bit unusual, but AS3 functions are objects of the type Function, and they can have their own properties and internal methods and can be passed to other functions as arguments. A Function type can’t be extended since it’s defined as “final” so you can’t manipulate it in an OO way. Functions are used in either a bound or closure mode. Bound functions are identical to Java ones in that they are defined within the class and their lexical environment is the instance of the enclosing class.

public class MyClass extends Label {

public function addNumbers(a:Number, b:Number) : void {

text = a + b;

}….

}

To use the addNumber method you have to provide a reference to an instance of the MyClass ob-ject. It’s a direct call to the instance and the context of the instance is available to the function.

You can find other examples of using a function object in some of the ActionScript methods. For example, when you add a listener to process an event, you register in ObjectProxy.addEventLis-tener (“myEvent,” myEventHandler) and call a name of the event to process and a function that will handle this event. Note that the name of the myEventHandler function is not followed by parenthe-ses here because we’re not calling the function myEventHandler, but are passing a reference to its code, which will be called if and only if the myEvent event in the ObjectProxy object occurs.

The fact that you can pass just the name of the function as a parameter makes it impossible for the compiler to check that this passed function is called with proper parameters, which may poten-tially lead to runtime errors. Thorough testing of Flex applications is even more important than in Java.

ClosuresClosures play a cornerstone role in dynamic languages. They’re essential for implementing fea-tures like OO or building frameworks. At the same time, the formal definition of closures doesn’t really help in understanding them. Let’s go through a few examples. First, we’ll show what closures look like and then we’ll give you their use patterns.

Learning Flex Through ApplicationsCHAPTER 4

Page 99: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 87

It all starts with the use of an anonymous function that has access to variables in the outer lexical scope:

<?xml version=”1.0”?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”vertical” creationComplete=”doInit(event)”>

<mx:Script>

import mx.controls.Alert;

private var greeting:String=”Hello”;

private function doInit(evt:Event) : void {

btn.addEventListener(“click”, function(evt:Event):void {

Alert.show( greeting + “, “ + txt.text);

});

}

</mx:Script>

<mx:Button id=”btn” label=”closure” />

<mx:TextInput id=”txt”/>

</mx:Application>

Listing 4.2 Anonymous function accessing outer scope variables

Compile and run this code – it shows a message box with the value of txt.text that was not defined in the closure.

Here’s an oversimplified three-part description of closures:

1 Closures are functions that are defined in a class or function context and passed to another object for execution at a later time.

2. A closure’s “attachment” happens at runtime (and can be executed multiple times during an application run). It’s just the stack-frame allocation where all context variables (“greeting,” in our case) are being saved for later use. In most cases it’s about surrounding variables of the hosting function and current runtime class instance.

3. Finally, closure’s execution can happen at any time, and can also have parameters when it’s called. A standard convention is to have an Event object passed in with information from the calling object.

It seems you can use closures to “snapshot” any number of parameters. Unfortunately, that’s true for some dynamic languages, but not for ECMAScript ones like ActionScript and JavaScript. Let’s illustrate the difference with a few examples. First, let’s make sure that the ActionScript closures are compile-time artifacts rather than true dynamically interpreted counterparts. Just swap the order of the closure and greetings definition statements.

<?xml version=”1.0”?>

Learning Flex Through ApplicationsCHAPTER 4

Page 100: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

88 RIA WITH ADOBE FLEX AND JAVA

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”vertical” creationComplete=”doInit(event)”>

<mx:Script>

import mx.controls.Alert;

private var myClosure :Function = function(evt:Event) {

Alert.show( greeting + “, “ + txt.text);

}

private function doInit(evt:Event) : void {

btn.addEventListener(“click”, myClosure);

}

private var greeting:String=”Hello”;

</mx:Script>

<mx:Button id=”btn” label=”closure”/>

<mx:TextInput id=”txt”/>

</mx:Application>

Listing 4.3 Closures are compile-time artifacts

It still works even though “greeting” should have been undefined at the time of the closure defini-tion – proving that just the reference is being used. Also, unlike Java, the scope of an object is the stack-frame content of the enclosing function or class. Here is an example that wouldn’t compile in Java, but is perfectly legal in AS3:

private function aaa():void{

{ var a = 1; } //in Java a is not visible outside of the block

Alert.show(a);

}

Flash is a stack machine. A closure is a stack variable in the enclosing function, and this stack-frame approach greatly simplifies the implementation of closures and code optimizers based on the stack, even though it requires some adjustments in coding style. Another issue is that we don’t have object values here – everything is done by reference. Let’s modify the greeting’s value right before the call:

<mx:Button label=”closure” click=”greeting=’good morning’”/>

As you can see, the greeting was replaced on an alert screen with the new value – it wouldn’t happen if the “closure” used a greeting reference by value at the time of definition.

Closures are first-class citizens of ActionScript. Every method in your class is a closure. That’s how it knows the instance variables of the class. Essentially every class is a big closure. You can write a function with closures inside that would be very much a class for all practical purposes.

Closures are unavoidable when you use asynchronous operations or need to process an event on

Learning Flex Through ApplicationsCHAPTER 4

Page 101: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 89

the other object. Almost any non-trivial action in Flex – communication with the server, say, or get-ting input from a user – is asynchronous. Using closure automatically gives you the reference to the class instance in which you have your function to the external object processing the event. That’s sufficient for processing the asynchronous method’s results in most cases. Automatic pointing of this context to the instance defining the function greatly simplifies the coding since it’s natural to the developer.

Before Flex 2, in Flex 1.5 developers were responsible for supplying context to the closure. The abil-ity to replace the closure context gives greater flexibility to the code and makes it truly dynamic.

The next code sample shows a closure on an arbitrary object to provide a custom context object:

public class Closure extends Object {

public static function create(context:Object, func:Function, ... pms):Function {

var f:Function = function():*

{

var target:* = arguments.callee.target;

var func:* = arguments.callee.func;

var params:* = arguments.callee.params;

var len:Number = arguments.length;

var args:Array = new Array(len);

for(var i:Number=0; i<len; i++){

args[i] = arguments[i];

args[“push”].apply(args, params);

return func.apply(target, args);

};

var _f:Object = f;

_f.target = context;

_f.func = func;

_f.params = pms;

return f;

}

}

Listing 4.4 A closure and a custom context object

The following code illustrates how to call this closure:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” creationComp

lete=”onCreationComplete(event)”>

Learning Flex Through ApplicationsCHAPTER 4

Page 102: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

90 RIA WITH ADOBE FLEX AND JAVA

<mx:Script>

<![CDATA[

import mx.controls.Alert;

private var myClosure:Function ;

private function onCreationComplete (evt:Event):void {

myClosure = Closure.create({greeting:”Good evening”},function(name:String):void

{

Alert.show( this.greeting + “, “ + name);

},”world”);

var greeting:String;

greeting =”Hello”;

}

]]>

</mx:Script>

<mx:Button id=”btn” label=”closure” click=”myClosure()” />

</mx:Application>

Listing 4.5 Testing a closure

Now the alert shows “Good evening, world” because the method has been applied using a differ-ent context. Often this methodology is called “delegation” and is used by business frameworks to centralize the processing of certain events.

The example above illustrates the relationship between context, functions, and closures. Using this technique lets you implement dynamic inheritance, polymorphism, and other OO concepts. Java 6 doesn’t support closures, but they may be introduced in Java 7. Closure functions weren’t included in the original Java specification because back then the plan was to allow object creation only in the heap memory if a new operator was called. But Java 5 introduced auto-boxing, the first violation of this principle because objects were created dynamically behind the scenes. If you find the concept of closures appealing, we’d recommend taking a look at products like BeanShell for Java and other Java interpreters that let you define whole classes or just methods as closures.

The closest constructs to closures in Java are anonymous inner classes (see section 15.9.5 of the Java Language Specification).

Let’s consider an example of an event listener in Java Swing where you define an anonymous class at the moment a listener is added. The following Java code snippet adds an event listener to a but-ton:

JButton myButton = new JButton(“Place Order”);

myButton.addActionListener(new ActionListener(){

public void actionPerformed(ActionEvent ae){

//your code to place an order goes here...

Learning Flex Through ApplicationsCHAPTER 4

Page 103: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 91

}

} // end of anonymous class declaration

);

This code means that the addActionListener() method in Java requires an instance of the listener class as an argument. But since this listener will only be used by this button, why bother even nam-ing the class? The code above (starting with new and ending with a curly brace) defines and creates an instance of an anonymous class and overrides the method actionPerformed() that processes a clicked event. Looks a bit convoluted, doesn’t it?

In AS3, you just define a function and pass it to the listener as an argument for an execution. For example:

myButton.addEventListener(“click”, placeOrder);

Here placeOrder is a function closure that defines the business logic; it’s passed to the method ad-dEventListener() and is executed as soon as the button is clicked.

To pass a closure to another method and execute it, Flex creates a dynamic object behind the scenes and wraps the closure up. In the example below, we define the following function closure:

gotoWebPage(theURL: String) {

// open a specified Web page in a new Web browser’s window

getURL(theURL, “_blank”);

}

Let’s pass the reference to this to a timer function. The next line will execute the function gotoWeb-Page() in five seconds while passing it the target URL as an argument:

setTimeout(gotoWebPage, 5000, “http://www.theriabook.com”);

Interestingly enough, the utility function setTimeout() is itself a closure. Adobe’s language refer-ence suggests using the Timer object instead for efficiency reasons: when you create an instance of an object yourself (as opposed to the dynamic objects that are used with closures), the garbage collector’s work becomes a bit easier. So setTimeout() and some other timing utility functions exist in AS3 only for backward compatibility/simplicity reasons.

Asynchronous ProgrammingTraditional Java programmers have to get used to the fact that when a client makes an RPC call to a server, it actually just requests the execution of this method, allowing the user to continue working with the application. It doesn’t block until the result comes back. After some time, which could be as long as several seconds, the result or error message can come back to the client, and it needs to remember which component has sent this request and process it accordingly. You’ll see an example

Learning Flex Through ApplicationsCHAPTER 4

Page 104: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

92 RIA WITH ADOBE FLEX AND JAVA

of such an application in Chapter 5. Meanwhile, we’ll walk you through a high-level overview of the communication process.

ActionScript doesn’t have an explicit multithreading API. It can be emulated by running multiple instances of the player within the hosting page, but it’s not a trivial task. On the bright side, Flex makes standard tasks that require multithreading much simpler. Instead of explicit multithread-ing, the classes that need to be executed asynchronously use pre-built Flash player components that handle all the tracking and optimization.

A financial news service from Yahoo accessed via an HTTPService is a good example of asynchro-nous communication:

<mx:HTTPService id=”newsFeed” destination=”YahooFinancialNews” /> …

var token: AsyncToken = newsFeed.send({security:”ADBE”});

token.responder = new Responder(processBusinessNews, processBusinessNewsErrors);

The last two lines mean the following: request the news about Adobe (ADBE), and expect the result to come back as an instance of the AsyncToken object.

Flash Player takes care of tracking multiple requests and the invocation of the responders’ methods upon completion. A responder specifies that upon return, Flash Player calls processBusinessNews when successful and processBusinessNewsErrors if it fails. The processBusinessNews function is also known as an event handler and must be present in the code.

The processBusinessNews function may look like this:

public function processBusinessNews(evt:Event):void {

myNewsCollection.addNews(evt.result.businessnews);

// code to display the news on the screen goes here

}

Data BindingThe Flex Developers Guide states that “Data binding is the process of tying the data in one object to another object. It provides a convenient way to pass data around in an application.”

We’d like to offer you another definition: data binding is the ability of one object to watch the chang-es of a specified property(ies) on another object, expression, or a function that returns a value.

To watch such changes in Java, you’d need to write and register listeners in one object, while an-other object has to fire appropriate events when the property in question changes. Even though AS3 has similar language constructs, there’s a much easier way to do this in Flex.

Flex offers you several ways of binding data between the objects using a very simple syntax and

Learning Flex Through ApplicationsCHAPTER 4

Page 105: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 93

the job of creating listeners will be done for you automatically without having to write such code manually. You can review the listeners and other generated code by using the compiler option –keep-generated-actionscript=true.

Binding in MXMLJust to give you a quick peek into data binding in its simplest form, look at the sample application in Listing 4.6 that consists of a TextInput field and a label. The text of the label is bound to the text of the input field by a simple use of curly braces:

text=”{myTextField.text}”

This line “tells” myLabel: “Watch the text property of the object called myTextField and as soon as it gets modified, update yourself.”

You won’t find any listener declaration or firing events in the code below. The very fact that we’ve put the text property in curly braces will force the Flex compiler to generate all the required AS3 listeners, event dispatchers, and binding automatically.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”>

<mx:TextInput id=”myTextField” x=”59” y=”41” />

<mx:Label id=”myLabel” text=”{myTextField.text}” x=”69” y=”89” width=”150” />

</mx:Application>

Listing 4.6 BindingTextFieldAndLabel.mxml

Figure 4.1 Typing in a text field is reflected in the label

This application may seem pretty useless, but we’ll give you plenty of real-world examples that demonstrate the power of data bindings in every chapter in this book. These are just some of the nearest examples:

Learning Flex Through ApplicationsCHAPTER 4

Page 106: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

94 RIA WITH ADOBE FLEX AND JAVA

• Watching a panel state as in Listing 4.13• Binding a data collection to the DataGrid as in Listing 4.28 • Displaying errors as in Listing 5.10• Implementing master-detail relations between components as in Listing 5.21

But to understand these and other examples, we have to cover some data binding basics.

In each pair of bound objects there’s a source, destination, and triggering event. In our example myTextField is the source, myLabel is the destination, and the “change” of the text in the myText-field is the triggering event. You can bind more than two objects by creating a so-called bindable property chain: object A is a source for destination B, which in turn is a source for destination C and so on.

To specify binding in MXML, you can use either the curly braces as we did before, or a dedicated <mx:Binding> Tag. The previous example can be rewritten as follows:

<mx:TextInput id=”myTextField” x=”59” y=”41” />

<mx:Label id=”myLabel” x=”69” y=”89” width=”150” />

<mx:Binding source=”myTextField.text” destination=”myLabel.text” />

Your program can have multiple <mx:Binding> tags using the same destinations and/or sources.

Binding ExpressionsTo complicate our example a little bit, we’ll add one more destination:

<mx:Label id=”myTwoPlusTwoLabel”

text=”{2+2+ Number(myTextField.text)}” x=”69” y=”109” width=”150” />

Now we have one source and two destinations (two labels). But more importantly, we’ve included an expression inside the curly braces. If you run this program, it’ll show 4 as the value of the second label. Start typing digits in the text field and both labels will immediately reflect the changes and the value in the second label will be greater by 4.

Learning Flex Through ApplicationsCHAPTER 4

Page 107: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 95

Figure 4.2 One source, two destinations

Entering an alpha character will display NaN (not a number) as the result of our expression evalu-ation.

On the same note, you can use an AS3 function in the binding expression as long as it returns a value.

What’s Under the Hood?Obviously, using curly braces is just the tip of the iceberg. How does this binding magic work in-ternally?

The Flex compiler generates bindable wrappers in AS3 for each property or expression that is be-ing watched (i.e., PropertyWatcher), and it’ll generate all the required AS3 code for event process-ing (see the section on events later in this chapter). And of course, it’ll create an instance of an mx.binding.Binding object that ties everything together.

Metatags are a way of providing compilers/linkers with information that’s not part of the lan-guage. In the Java universe, annotations are peers of AS3 metatags. For data binding, AS3 offers the metatag [Bindable]. The information in the metatags is used to generate additional framework code and instruct the compiler to use the generated code whenever the annotated properties/methods are used.

For example, let’s see the inner workings of our TextImput/Label binding example (the code is gen-erated by the Flex compiler). We start in the source code of the TextInput control and analyze the declaration there. This is what we find:

[DefaultBindingProperty(source=”text”, destination=”text”)]

[DefaultTriggerEvent(“change”)]

[Bindable(“textChanged”)]

[NonCommittingChangeEvent(“change”)]

Learning Flex Through ApplicationsCHAPTER 4

Page 108: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

96 RIA WITH ADOBE FLEX AND JAVA

These binding declarations cause the compiler to generate the following data structures:

binding = new mx.binding.Binding(this,

function():String

{

var result:* = (myTextField.text);

var stringResult:String = (result == undefined ? null :

String(result));

return stringResult;

},

function(_sourceFunctionReturnValue:String):void

{

myLabel.text = _sourceFunctionReturnValue;

},

“myLabel.text”);

_bindings[0] = binding;

watchers[1] = new mx.binding.PropertyWatcher(“myTextField”,

{

propertyChange: true

}

);

watchers[2] = new mx.binding.PropertyWatcher(“text”,

{

textChanged: true,

change: false

}

);

watchers[1].addListener(bindings[0]);

watchers[1].propertyGetter = propertyGetter;

watchers[1].updateParent(target);

watchers[2].addListener(bindings[0]);

watchers[1].addChild(watchers[2]);

bindings[0].uiComponentWatcher = 1;

bindings[0].execute();

What about watching not just the properties but the expressions? Consider this one:

2+2+ Number(myTextField.text)

Learning Flex Through ApplicationsCHAPTER 4

Page 109: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 97

It works the same way. The Flex compiler automatically generates an anonymous wrapper function for the source of the event, which is an expression:

function():String {

var result:* = (2+2+ Number(myTextField.text));

var stringResult:String = (result == undefined ? null : String(result));

return stringResult;

}

If you’d like to learn more about binding internals read the section in the Flex Developer’s Guide called “About the Binding Mechanism.”

Binding in ActionScriptYou can put the [Bindable] metatag above one or more properties of an AS3 class, but if you’d like all the properties of the class to be bindable, just add one [Bindable] metatag above the class definition:

[Bindable]

public class myClass{

}

If you don’t specify which event should trigger an update of the destination, Flex will use proper-tyChange by default. But this metatag allows another syntax that lets you specify any other appli-cable event name.

For example, if the variable price should trigger some actions when the stock price changes, you can define a custom event priceChanged (described later in this chapter) and use the following syntax:

[Bindable(event=”priceChanged”)]

var price:Number;

A quick peek at the generated folder reveals that the [Bindable] metatag causes the compiler to generate a shadow class with getters/setters for each bindable property. In the setter method the compiler adds a dispatch event to notify listeners of the object change. Also, the destination object automatically gets a binding/watcher for the respective source.

In some cases it is beneficial to provide dynamic binding.

For details, check out the documentation for the classes BindingUtils and ChangeWatcher.

Binding Inside a String and Application ParametersIn this little section we’ll kill two birds with one stone.First, we’ll show you how to pass parameters

Learning Flex Through ApplicationsCHAPTER 4

Page 110: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

98 RIA WITH ADOBE FLEX AND JAVA

to a Flex application from the HTML wrapper generated by Flex Builder, and then you’ll see how to use the binding inside a text string.

Our next assignment will be to write a Flex application that will run against different servers (dev, uat, prod) without having to recompile SWF. It doesn’t take a rocket scientist to figure out that the URL of the server should be passed to SWF as a parameter, and we’ll do this by using a special flash-Vars variable in the HTML wrapper. Flex’s documentation suggests including flashVars parameters in the Object and Embed tags and reading them using Application.application.parameters in AS3 code. As this was written, this doesn’t work. But as the ancient saying goes, “Adobe closes one door but opens another one.” But first, let’s get familiar with Flex code:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”

applicationComplete=”initApp()”>

<mx:Label text=

“Will run the app deployed at http://{serverURL}:{port}/MyGreatApp.html” />

<mx:Script>

<![CDATA[

[Bindable]

var serverURL:String;

[Bindable]

var port:String;

function initApp():void{

serverURL=Application.application.parameters.serverURL;

port=Application.application.parameters.port

}

]]>

</mx:Script>

</mx:Application>

Listing 4.7 BindingWithString.mxml

The script portion of this code gets the values of parameters serverURL and port (defined by us) using the Application object. We’ll add the values of these parameters to the HTML file as described below. These values are bound to the MXML label as a part of the text string.

If you’ll open the HTML file generated, you’ll find the JavaScript function AC_FL_RunContent that includes flashVars parameters in the form of key-value pairs. For example, in our sample application it looks like this:

“flashvars”,’historyUrl=history.htm%3F&lconid=’ + lc_id +’’

Learning Flex Through ApplicationsCHAPTER 4

Page 111: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 99

Add our parameters serverURL and port to this string:

“flashvars”,’serverURL=MyDevelopmentServer&port=8181&historyUrl=history.

htm%3F&lconid=’ + lc_id

Run the Application and it’ll display the URL of the server it connects to. If you’d like to test your ap-plication against a QA server, just change the values of the flashVars parameters in the HTML file.

Figure 4.3 The output of BindingWithinString.html

We have one last little wrinkle to iron out: if you manually change the content of the generated HTML file, next time you clean the project in Flex Builder, its content will be overwritten and you’ll lose the added flashVars parameters. There’s a simple solution to this problem: instead of adding flashVars parameters to the generated HTML, add them to the file index.template.html from the html-template directory, which Flex Builder uses to generate the run and debug versions of the HTML wrapper.

Of course, this little example doesn’t connect to any server, but it gives you an idea of how to pass the server URL (or any other value) as a Flash parameter, and how to assemble the URL from a mix of text and bindings

Is Data Binding a Silver Bullet?The ease of use of Flex data binding is very addictive, but there are cases when using data binding isn’t recommended. For example, if a number of changes is done on various properties of a class and you want to display the final state only when all the changes are complete, making each data item bindable would generate unnecessary event firing after each data change.

The other drawback to using binding to tie together properties of different components is that it as-sumes some knowledge about the component internals. It makes application design a bit complex because it statically links components together and makes changes interdependent. It also requires the compiler to generate a lot of cross-referencing code that consumes both time and memory.

Learning Flex Through ApplicationsCHAPTER 4

Page 112: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

100 RIA WITH ADOBE FLEX AND JAVA

Alternative architecture is to use loosely bound components. You can read about them in the sec-tion on Custom Events below.

Program in Style or an Elevator PitchWe usually run Flex training for our private clients, but once in a while we teach public classes for people with different programming backgrounds, and each of them comes with a different under-standing of how to do things right.

We’ll tell you a story that might have happened in real life, but first, we’ll remind you of the old Indian tale about seven blind men and an elephant. One blind man touched the elephant’s head, another one the tail, another was by the leg and each of them visualized the elephant differently based on what he touched.

Students usually arrive in the classroom early, but this time three seats were empty. Five minutes later the instructor got a phone call from one person explaining that the three had gotten stuck in the elevator and would be there for another 15 minutes until the serviceman arrived. Needless to say each of them had a laptop (do not leave home without one), so the instructor gave them a short assignment to help them use the time productively. Here’s the assignment:

Create a window with a panel that can resize itself with the click of a +/- button located in the right-hand corner of the panel. One click should minimize the panel’s height to 20 pixels, and a subsequent one should maximize to 100 pixels, and so on. For example, these are the two states of such a panel:

Figure 4.4 Two states of the panel

Learning Flex Through ApplicationsCHAPTER 4

Page 113: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 101

From COBOL to FlexA COBOL programmer thought to himself, ”We used to write long programs because during job interviews they usually ask how many lines of code I wrote. These guys are different, so to earn a good grade, this program should be small.” He finished the program on time and this is what it looked like:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”>

<mx:Panel id=”thePanel” title=”The Panell” height=”90” width=”100%” headerHe-

ight=”20” />

<mx:HBox width=”100%” horizontalAlign=”right” paddingRight=”2”>

<mx:Label text=”-” fontSize=”16” width=”20” height=”17” fontWeight=”bold”

id=”minimizeActions”

click=”{if (minimizeActions.text==’+’){

minimizeActions.text=’-’;

thePanel.height=100;

} else {

minimizeActions.text=’+’;

thePanel.height=20;

}

}” />

</mx:HBox>

</mx:Application>

Listing 4.8 The “Cobol” version

From Java to FlexThe Java programmer thought, “The standard Flex Panel class doesn’t have a property that remem-bers the current state of the panel, but Flex components are easily extendable, so I’ll create a de-scendent of the panel in ActionScript, add a private state flag (minimized), a public setter and get-ter, and a resize function. That way my new panel class will be reusable and self-contained.” This is his reusable AS3 class called ResizableJPanel:

package {

import mx.containers.Panel;

public class ResizableJPanel extends Panel {

// state of the panel

private var isPanelMinimized:Boolean;

public function get minimized():Boolean{

return isPanelMinimized;

}

public function set minimized(state:Boolean){

Learning Flex Through ApplicationsCHAPTER 4

Page 114: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

102 RIA WITH ADOBE FLEX AND JAVA

isPanelMinimized=state;

}

public function resizeMe():void{

if (minimized){

minimized=false;

height=maxHeight;

} else {

minimized=true;

height=minHeight;

}

}

}

}

Listing 4.9 The “Java” version of the panel class

This is the Javist’s MXML code:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:local=”*”

layout=”absolute”>

<local:ResizableJPanel id=”aPanel” height=”90” width=”100%”

title=”The Panel” minHeight=”20” maxHeight=”100” headerHeight=”20” />

<mx:HBox width=”100%” horizontalAlign=”right” paddingRight=”2”>

<mx:Label text=”-” fontSize=”16” width=”20” height=”17” fontWeight=”bold”

id=”minimizeActions” click=”resizePanel(aPanel)” />

</mx:HBox>

<mx:Script>

<![CDATA[

function resizePanel(thePanel:ResizableJPanel):void{

if (thePanel.minimized){

minimizeActions.text=”-”;

thePanel.resizeMe();

} else {

minimizeActions.text=”+”;

thePanel.resizeMe();

}

}

]]>

</mx:Script>

Learning Flex Through ApplicationsCHAPTER 4

Page 115: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 103

</mx:Application>

Listing 4.10 The MXML from a Java student

From Smalltalk to FlexThe Smalltalk guy thought, “Let me see if the standard panel is a dynamic class. If not, I’ll extend it just to make it dynamic and assign the panel’s state on-the-fly. I hope the instructor isn’t one of those object-oriented Nazis.” This is his panel AS3 class that just adds a dynamic behavior to the panel:

package{

import mx.containers.Panel;

public dynamic class ResizableSmtPanel extends Panel

{

}

}

Listing 4.11 Dynamic subclass of a panel

His MXML class looked like this:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” layout=”absolute”>

<ResizableSmtPanel title=”The Panel” id=”thePanel” height=”90” width=”100%”

minHeight=”20” maxHeight=”100” headerHeight=”20”>

</ResizableSmtPanel>

<mx:HBox width=”100%” horizontalAlign=”right” paddingRight=”2”>

<mx:Label text=”-” fontSize=”16” width=”20” height=”17” fontWeight=”bold”

id=”minimizeActions” click=”resizePanel()” />

</mx:HBox>

<mx:Script>

<![CDATA[

function resizePanel():void{

if (thePanel.minimized){

minimizeActions.text=”-”;

thePanel.minimized=false;

thePanel.height=thePanel.maxHeight;

} else {

minimizeActions.text=”+”;

thePanel.minimized=true;

thePanel.height=thePanel.minHeight;

}

Learning Flex Through ApplicationsCHAPTER 4

Page 116: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

104 RIA WITH ADOBE FLEX AND JAVA

}

]]>

</mx:Script>

Listing 4.12. The “Smalltalk” version of MXML

Fifteen minutes later, the three students were in the classroom, and each got an “A” for this elevator job. And here’s the Flex version:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” layout=”absolute”>

<mx:Component className=”ResizablePanel”>

<mx:Panel>

<mx:Script>

[Bindable]

public var minimized:Boolean = false;

</mx:Script>

</mx:Panel>

</mx:Component>

<ResizablePanel title=”The Panel” id=”thePanel” minimized=”false” height=”{thePanel.

minimized?thePanel.minHeight:thePanel.maxHeight}” width=”99%”

minHeight=”20” maxHeight=”100” headerHeight=”20”/>

<mx:HBox width=”99%” horizontalAlign=”right” paddingRight=”2”>

<mx:Label text=”{thePanel.minimized?’+’:’-’}” fontSize=”16” width=”20”

height=”17” fontWeight=”bold”

id=”minimizeActions” click=”{thePanel.minimized=!thePanel.minimized}” />

</mx:HBox>

</mx:Application>

Listing 4.13 The instructor’s version of the solution

There’s another simple solution to this particular assignment and we’ll let the reader try to figure it out (hint: use states).

What’s the moral of this story? Learn ANOTHER language, no matter what your current background is. Initially you’ll try to bring your own culture to this new language, but eventually your horizons will broaden and you’ll become a better programmer.

Object-Oriented ActionScriptYou know the drill: a language is called object-oriented if it supports inheritance, encapsulation,

Learning Flex Through ApplicationsCHAPTER 4

Page 117: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 105

and polymorphism. The first two notions can be easily defined:

Inheritance lets you design a class by deriving it from an existing one. This feature allows you to reuse existing code without copying and pasting. AS3 provides the keyword extends for declaring inheritance.

package com.theriabook.oop{

public class Person {

var name:String;

}

}

package com.theriabook.oop{

public class Consultant extends Person{

var dailyRate:Number;

}

}

package com.theriabook.oop{

public class Employee extends Person{

var salary:Number;

}

}

Listing 4.14. The ancestor and two descendents

Encapsulation is an ability to hide and protect data. AS3 has access-level qualifiers such as public, private, protected, and internal to control the access class variables and methods. Besides Java-like public, private, protected, and package access levels, you can also create namespaces in AS3 that will give you another way to control access to properties and methods. This chapter includes some basic samples of namespaces, and you may want to read about the component manifest tiles in Chapter 11.

However, if Java enforces an object-oriented style of programming, this isn’t the case with AS3, be-cause it’s based on the scripting language standard. Object-oriented purists may not like the next code snippet, but this is how a HelloWorld program can look in AS3:

trace(“Hello, world”);

That’s it. No class declaration is required for such a simple program, and the debug function trace() can live its own class-independent life, as opposed to Java’s println() doubly wrapped in the Sys-tem and PrintStream classes. You can write your own functions, attach them to dynamic objects, and pass them as parameters to other functions. AS3 supports a regular inheritance chain as well as so-called prototype inheritance where you can add new properties to the class definitions and

Learning Flex Through ApplicationsCHAPTER 4

Page 118: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

106 RIA WITH ADOBE FLEX AND JAVA

they become available to all instances of the class. Moreover, you can disable the validation of the properties and methods during compilation by turning off the “strict” mode. In Java, behind every object instance there’s an entity of the type Class. This is not an object itself, but it’s put in memory by class loaders.

Program Design with Interfaces and PolymorphismAs in Java, AS3 interfaces are special entities that define the behavior (methods) that can be imple-mented by classes using the keyword implement. After explaining what’s crucial to OOP interfaces, we’ll discuss how to write generic code without them.

To illustrate how you can design AS3 programs with interfaces, we’ll add some behavior to the classes from Listing 4.14. Let’s work on the following assignment.

A company has employees and consultants. Design classes to represent the people working in this company. The classes can have the following methods: changeAddress, giveDayOff, increasePay. Pro-motion can mean a day off and a salary raised a specified percentage. For employees, the increasePay method should raise the yearly salary and, for consultants, it should increase their hourly rate.

First, we’ll add all the common methods that are applicable to both employees and consultants to the Person class.

package com.theriabook.oop {

public class Person {

var name:String;

public function changeAddress(address: String): String {

return “New address is” + address;

}

private function giveDayOff(): String {

return “Class Person: Giving an extra a day off”;

}

}

}

Listing 4.15. The Ancestor class: Person

In the next step, we’ll add a new behavior that can be reused by multiple classes: the ability to in-crease the amount of a person’s paycheck. Let’s define a Payable interface:

package com.theriabook.oop

{

public interface Payable

Learning Flex Through ApplicationsCHAPTER 4

Page 119: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 107

{

function increasePay(percent:Number): String;

}

}

Listing 4.16. Interface Payable

More than one class can implement this interface:

package com.theriabook.oop

{

public class Employee extends Person implements Payable

{

public function increasePay(percent:Number):String {

// Employee-specific code goes here …

return “Class Employee:Increasing the salary by “+ percent + “%\n”;

}

}

}

Listing 4.17 The AS3 class Employee implementing the Payable interface

package com.theriabook.oop

{

public class Consultant extends Person implements Payable {

public function increasePay(percent:Number): String{

// Consultant-specific code goes here …

return “Class Consultant: Increasing the hourly rate by “ + percent + “%\n”;

}

}

}

Listing 4.18 The Consultant class implementing Payable

When the Consultant class declares that it implements a Payable interface, it “promises” to pro-vide implementation for all the methods declared in this interface – in our case there’s just one increasePay()method. Why is it so important that the class “keeps the promise” and implements all the interface’s methods? An interface is a description of some behavior(s). In our case the Payable behavior means the existence of a method with the signature boolean increasePay(int percent).

If any other class knows that Employee implements Payable, it can safely call any method declared in the Payable interface (see the interface example in the Promoter class in Listing 4.19).

Learning Flex Through ApplicationsCHAPTER 4

Page 120: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

108 RIA WITH ADOBE FLEX AND JAVA

In Java, besides method declarations, interfaces can contain final static variables, but AS3 doesn’t allow anything in the interfaces except method declarations.

Interfaces are another workaround for adjusting to the absence of multiple inheritance. A class can’t have two independent ancestors, but it can implement multiple interfaces, it just has to im-plement all the methods declared in all the interfaces. One way to implement multiple inheritance (that we often use but don’t recommend it) is to use an “include” statement with the complete implementation of all classes implementing interface:

public class Consultant extends Person implements Payable {

include “payableImplementation.as”

}

public class Employee extends Person implements Payable {

include “payableImplementation.as”

}

For example, a Consultant class can be defined as: class Consultant extends Person

implements Payable, Sueable {…}

But if a program such as Promoter.mxml (see Listing 4.19) is interested only in Payable functions, it can cast the object only to those interfaces it intends to use. For example: var emp:Employee = new Employee();

var con:Consultant = new Consultant();

var person1:Payable = emp as Payable;

var person2:Payable = con as Payable;

Now we’ll write an MXML program Promoter that will use the Employee and Consultant classes defined in Listings 4.17 and 4.18. Click on the button and it’ll create an array with a mix of employ-ees and consultants. Iterate through this array and cast it to the Payable interface, then call the increasePay()method on each object in this collection.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”>

<mx:Label y=”10” text=”Inheritance, Interfaces and Polymorphysm” width=”398”

height=”35” fontWeight=”bold” horizontalCenter=”-16” fontSize=”16”/>

<mx:Button x=”93” y=”66” label=”Increase Pay” width=”172” fontSize=”16”

click=”startPromoter()” id=”starter”/>

<mx:TextArea x=”26” y=”114” width=”312” height=”133” id=”output” wordWrap=”true”

editable=”false” borderStyle=”inset”/>

<mx:Script>

Learning Flex Through ApplicationsCHAPTER 4

Page 121: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 109

<![CDATA[

import com.theriabook.oop.*;

function startPromoter():void{

output.text=”Starting global promotions...\n”;

var workers:Array = new Array();

workers.push(new Employee());

workers.push(new Consultant());

workers.push(new Employee());

workers.push(new Employee());

for(var i: int = 0; i < workers.length; i++) {

// Raise the compensation of every worker using Payable

// interface

var p: Payable = workers[i] as Payable;

output.text+= p.increasePay(5);

//p.giveDayOff(); would not work. Payable does not know

// about this function

}

output.text+=”Finished global promotions...”;

}

]]>

</mx:Script>

</mx:Application>

Listing 4.19 Promoter.mxml

The output of this program will look like:

Figure 4.5 The output of Promoter.mxml

Learning Flex Through ApplicationsCHAPTER 4

Page 122: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

110 RIA WITH ADOBE FLEX AND JAVA

The line p.increasePay(5) in the listing above may look a little confusing. How can we call a con-crete increasePay method on a variable of an interface type? Actually we call a method on a con-crete instance of the Employee or a Consultant object, but by casting this instance to the Payable type we’re just letting the AVM know that we’re only interested in methods that were declared in this particular interface. Polymorphism – When you look at our Promoter from Listing 4.19, it looks like it calls the same increasePay()method on different types of objects, and it generates different outputs for each type. This is an example of polymorphic behavior.

In the real world, array workers would be populated from some external data source. For example, a program could get a person’s work status from the database and instantiate an appropriate con-crete class. The loop in Promoter.mxml will remain the same even if we add some other types of workers inherited from the Person class! For example, to add a new category of worker – a foreign contractor, we’ll have to create a ForeignContractor class that implements the increasePays method and might be derived from the Person class. Our Promoter will keep casting all these objects to the Payable type during runtime and call the increasePay method of the current object from the array.

Polymorphism allows you to avoid using switch or if statements with the checking type operator is. Below is a bad (non-polymorphic) alternative to our loop from Promoter.mxml that checks the type of the object and calls the type-specific methods increaseSalary() and increaseRate() (assum-ing that these methods were defined):

for(var i: int = 0; i < workers.length; i++) {

var p: Person = workers[i] as Person;

if (p is Employee){

increaseSalary(5);

} else if (p is Consultant) {

increaseRate(5);

}

}

Listing 4.20 A bad practice example

You’d have to modify the code above each time you add a new worker type.

Polymorphism Without InterfacesIf this was a Java book, we could have patted ourselves on the back for providing a nice example of polymorphism. Let’s think of a more generic approach – do we even need to use interfaces to en-sure that a particular object instance has a required function like increasePay? Of course not. Java has a powerful introspection and reflection mechanism that analyzes which methods exist in the class in question. It’s important to remember though that in Java, object instances have only those methods that were defined in their classes (blueprints). This is not the case with AS3.

Learning Flex Through ApplicationsCHAPTER 4

Page 123: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 111

There is another urban myth that reflection is slow, and you should use it only if you have to. But this consideration isn’t valid in programs that run on the client’s PCs, because we don’t have to worry about hundreds of threads competing for a slice of time on the same server’s CPU(s). Using reflection on the client is fine. Even on the server proper the combination of reflection with caching allows you to avoid any performance penalties.

AS3 provides very short and elegant syntax for introspection, and we’d like to spend some time il-lustrating polymorphism without the typecasting and strict Java-style coding.

Let’s revisit our sample application. Workers get pay and benefits and vacations; consultants are paid hourly wages. But retirees may have some form of receiving pensions, the board of directors might get paid but no benefits – are they workers? No, they’re not and their objects may not neces-sarily implement the Payable interface, which means that the typecasting from Listing 4.19 would cause a runtime exception.

How about raising the compensation of every Person even if it doesn’t implement Payable? If one of these objects sneaks into the workers array , simple casting to Payable as shown below will throw an exception:

Payable p = Payable(workers[i]);

Let’s rewrite the loop from Listing 4.19 as follows:

for(var i:uint = 0; i < workers.length; i++) {

var p:* = workers[i][“increasePay”];

output.text+=p==undefined?”no luck”:p(5);

}

This short loop deserves an explanation. First, we’ve declared a variable p of type *. This declaration means that p can be any type. Using an asterisk a bit more open than var p:Object; allows the vari-able p to have a special value of an undefined type as used in the above code sample.

Let’s dissect the following line:

var p:* = worker[i][“increasePay”];

It means “Get a reference to the increasePay()function from the array element workers[i].” You may ask, “Why use the brackets around increasePay instead of the dot notation?” The reason is that dot notation would ask the compiler to find and validate this function, while the brackets tell the com-piler not to worry about it, the program will take care of this little something inside the brackets during runtime.

Basically, the single line above performs the introspection and gets a pointer to the increasePay function for future execution in the line:

Learning Flex Through ApplicationsCHAPTER 4

Page 124: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

112 RIA WITH ADOBE FLEX AND JAVA

output.text+=p ==undefined?”no luck”:p(5);

If this particular element of the workers array doesn’t have increasePay defined (its class must be declared dynamic), add “no luck” to the text field. Otherwise execute this object’s version of in-creasePay, passing the number five as its argument. The line above is still a potential problem if the class doesn’t have the increasePay function, but has a property with the same name. The better version looks like this:

output.text+=!(p is Function)?”no luck”:p(6);

Let’s emphasize that again: this increasePay method doesn’t have be defined in any interface.

Java programmers would call this wild anarchy. Of course, adhering to strict rules and contracts in Java leads to more predictable code and less surprises at runtime. But modern Java moves toward dynamic scripting, adding implicit typecasting, runtime exceptions, etc. Overuse of interfaces, pri-vate, protected, and other “nice clean object-oriented techniques” doesn’t promote creative think-ing in software developers. We hope all code samples in this chapter break the OOP spell that Java developers live under.

Namespaces in ActionScriptNamespaces in AS3 as in MXML are used to limit the scope (visibility) of methods, properties, or constants. They’re also used to avoid naming conflicts in cases where you create your own custom components that may have the same names as the Flex Framework or other vendor’s counter-parts.

You can think of access control keywords – public, private, protected, and internal – as built-in namespaces. If a method has been declared as

protected calculateTax(){}

you can say that the calculateTax()method has a protected namespace. But AS3 lets you define your own namespaces to use instead of these standard language qualifiers.

To introduce your own namespace, you need to take the following steps:

• Declare a namespace• Apply the namespace• Reference the namespace

Let’s write a simple program for an accountant who calculates taxes, but customers who belong to the Mafia would pay only half the amount. To do this, we’ll start by declaring two namespaces: regular and riabook.

Learning Flex Through ApplicationsCHAPTER 4

Page 125: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 113

package com.theriabook.namespaces {

public namespace mafia=”http://www.theriabook.com/namespaces”;

}

Listing 4.21 riabook.as

Please note that using a URI in the namespace declaration is optional. The listing below doesn’t use any explicit URI, but the compiler will generate one.

package com.theriabook.namespaces {

public namespace regular;

}

Listing 4.22 regular.as

To apply the namespaces, we’ll define a Tax class with two calcTax()methods that will differ in their namespace access attributes and the amount of tax “calculated”:

package com.theriabook.tax{

import com.theriabook.namespaces.*;

public class Tax

{

regular static function calcTax():Number{

return 3500;

}

riabook static function calcTax():Number{

return 1750;

}

}

}

Listing 4.23 The AS3 class Tax

package com.theriabook.test

{

import com.theriabook.namespaces.*;

import com.theriabook.tax.Tax;

import mx.controls.Alert;

use namespace regular;

// use namespace mafia;

public class TestTax

{

public static function myTax():void {

Learning Flex Through ApplicationsCHAPTER 4

Page 126: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

114 RIA WITH ADOBE FLEX AND JAVA

var tax:Number;

tax=Tax.calcTax();

Alert.show(“Your tax is “+ tax,”Calculation complete”);

}

}

}

Listing 4.24 TestTax.as

Since we apply the namespace for the regular customer, s/he will have to pay a tax of $3,500. The MXML code that uses TestTax is shown below:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”absolute” creationComplete=”initApp();”>

<mx:Script>

<![CDATA[

import com.theriabook.test.TestTax;

public function initApp():void {

TestTax.myTax();

}

]]>

</mx:Script>

</mx:Application>

Listing 4.25 TestTax.mxml

The output of this program looks like Figure 4.6. Switch to another namespace by changing the use statement to look like

use namespace riabook;

and the amount of the tax to be paid will be substantially lower. Besides the directive use that af-fects the entire block of code, AS3 permits finer-grained notation to refer to a specific namespace with a name qualifier (a double colon). In our example, this may look like:

tax = Tax.riabook::calcTax();

Learning Flex Through ApplicationsCHAPTER 4

Page 127: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 115

Figure 4.6 The output of the TextTax.mxml for regular customers

Using namespaces provides an additional means of visibility control. The methods, class proper-ties of the constants, can be physically located in different packages, but marked with the same namespace qualifier, and a one-line namespace change can engage a completely different set of methods/properties across the entire application.

Using Flex with JavaServer PagesIn Chapter 5, we’ll use Flex Data Services to connect a Flex client with plain old Java objects (POJO) on the server using the object <mx:RemoteObject>. FDS is great software, but you may already have some Web applications written in another technology and just want to put a pretty Flash Play-er face on your existing Java Web applications that use JavaServer Pages (JSP). So the next couple of pages will show you how to “teach” Flash Player to communicate with a JSP without having to use FDS.

Retrieving Data from JSPWe’ll be using JSP here, but you can replace JSP with any technology you’re comfortable with: serv-lets, Active Server Pages, Python, PHP, et al. Whatever can spit out the data to a Web browser should work the same way.

We’ll show you a really simple application written in Flex 2 that talks to a JSP that generates XML with the information about employees:

<people>

<person>

<name>Alex Olson</name>

<age>22</age><skills>java, HTML, SQL</skills>

Learning Flex Through ApplicationsCHAPTER 4

Page 128: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

116 RIA WITH ADOBE FLEX AND JAVA

</person>

...

</people>

Listing 4.26 A fragment of the XML employees data

Let’s just hardcode this XML (we’ve got three persons) into a JSP that consists of one out.println() statement, where the XML goes between the double quotes:

<%out.println(“...”); %>

The complete JSP looks like this (just put your XML in one line so you won’t be bothered with string concatenations):

<%

out.println(“<?xml version=\”1.0\” encoding=\”UTF-8\”?><people><person><name>Alex Ol-

son</name><age>22</age><skills>java, HTML, SQL</skills></person><person><name>Brandon

Smith</name><age>21</age><skills>PowerScript, JavaScript, ActionScript</skills></person>

<person><name>Jeremy Plant</name><age>20</age><skills>SQL, C++, Java</skills></person></

people>”);

%>

Listing 4.27 employees.jsp

Deploy this JSP under some servlet container. In the popular Apache Tomcat this means to save it as employees.jsp under the webapp\test directory. As a sanity check we make sure that we’ve deployed this JSP correctly: entering http://localhost:8080/test/employees.jsp in the Web browser has to return the employee data. Now open Flex Builder and create the application:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

applicationComplete=”employees.send()”>

<mx:HTTPService id=”employees” useProxy=”false” method=”POST”

url=”http://localhost:8080/test/employees.jsp” />

<mx:DataGrid dataProvider=”{employees.lastResult.people.person}” width=”60%”>

<mx:columns>

<mx:DataGridColumn dataField=”name” headerText=”Name”/>

<mx:DataGridColumn dataField=”age” headerText=”Age”/>

<mx:DataGridColumn dataField=”skills” headerText=”Skills”/>

</mx:columns>

</mx:DataGrid>

</mx:Application>

Learning Flex Through ApplicationsCHAPTER 4

Page 129: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 117

Listing 4.28 DataGrid_E4X_JSP.mxml

This code uses the <mx:HTTPService> component that lets you connect to a specified URL either directly or through a proxy. The HttpService object is designed to communicate with any URI that understands HTTP requests and responses. In the code above we’ve just specified the URL for the JSP from Listing 4.24. The data provider of our data grid uses binding (see the curly braces) and E4X syntax to parse the XML and populate this table with the elements located under the <person> XML tag that’s coming from our employees.jsp. In the next section we’ll explain Flex data binding in more detail.

On the applicationComplete event, the code employees.send() makes an HTTP request to the URL specified in the HTTPService, and our JSP readily returns the XML that’s bound to the data grid.

Compile and run this program, and it’ll show you the following:

Figure 4.7 The output of DataGrid_E4X_JSP.mxml

Keep in mind that such a direct connection from HTTPService to a JSP is only permitted if your JSP and Flex application are coming from the same domain, or if the Web server you’re connecting to has the crossdomain.xml file specifying a cross-domain connection policy with the appropriate permission for yours or all domains. You can read more about configuring crossdomain.xml in the product manual under “Building and Deploying Flex 2 Applications.”

Learning Flex Through ApplicationsCHAPTER 4

Page 130: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

118 RIA WITH ADOBE FLEX AND JAVA

Sending Data from Flex to JSPIn the next version of our Flex-JSP application we’ll show you how to post data from a Flex form to JSP. We’ll put a simple form under the data grid above to enter the data about the new employee as in Figure 4.8. Pressing the Add Employee button will submit the entered data to the JSP, which will attach them to existing employees and return back so the data grid can be repopulated to include the newly inserted employee.

To design the form, we’ll be using the <mx:Form> Flex objects container, which differs from the HTML tag <form>. The latter is an invisible container that holds some data, while <mx:Form> is used to arrange the input controls on the screen with their labels. We’ll also use <mx:Model> to store the data bound to our <mx:Form>. Let’s also make the employee’s name a required field and add a so-called validator to prevent the user from submitting the form without entering the name. It will look like:

<mx:StringValidator id=”empNameVld” source=”{empName}” property=”text” />

<mx:Model id=”employeeModel”>

<root>

<empName>{empName.text}</empName>

<age>{age.text}</age>

<skills>{skills.text}</skills>

</root>

</mx:Model>

<mx:Form width=”100%” height=”100%”>

<mx:FormItem label=”Enter name:” required=”true”>

<mx:TextInput id=”empName” />

</mx:FormItem>

<mx:FormItem label=”Enter age:”>

<mx:TextInput id=”age” />

</mx:FormItem>

<mx:FormItem label=”Enter skills”>

<mx:TextInput id=”skills” />

</mx:FormItem>

<mx:Button label=”Add Employee” click=”submitForm()”/>

</mx:Form>

Listing 4.29 The employee entry form and its model

The required=true attribute displays a red asterisk by the required field but doesn’t do any valida-tion. The <mx:StringValidator> displays the prompt “This field is required” and makes the border of the required field red if you move the cursor out of the name field while it’s empty, and shows a prompt when you return to this field again as in Figure 4.8. But we’d like to turn this default valida-tion off by adding the triggerEvent property with a blank value:

Learning Flex Through ApplicationsCHAPTER 4

Page 131: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 119

<mx:StringValidator id=”empNameValidator” source=”{empName}”

property=”text” triggerEvent=””/>

We’ll also add our own AS3 validateEmpName()function. Now the click event of the Add Employee button will call validateName(), which in turn will either call the submitForm()function if the name was entered or display a message box “Employee name can not be blank”.

Validators are outside the scope of this chapter, and so we’ll just mention that Flex comes with a number of pre-defined classes that derive from the base class Validator. They ensure that the input data meet certain rules. The names of these classes are self-explanatory: DateValidator, EmailVali-dator, PhoneNumberValidater, NumberValidator, RegExValidator, CreditCardValidator, ZipCode-Validator, and StringValidator. These validators work on the client side, and round-trips to the serv-er aren’t required. A program initiates the validation process either as a response to an event or by a direct call to the method validate() of the appropriate validator instance as in Listing 4.30

The final version of the Flex portion of our application is shown below.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

applicationComplete=”employees.send()”>

<mx:HTTPService id=”employees” useProxy=”false” method=”POST”

url=”http://localhost:8080/test/employees.jsp” result=”onResult(event)” />

<mx:DataGrid dataProvider=”{employees.lastResult.people.person}” width=”100%”>

<mx:columns>

<mx:DataGridColumn dataField=”name” headerText=”Name” />

<mx:DataGridColumn dataField=”age” headerText=”Age”/>

<mx:DataGridColumn dataField=”skills” headerText=”Skills”/>

</mx:columns>

</mx:DataGrid>

<mx:StringValidator id=”empNameValidator” source=”{empName}”

property=”text” triggerEvent=””/>

<mx:Model id=”employeeModel”>

<root>

<empName>{empName.text}</empName>

<age>{age.text}</age>

<skills>{skills.text}</skills>

</root>

</mx:Model>

<mx:Form width=”100%” height=”100%”>

<mx:FormItem label=”Enter name:” required=”true”>

Learning Flex Through ApplicationsCHAPTER 4

Page 132: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

120 RIA WITH ADOBE FLEX AND JAVA

<mx:TextInput id=”empName” />

</mx:FormItem>

<mx:FormItem label=”Enter age:”>

<mx:TextInput id=”age” />

</mx:FormItem>

<mx:FormItem label=”Enter skills”>

<mx:TextInput id=”skills” />

</mx:FormItem>

<!--mx:Button label=”Add Employee” click=”submitForm()”/-->

<mx:Button label=”Add Employee” click=”validateEmpName()”/>

</mx:Form>

<mx:Script>

<![CDATA[

import mx.events.ValidationResultEvent;

import mx.controls.Alert;

private function validateEmpName():void{

if (empNameValidator.validate().type == ValidationResultEvent.VALID){

submitForm();

} else{

Alert.show(“Employee name can not be blank”);

}

}

private function submitForm():void {

employees.cancel();

employees.send(employeeModel);

}

private function onResult(event:Event):void{

trace(‘Got the result’); // works only in the debug mode

return;

}

]]>

</mx:Script>

</mx:Application>

Listing 4.30 DataGrid_EX4-JSP2.mxml

When the user hits the Add Employee button on the form, our HTTPService will submit the employ-eeModel to a modified employees.jsp, which will now get the parameters from the HTTPRequest ob-ject, prepare the new XML element newNode from the received data, concatenate it to the original three employees, and return it back to the client, which will display all the employees in the datagrid:

Learning Flex Through ApplicationsCHAPTER 4

Page 133: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 121

<%

String employees=”<?xml version=\”1.0\” encoding=\”UTF-8\”?><people><person><name>Alex

Olson</name><age>22</age><skills>java, HTML, SQL</skills></person><person><name>Brandon

Smith</name><age>21</age><skills>PowerScript, JavaScript, ActionScript</skills></person>

<person><name>Jeremy Plant</name><age>20</age><skills>SQL, C++, Java</skills></person>”;

// Get the parameters entered in the GUI form

String name=request.getParameter(“empName”);

String age=request.getParameter(“age”);

String skills=request.getParameter(“skills”);

String newEmployee=”<person><name>” + name+ “</name><age>” + age + “</age><skills>”

+ skills +”</skills></person>”;

if (name == null){

newEmployee=””;

}

// the xml goes back to the Web browser via HTTPResponse

out.println(employees + newEmployee + “</people>”);

%>

Listing 4.31 The new version of employee.jsp

Figure 4.8 The employee form and default validator’s message

You’ll see more examples of the use of HTTPService object in Chapter 5, where we’ll retrieve finan-cial news from Yahoo!, and in Chapter 13, where we’ll download photos from the popular Flickr.

Learning Flex Through ApplicationsCHAPTER 4

Page 134: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

122 RIA WITH ADOBE FLEX AND JAVA

com. In Chapter 5 we’ll also use HTTPService through a proxy configured with FDS.

Note: There are other ways to pass the data from Flex to a server-side Web application. For example, you can create an instance of the URLVariables object, create the data to be passed as its properties, attach URLVariables to URLRequest.data, and call navigateToURL().

E4X, Data Binding, and Regular ExpressionsParsing XML has never been fun. Java programmers use way too many different parsers and APIs. Java 6 includes Java Architecture for XML Binding (JAXB 2.0) and the implementation of the XML Data Binding Specification (JSR 31) that maps JavaBeans and XML data. To be more accurate, JAXB 2.0 offers two-directional mapping between JavaBeans and XML Schema (see the javax.xml.bind package). Java 6 comes with a new tool called xjc that takes an XML Schema as an input and gener-ates the required JavaBeans as an output.

ActionScript 3.0 supports E4X, which is an ECMA standard for working with XML (see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-357.pdf). It’s very powerful and yet simple to use. You can forget about these SAX and DOM parsers. E4X is a step towards making XML a programming language.

For example, an MXML program can read the XML file people.xml (or any other XML source) shown in Listing 4.23 into a variable with only one line (without worrying about error processing):

<mx:XML id=”myXmlFile” source=”people.xml”/>

You’ll need another line of code to populate the data grid using Flex data binding (remember, tying the data from a source to a destination). In our case myXmlFile is the source that populates the data grid aka destination:

<mx:DataGrid dataProvider=”{myXmlFile.person}”>

This line means that each element <person> will populate one row in the data grid. Let’s make the XML from Listing 4.23 a bit more complex: we’ll introduce nesting in the name element. Now it consists of separate <first> and <last> elements.

<?xml version=”1.0” encoding=”UTF-8”?>

<people>

<person>

<name>

<first>Yakov</first>

<last>Fain</last>

</name>

<age>22</age>

<skills>java, HTML, SQL</skills>

Learning Flex Through ApplicationsCHAPTER 4

Page 135: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 123

</person>

<person>

<name>

<first>Victor</first>

<last>Rasputnis</last>

</name>

<age>21</age>

<skills>PowerScript, JavaScript, ActionScript</skills>

</person>

<person>

<name>

<first>Anatole</first>

<last>Tartakovsky</last>

</name>

<age>20</age>

<skills>SQL, C++, Java</skills>

</person>

</people>

Listing 4.32 The XML file FarataSystems_skills.xml

Our goal is to produce a window that looks like this:

Figure 4.9 A data grid populated from people.xml

Please note that we also want to concatenate the values from the <first> and <last> XML elements for the data grid’s Name column. A small program in Listing 4.30 does exactly this. The fullName method concatenates the first and last names, and since we specified the labelFunction property in the name column, the data rendering will be controlled by the fullName() function. We’ll return to labelFunction in Chapter 11.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”>

Learning Flex Through ApplicationsCHAPTER 4

Page 136: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

124 RIA WITH ADOBE FLEX AND JAVA

<mx:XML id=”myXmlFile” source=”FarataSystems_Skills.xml”/>

<mx:Label text=”Java Developers” fontSize=”18”/>

<mx:DataGrid dataProvider=”{myXmlFile.person}” width=”500”>

<mx:columns>

<mx:DataGridColumn dataField=”name” headerText=”Name” labelFunction=”fullName”/>

<mx:DataGridColumn dataField=”age” headerText=”Age”/>

<mx:DataGridColumn dataField=”skills” headerText=”Skills”/>

</mx:columns>

</mx:DataGrid>

<mx:Script>

<![CDATA[

private function fullName(item:Object, column:DataGridColumn):String {

return item.name.first + “ “ + item.name.last;

}

]]>

</mx:Script>

</mx:Application>

Listing 4.33 Populating a data grid from an XML file

The next step is to add regular expressions to filter the data while populating the data grid. There’s a nice example of predicate filtering with E4X and RegExp in Darron Schall’s blog at http://www.darronschall.com/weblog/archives/000214.cfm.

Let’s imagine that a recruiter wants to do a quick search in our XML file to identify people with Java skills. A small one-line change will do this trick, or at least will be a step in the right direction.

The RegExp class lets you create an instance of the object per the specified pattern, and then find the substring(s) with this pattern and perform the manipulations with the found substrings, if any.

In AS3, you can create an instance of the RegExp by using a familiar syntax with a constructor. For example:

var javaPattern:RegExp = new RegExp(“Java”, “i”);

This is a pattern for finding occurrences of “Java,” ignoring the letter case.

Here’s another way of creating this instance: var javaPattern:RegExp = /Java/i;

We’ll use the latter syntax by feeding the E4X output to this RegExp instance and the result will be used as a data provider for the data grid. Let’s modify the MXML code for the <mx:DataGrid> tag to

Learning Flex Through ApplicationsCHAPTER 4

Page 137: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 125

apply this regular expression to the XML element called skills:

<mx:DataGrid dataProvider=”{myXmlFile.person.(/Java/.test( skills ))}” >

In the line above, /Java/ creates an instance of the RegEx object and the test(skills) method will ensure that only those XML elements that contain Java are included in the myXmlFile. Now the resulting window will look as follows:

Figure 4.10 Displaying FarataSystems_skills.xml with Java skills

We still don’t like a couple of things here. First, this output didn’t include Yakov Fain because the word Java was written in lower case in his skills element. Adding the ignore case option “i” to our RegExp instance will help:

<mx:DataGrid dataProvider=”{myXmlFile.person.(/Java/i.test(skills))}” >

The output will again look like Figure 4.9. The next step is to filter out people who were included in this list just because of JavaScript, which has very little to do with Java. One of the ways to do this is by requesting that there should be a space or a comma in the regular expression after the word Java:

<mx:DataGrid

dataProvider=”{myXmlFile.person.(/Java? ?,/i.test(skills))}” >

Now we’ve lost both Victor and Anatole. Even though Anatole knows Java, there’s no space or com-ma after the word Java in his list of skills. Adding an OR (|) condition that means we’re also inter-ested in people with the word Java as the last word in the string will help.

<mx:DataGrid

dataProvider=”{myXmlFile.person.(/Java? ?, | Java$/i.test(skills))}”

Learning Flex Through ApplicationsCHAPTER 4

Page 138: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

126 RIA WITH ADOBE FLEX AND JAVA

Figure 4.11 Finding Java programmers

Today, E4X doesn’t support XML Schema, and all the XML elements are returned as text, but it’ll change in future versions of the ECMAScript for XML. Meanwhile, the implementation of E4X by any programming language makes it more appealing to developers who have to deal with XML.

Collections, Filters, and Master-DetailIn the last section, we were using the <mx:XML> object to store data retrieved from the people.xml file (see Listing 4.33). We also used this object as a data provider for the data grid control. But that example used static data, where the content of the XML file didn’t change during runtime. In real-world scenarios, data change frequently, and we have to display the latest values on the user (think of an auction or stock exchange displaying the latest prices).

Flex comes with a mx.collections package that contains collection classes that have convenient methods to sort and filter the objects from the underlying collection like XML or array, and they also fire events when the data in the collection change. This makes collection classes very convenient data providers for Flex visual controls – as soon as the data change, visual controls that are bound to this collection immediately reflect the change without any special programming required.

In this section we’ll build a small gas station application that will monitor daily operations like gasoline sales. While building this application, you’ll get familiar with the XMLListCollection class and learn how to filter the data in these collections.

The window output in this application will look like Figure 4.12. We’ll read the initial “gas station activities” data from the following XML:

<messages>

<message msgType=”sale”>

<transID>1234</transID>

<octane>87</octane>

<price>2.50</price>

<gallons>10.2</gallons>

<paidby>MC</paidby>

Learning Flex Through ApplicationsCHAPTER 4

Page 139: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 127

</message>

<message msgType=”sale”>

<transID>1035</transID>

<octane>89</octane>

<price>2.69</price>

<gallons>14.5</gallons>

<paidby>Cash</paidby>

</message>

<message msgType=”spill”>

<transID>2301</transID>

<octane>93</octane>

<price>2.99</price>

<paidby></paidby>

<gallons>17.3</gallons>

</message>

</messages>

Listing 4.34 GSActivities.xml

The final version of our application will include a timer with a random data generator that will add new messages to the window from Figure 4.12, emulating the data feed of messages like sale, purchase, and spill.

The first version of GasStation.mxml reads and parses the data from GSActivities.xml using this one-liner:

<mx:XML id=”activities” source=”GSactivity.xml” />

Behind the scenes, Flex creates an object with the reference variable activities used as a data pro-vider for the data grid as follows:

<mx:DataGrid id=”messageBook” dataProvider=”{activities.message}” width=”100%”

height=”100%”>

The dataProvider activities.message represents the XML <message> element from GSActivity.xml, which is displayed as a row in the data grid.

The AS3 paid() function is called for each datagrif row and calculates the amount by multiplying the number of gallons and the price per gallon. The <mx:CurrencyFormatter> ensures that the cal-culated “paid” column is displayed as a dollar amount.

The rest of the code below just displays other controls that we’ll use for filtering and illustrating master-detail relations later in this section.

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

Learning Flex Through ApplicationsCHAPTER 4

Page 140: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

128 RIA WITH ADOBE FLEX AND JAVA

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” backgroundColor=”#e0e0FF”>

<mx:XML id=”activities” source=”GSactivity.xml” />

<mx:Canvas x=”10” y=”10” width=”100%” height=”100%”>

<mx:HBox x=”10” y=”20” width=”100%” height=”30”>

<mx:CheckBox id=”cbx93” label=”93”/>

<mx:CheckBox id=”cbx89” label=”89”/>

<mx:CheckBox id=”cbx87” label=”87”/>

<mx:Label text=”Msg.Type” />

<mx:ComboBox id=”cbMsgTypes” width=”117”

dataProvider=”{messageType}”></mx:ComboBox>

</mx:HBox>

<mx:VBox x=”10” y=”64” height=”100%” width=”100%”>

<mx:Label text=”Activity” width=”100%” fontSize=”15”/>

<mx:DataGrid id=”messageBook” dataProvider=”{activities.message}” width=”100%”

height=”100%”>

<mx:columns>

<mx:DataGridColumn headerText=”Message Type” dataField=”@msgType”/>

<mx:DataGridColumn headerText=”Transaction ID” dataField=”transID”/>

<mx:DataGridColumn headerText=”Octane” dataField=”octane”/>

<mx:DataGridColumn headerText=”Price per gal.” dataField=”price”/>

<mx:DataGridColumn headerText=”Amount(gal.)” dataField=”gallons”/>

<mx:DataGridColumn headerText=”Paid” labelFunction=”paid”/>

</mx:columns>

</mx:DataGrid>

<mx:Label text=”Required actions” fontSize=”15” />

<mx:TextArea id=”txtAction” width=”100%”/>

</mx:VBox>

</mx:Canvas>

<!--Defining USD formatting -->

<mx:CurrencyFormatter id=”usdFormatter” precision=”2”

currencySymbol=”$” useThousandsSeparator=”false” alignSymbol=”left” />

<mx:Script>

<![CDATA[

//Data for the Message type combo

[Bindable]

private var messageType: Array = [“all”,”sale”, “spill”, “purchase”];

private function paid(item:Object, column:DataGridColumn):String {

// calculate total gain/loss

var total:Number=Number(item.gallons)* Number(item.price);

if (item.@msgType!=”sale”){

Learning Flex Through ApplicationsCHAPTER 4

Page 141: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 129

total*=-1;

}

return “”+usdFormatter.format(total); //USD formatting

}

]]>

</mx:Script>

</mx:Application>

Listing 4.35 GasStation1.mxml

Please note that the combobox cbMsgTypes is populated from a messageType array that is marked [Bindable] and will be used below for filtering the messages in the data grid. Also, since we didn’t define the Paid By data grid column in this version of the gas station application, the corresponding data from the data provider aren’t shown.

Figure 4.12 Running GasStation1

Learning Flex Through ApplicationsCHAPTER 4

Page 142: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

130 RIA WITH ADOBE FLEX AND JAVA

Adding XMLListCollectionFlex collection classes implement the Ilist and ICollectionView interfaces that let you add, remove, and update items in a collection. These interfaces also have methods for dispatching events when the data in the underlying collection change. This becomes handy when you use a collection as a data provider of one of the list-based controls – just add a new element to such collection and the data in these controls automatically reflect the change.

Using collections (see the mx.collections package) as data providers is well described at http://www.adobe.com/devnet/flex/quickstart/using_data_providers/. We’ll just show you one of the ways to deal with collections in our gas station application.

Basically we’ll add a middleman between the XML object and the data grid. Now the data grid’s provider will become an XMLListCollection (built on top of XML activities):

<mx:XML id=”activities” source=”GSactivity.xml” />

<mx:XMLListCollection id=”msgList” source=”{activities.message}” />

<mx:DataGrid id=”messageBook” dataProvider=”{msgList}”>

Just recompile and run the application again – it will display the same window as in Figure 4.11.

FilteringThe next step is to allow the user to filter the data by octane (the checkboxes) or message type (the combo box). We’ll add an init() function that will be called on the applicationComplete event, when all the objects are constructed to assign the filterMessages() filter function to the collection to do the filtering: msgList.filterFunction=filterMessages;

The actual filtering will happen when we call the refresh()function on the collection.

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

backgroundColor=”#e0e0FF” applicationComplete=”init()”>

// some code is omitted here

private function init():void {

// assign the filter function

msgList.filterFunction=filterMessages;

// perform filtering

msgList.refresh();

}

private function filterMessages(item:Object):Boolean{

// Check every checkbox and the combobox and

Learning Flex Through ApplicationsCHAPTER 4

Page 143: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 131

// populate the datagrid with rows that match

// selected criteria

if (item.octane==”87” && this.cbx87.selected)

return true;

if (item.octane==”89” && this.cbx89.selected)

return true;

if (item.octane==”93” && this.cbx93.selected)

return true;

return false;

}

If you need to remove the filter, just set the filterFunction property to null.

Run the application after making these changes and you’ll see an empty table on the screen. When the creation of the application was complete, Flash VM called the init method, which assigned the filter function to our XMLListCollection, and called refresh(), which applied this filter to each XML node of our collection. Since none of the checkboxes was selected, the filterMessages function cor-rectly returned false to each node leaving the data grid empty. To fix this, let’s make a slight change in the checkboxes so they’ll be checked off during creation.

<mx:CheckBox id=”cbx93” label=”93” selected=”true”/>

<mx:CheckBox id=”cbx89” label=”89” selected=”true”/>

<mx:CheckBox id=”cbx87” label=”87” selected=”true”/>

Now the program will show all the rows again. Try to uncheck the boxes – nothing happens because the application doesn’t know that it needs to reapply the filter function to the msgList again. This is an easy fix – let’s refresh the msgList on each click on the checkbox:

<mx:CheckBox id=”cbx93” label=”93” selected=”true” click=”msgList.refresh()”/>

<mx:CheckBox id=”cbx89” label=”89” selected=”true” click=”msgList.refresh()”/>

<mx:CheckBox id=”cbx87” label=”87” selected=”true” click=”msgList.refresh()”/>

The filtering by octane number works fine. Adding the code snippet below to the beginning of the filterMessages() function will engage the filtering by message type according to the combo box selection:

if (cbMsgTypes.selectedLabel !=”all” &&

item.@msgType!=cbMsgTypes.selectedLabel ){

return false;

}

Learning Flex Through ApplicationsCHAPTER 4

Page 144: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

132 RIA WITH ADOBE FLEX AND JAVA

Master-Detail RelationshipsWe took care of the basic functionality of the data grid control. Since the turnover rate at gas sta-tions is pretty high, let’s add some help to new employees by populating the Required Actions text area based on the selected message type. This is a typical master-detail relationships task, where the data grid with messages is “the master” and the text box shows the details.

We’ll start by creating an actions.xml file where we store the recommended actions for each mes-sage type.

<MessageTypes>

<message type=”sale”>

<description>Sale of gasoline products</description>

<actions>Sale is good news. No action required

</actions>

</message>

<message type=”purchase”>

<description>Purchase of gasoline products from suppliers</description>

<actions>If the gas station owner is not on premises, please call him at 212-

123-4567. Otherwise no actions is required

</actions>

</message>

<message type=”spill”>

<description>Spill of gasoline products on the ground</description>

<actions> Get a bucket of sand and cover the mess. Expect to receive smaller

pay check this week.

</actions>

</message>

</MessageTypes>

Listing 4.36 MessageTypes.xml

To read and parse this file into an XML object, we just have to write one line (thanks to E4X):

<mx:XML id=”msgTypes” source=”MessageTypes.xml” />

The next step is to specify that a selection of a different row in the data grid should call the function that finds and displays the appropriate message from MessageTypes.xml. And again, E4X makes this job a breeze:

private function getAction():void {

txtAction.text=

msgTypes.message.(@type==messageBook.selectedItem.@msgType).actions;

}

Learning Flex Through ApplicationsCHAPTER 4

Page 145: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 133

The expression

msgTypes.message.(@type==messageBook.selectedItem.@msgType )

means select the XML <message> element that has an attribute type that is the same as in the selected row in the @msgType column in the data grid. When this XML element is identified, we assign its <actions> value to the txtAction text area.

As we said earlier, changing the selected row in the data grid should initiate the getAction() func-tion call. Let’s modify the declaration of the data grid and add the change event processing:

<mx:DataGrid id=”messageBook” dataProvider=”{msgList}” width=”100%”

height=”100%” change=”getAction()”>

Compile and run this program, select a row in the data grid, and the action text box will be popu-lated:

Figure 4.13 Populating the required actions field

Learning Flex Through ApplicationsCHAPTER 4

Page 146: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

134 RIA WITH ADOBE FLEX AND JAVA

We’re almost there. Why almost? Because if the user starts filtering the data by octane or a message type, the action text field won’t be cleaned. To fix this, let’s create a refreshData() function that will not only refresh the XMLListCollection, but also clean the text field:

private function refreshData():void{

msgList.refresh();

txtAction.text=””;

}

Don’t forget to replace all calls to msgList.refresh() with refreshData().

Adding a Data Feed In the real world, all the messages should be pushed to our application by some kind of messaging program. Another possibility is to have the gas station front end poll the data at some specified interval by some kind of a server-side program that can be written in JSP, ASP, PHP, or whatever else can bake an HTTPResponse. In coming chapters of this book, you’ll learn about various ways to communicate with remote programs. But at this point, for simplicity’s sake, we’ll emulate a real-time data feed by using a random-number generator and a timer that will add items to our msgList collection at specified time intervals. Since the data collection will be constantly receiving new data, the output window should reflect this by adding new rows to the data grid.

If the speed of your data feed is crucial, don’t pass the data as XML, and consider using ArrayCol-lection for storing data instead of XMLListCollection.

Here’s the final code for the application:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

backgroundColor=”#e0e0FF” applicationComplete=”init()”>

<mx:XML id=”msgTypes” source=”MessageTypes.xml” />

<mx:XML id=”activities” source=”GSactivity.xml” />

<mx:XMLListCollection id=”msgList” source=”{activities.message}” />

<mx:Canvas x=”10” y=”10” width=”100%” height=”100%”>

<mx:HBox x=”10” y=”20” width=”100%” height=”30”>

<mx:CheckBox id=”cbx93” label=”93” selected=”true” click=”refreshData()”/>

<mx:CheckBox id=”cbx89” label=”89” selected=”true” click=”refreshData()”/>

<mx:CheckBox id=”cbx87” label=”87” selected=”true” click=”refreshData()”/>

<mx:Label text=”Msg.Type” />

<mx:ComboBox id=”cbMsgTypes” width=”117” dataProvider=”{messageType}”>

<mx:change>refreshData()</mx:change>

</mx:ComboBox>

</mx:HBox>

Learning Flex Through ApplicationsCHAPTER 4

Page 147: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 135

<mx:VBox x=”10” y=”64” height=”100%” width=”100%”>

<mx:Label text=”Activity” width=”100%” fontSize=”15”/>

<mx:DataGrid id=”messageBook” dataProvider=”{msgList}” width=”100%”

height=”100%” change=”getAction()”>

<mx:columns>

<mx:DataGridColumn headerText=”Message Type” dataField=”@msgType”/>

<mx:DataGridColumn headerText=”Transaction ID” dataField=”transID”/>

<mx:DataGridColumn headerText=”Octane” dataField=”octane”/>

<mx:DataGridColumn headerText=”Price per gal.” dataField=”price”/>

<mx:DataGridColumn headerText=”Amount(gal.)” dataField=”gallons”/>

<mx:DataGridColumn headerText=”Paid” labelFunction=”paid”/>

<mx:DataGridColumn headerText=”Paid by” dataField=”paidby”/>

</mx:columns>

</mx:DataGrid>

<mx:Label text=”Required actions” fontSize=”15” />

<mx:TextArea id=”txtAction” width=”100%”/>

</mx:VBox>

</mx:Canvas>

<!--Defining USD formatting -->

<mx:CurrencyFormatter id=”usdFormatter” precision=”2”

currencySymbol=”$” useThousandsSeparator=”false” alignSymbol=”left” />

<!-- Gallons Amount formating with 2 digits after dec.point -->

<mx:NumberFormatter id=”numberFormatter” precision=”2”/>

<mx:Script>

<![CDATA[

//Message type combo data

[Bindable]

private var messageType: Array = [“all”,”sale”, “spill”, “purchase”];

import mx.collections.*;

private var sortMessages:Sort;

[Bindable]

private var grandTotalSale:Number=0;

private function init():void {

Learning Flex Through ApplicationsCHAPTER 4

Page 148: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

136 RIA WITH ADOBE FLEX AND JAVA

// assign the filter function

msgList.filterFunction=filterMessages;

// perform filtering

refreshData();

// emulating message feed in specified intervals

var myTimer:Timer = new Timer(5000, 0); // every 5 sec

myTimer.addEventListener(“timer”, addMessage);

myTimer.start();

}

private function filterMessages(item:Object):Boolean{

// filter by message types

if (cbMsgTypes.selectedLabel !=”all” &&

item.@msgType!=cbMsgTypes.selectedLabel ){

return false;

}

// Check every checkbox and the combobox and

// populate the datagrid with rows that match

// selected criteria

if (item.octane==”87” && this.cbx87.selected)

return true;

if (item.octane==”89” && this.cbx89.selected)

return true;

if (item.octane==”93” && this.cbx93.selected)

return true;

return false;

}

private function paid(item:Object, column:DataGridColumn):String {

// calculate total gain/loss. Label function is not

// the best place for calculations as it’s being called

// on each change of the underlying collection

var total:Number=Number(item.gallons)* Number(item.price);

if (item.@msgType!=”sale”){

total*=-1;

}

return “”+usdFormatter.format(total); //USD formatting

}

private function getAction():void {

Learning Flex Through ApplicationsCHAPTER 4

Page 149: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 137

txtAction.text=msgTypes.message.(@type==messageBook.selectedItem.@msgType).

actions;

}

private function refreshData():void{

msgList.refresh();

txtAction.text=””;

}

private function addMessage(event:TimerEvent):void{

// create and add one message with randomly-generated

// values to the collection

var newNode:XML = new XML();

var transID:String=Math.round(Math.random()*5000).toString();

var octanes: Array = [“87”, “89”, “93” ];

var octaneIndex:Number=Math.round(Math.random()*2);

var octane:String=octanes[octaneIndex];

var prices: Array = [2.49, 2.69, 2.99 ];

var price:Number=prices[octaneIndex];

var msgTypes: Array = [“sale”, “purchase”, “spill”];

var msgType:String=msgTypes[Math.round(Math.random()*2)];

var payTypes: Array = [“MC”, “Visa”, “Cash” ];

var payType:String=msgType==”sale”?payTypes[Math.round(Math.random()*2)]:””;

var gals:String=(numberFormatter.format(Math.random()*50).toString());

newNode=<message msgType={msgType}>

<transID>{transID}</transID>

<octane>{octane}</octane>

<price>{price}</price>

<gallons>{gals}</gallons>

<paidby>{payType}</paidby>

</message>;

// adding new messages always on top

activities.insertChildBefore( activities.message[0], newNode);

}

]]>

</mx:Script>

</mx:Application>

Listing 4.37 GasStation3.mxml

Learning Flex Through ApplicationsCHAPTER 4

Page 150: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

138 RIA WITH ADOBE FLEX AND JAVA

We’ve chosen an XML data feed in this application just to introduce the reader to the ease of XML parsing with E4X. In this case, the better performing solution would be to move the data from XML to an AS3 data transfer object and use ArrayCollection instead of XMLListCollection. This AS3 ob-ject should define getters that provide data to all the data grid columns, including the calculated amount for the Paid column. Keeping calculations in the labelFunction paid() is not a good idea because the label function is called for each visible row when the new XML element is inserted into the underlying XML collection. Flash Player repaints each visible data grid row when each new gas transaction is inserted, which means that the paid amounts for each visible row will be recalculated.

While using XML and E4X may look very attractive, you shouldn’t forget that when you’re creating your own AS3 classes, there’s less data to push over the wire.

All of the code for our gas station application fits in three pages of this book. You can create simple prototype applications in Flex with a relatively small number of lines of code, but let’s not fool ourselves: making efficient real-world applications still requires of programming in good old Java.

EventsIn object-oriented languages, if object A needs to notify object B about some important event, it’s done using a so-called Observer design pattern; Java implements this pattern in the Observer in-terface and Observable class. An observable object may have something interesting going on, and other objects that want to know about this implement the Observer interface and register them-selves with the observable class.

AS3 implements this design pattern using an event model. Objects can trigger events to each other. System events are triggered by the AVM, while others are triggered as a result of user interactions with your application, such as a click on a button or a mouse move. Below are some situations when events can be dispatched (triggered):

• When Flash Player finishes creating a component, it dispatches a creationComplete event. The DisplayObject class is a subclass of the EventDispatcher class, which means that each of the display objects can register an event listener and process events as they occur.

• User-defined classes can dispatch events according to the business logic of the application. • The EnterFrame event is triggered by the AVM at the application’s frame rate. In movie-type

applications it’s being dispatched when the playhead is entering a new frame. Even though Flex applications don’t have multiple frames, this event is continuously dispatched. This makes it a good candidate to check if some important action has occurred, i.e., a Boolean flag indicating the status of some business transaction is set to true.

• The objects of your application can send custom events to each other.

In an MXML application you’d just specify the name of the event and its handler function (or in-line code) in the attribute of the component without worrying too much about the underlying

Learning Flex Through ApplicationsCHAPTER 4

Page 151: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 139

AS3 code. But all Flex events are subclasses of flash.events.Event, and in AS3 programs you should register an event listener to “listen” to this event and write a function to handle this event when it arrives. For example, the next code snippet specifies that Flash Player has to call a method onEn-teringFrame (written by you) on each EnterFrame event:

addEventListener(Event.ENTERFRAME, onEnteringFrame);

In Java, event listeners are objects, but in AS3 only functions or methods can listen to the events.

If you need to trigger an event from the ActionScript class, it has to be either a subclass of Event-Dispatcher or implement the IEventDispatcher interface. The latter is the more preferred way be-cause AS3 doesn’t support multiple inheritance, and your class may need to extend another class. The other reason to use a lighter interface is that you may not need all the functionality that was defined in the EventDispatcher class and implement the required interface method as you see fit. For example:

class MyClass extends HisClass implements IEventDispatcher{

}

Event FlowWhen the event listener calls an event handler function, it receives an event object as a parameter, which contains various attributes of the event, and the most important one is the event’s target. This terminology might be a little confusing to Java developers since they refer to the component that generates an event as an event source. But here we say that all events are generated by the Flash Player. They are initiated at the stage level and flow to the target component (capturing the stage), i.e., Button, Shape, etc.. After reaching the target, the event “bubbles” its way through to the parents. So, when you click on a button, we can say that a button is the event target. If a button is located on a panel, this event will flow from the stage to the panel and then to the button – and then all the way back.

Flash Player 9 implements an event model based on the World Wide Web Consortium’s (W3C) speci-fication entitled Document Object Model Events available at http://www.w3.org/TR/DOM-Level-3-Events/events.html. According to this document, the lifecycle of an event that deals with display objects consists of three phases: capture, target, and bubbling.

• Capture: During this phase, Flash Player makes a first pass to check every object from the root of the display list to the target component to see if any parent component might be interested in processing this event. By default, events are ignored by the parents of the target component at the capture phase.

• Target: At this phase, event object properties are set for the target and all registered event listeners for this target will get this event.

Learning Flex Through ApplicationsCHAPTER 4

Page 152: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

140 RIA WITH ADOBE FLEX AND JAVA

• Bubbling: Finally, the event flows back from the target component all the way up to the root to notify all interested parties identified during the capture phase. Not all events have a bub-bling phase and you should consult the AS3 language reference for the events you’re inter-ested in.

The three event phases described above don’t apply to the user-defined events because Flash Player 9 doesn’t know about parent-child relations between user-defined event objects. But AS3 develop-ers can create custom event dispatchers, if they want to arrange event processing in three phases.

Event Propagation or BubblingConsider the following sample MXML code with a button in a panel.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” >

<mx:Panel x=”17.5” y=”20” width=”209” height=”142” layout=”absolute”

click=”trace(‘click event in the Panel’)” title=”Just a Panel”>

<mx:Button label=”ClickMe” x=”60.5” y=”38”

click=”trace(‘click event in the Button’)”/>

</mx:Panel>

</mx:Application>

Listing 4.38 Events.mxml

Run this simple program in the debug mode to enable trace and it’ll show you the following out-put:

Figure 4.14 Output of the Events.mxml

Clicking on the button in the Flex Builder’s console will show the following:

Learning Flex Through ApplicationsCHAPTER 4

Page 153: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 141

click event in the Button

click event in the Panel

This illustrates events propagation or bubbling: the click event bubbles up from the target (button) to its parent (panel).

Now let’s create another version of this application, where the button and the panel as well as the event processing are coded in AS3. This way you’ll see and appreciate all the legwork that MXML did for us:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

creationComplete=”onCreationComplete();” >

<mx:Script>

<![CDATA[

import mx.controls.Button;

import mx.containers.Panel;

private var myButton:Button;

private var myPanel:Panel;

// An event handler for creationComplete event

private function onCreationComplete():void {

myPanel=new Panel();

myPanel.width=200;

myPanel.height=150;

myPanel.title=”Just a Panel”;

addChild(myPanel);

myButton = new Button();

myButton.label = “Click me”;

addChild (myButton);

}

]]>

</mx:Script>

</mx:Application>

Listing 4.39 Creating a button and a panel in ActionScript

Learning Flex Through ApplicationsCHAPTER 4

Page 154: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

142 RIA WITH ADOBE FLEX AND JAVA

If you run this code, the output will look like this:

Figure 4.15 A button and a panel without nesting

This is not exactly the result we were looking for and the reason is simple: both the panel and the button were added to the application’s display list independently. Let’s fix this by adding the button to the panel by replacing addChild(myButton) with myPanel.addChild(myButton).

Now the hierarchy of the nodes in the display list will be different, and the node representing the button will be created under the parent node of the panel as shown in Figure 4.16.

Figure 4.16 A button and a panel with nesting

This is much better, but still not exactly the same as Figure 4.14. Let’s try to set the coordinate of the

Learning Flex Through ApplicationsCHAPTER 4

Page 155: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 143

button to values from Listing 4.38:

myButton.x=60.5;

myButton.y=38;

It did not help because, by default, the panel container uses the vertical layout (see Chapter 3) and ignores the absolute coordinates. Let’s add one line to change it into an absolute layout:

myPanel.layout=”absolute”;

Now if you run the application it’ll look the same as in Figure 4.13.

We’ve written a lot of ActionScript code, and we haven’t even processed the click events yet! We still need to add event listeners and event handlers to the button and the panel:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

creationComplete=”onCreationComplete();” >

<mx:Script>

<![CDATA[

import mx.controls.Button;

import mx.containers.Panel;

import flash.events.MouseEvent;

private var myButton:Button;

private var myPanel:Panel;

// An event handler for creationComplete

private function onCreationComplete():void {

myPanel=new Panel();

myPanel.width=200;

myPanel.height=150;

myPanel.layout=”absolute”;

myPanel.title=”Just a Panel”;

addChild(myPanel);

myButton = new Button();

myButton.label = “Click me”;

myButton.x=60.5;

myButton.y=38;

myPanel.addChild (myButton);

Learning Flex Through ApplicationsCHAPTER 4

Page 156: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

144 RIA WITH ADOBE FLEX AND JAVA

// Adding the click event processing

myButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);

myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler);

}

// The button click handler (the target phase)

private function buttonClickHandler(event:MouseEvent) :void{

trace (“click event in the Button”);

}

// The panel handler to demo bubbling

private function panelClickHandler(event:MouseEvent) :void{

trace (“click event in the Panel”);

}

]]>

</mx:Script>

</mx:Application>

Listing 4.40 AS_Bubbling.mxml

Run this application in debug mode and the console screen will look the same as the MXML ver-sion of our application:

click event in the Button

click event in the Panel

The order of these messages is a clear indication that the target event was processed first and the panel responded in the bubbling phase.

Typically, during the capture stage event, listeners on the parent components aren’t called, but there’s a version of the addEventListener()method that can request calling the listeners during the capture phase. To turn on event handling during the capture phase, you should use the three-argu-ments version of the addEventListener() function in the panel:

myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler, true);

When the third argument equals true, it tells the Flash Player that we are registering this particular listener for the capture phase (there’s no way to do this in MXML). Now run the application through the debug mode again and you’ll see that the panel responds first, then the target button. There’s no event processing in the bubbling phase.

click event in the Panel

click event in the Button

Learning Flex Through ApplicationsCHAPTER 4

Page 157: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 145

If you’d like to process parent events during both the capture as well as bubbling phase, register two listeners for the panel – one with the three arguments and one with two. These listeners

myButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);

myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler, true);

myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler);

will produce the output proving that the panel has processed the button click event twice: during both the capture and bubbling phases.

click event in the Panel

click event in the Button

click event in the Panel

Let’s make a change in the panel event handler to show how you can prevent the event from being delivered to the target if something bad has occurred (from a business application perspective):

private function panelClickHandler(event:MouseEvent) :void{

var badNews: Boolean = true; // a flag to emulate a bad situation

if (event.eventPhase == EventPhase.CAPTURING_PHASE){

trace (“Capturing phase: click event in the Panel”);

if (badNews){

trace (“Capturing phase: Bad news. Will not propagate click to But

ton”);

event.stopPropagation();

}

}else {

trace (“click event in the Panel”);

}

}

The stopPropagation() method can be called at any phase of the event flow. The line

myPanel.addEventListener(MouseEvent.CLICK, panelClickHandler);

means “listen to the click event, and when it’ll occur, call the function panelClickHandler.”

Handling events was so much easier in MXML…

Why do we even need to know about these addEventListener() function calls? Well, first, there are some classes that don’t have equivalents in MXML, hence you don’t have a choice. Second, if your program is adding components dynamically, addListener() is your only choice since there’s no way to use MXML notation there. And third, you may prefer writing your components only in AS3.

Learning Flex Through ApplicationsCHAPTER 4

Page 158: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

146 RIA WITH ADOBE FLEX AND JAVA

Custom EventsWhile Flex components come with their pre-defined events, developers can create custom events specific to their applications. Event-driven programming is a very important design concept be-cause it allows an application to react to user interaction without imposing a pre-defined “flow” on the end user. It means that unlike back-end processes that tend to have a “single track of mind” design, front-end applications have to react to what often seems like an unrelated sequence of end-user actions. Fortunately, the event-driven model offers an excellent architecture for such interac-tion based on loosely coupled components consuming and throwing the events.

This simply means a properly designed component knows how to perform some functionality and no-tifies the outside world by broadcasting one or more custom events. We need to stress that such a com-ponent doesn’t send these events to any other component(s). It just broadcasts its “exciting news” to the event dispatcher. If any other component is interested in processing this event, it must register a lis-tener for this event. Unlike direct component-to-component calls via public interfaces, this approach lets you add processing components and set up priorities without affecting the working components.

Before explaining how to create an event-driven component, we’ll state how to use them. This is a typical scenario: MyApplication uses Component1 and Component2. The components don’t know about each other. Any event-handling component has to define this event inside.

For example, Component1 dispatches this custom event and sends out an instance of the event object, which may or may not carry some additional component-specific data. MyApplication handles this custom event and, if needed, communicates with Component2 with or without feed-ing it with data based on the results of the first component event.

We’ll create a new shopping cart application that will include a main file and two components: the first, a large green button, and the second, a red TextArea field. We’ll create two separate directories, “controls” and “cart,” respectively, where our components will live.

To create our first MXML component in Flex Builder, select the “controls” directory and click on the menu File | New MXML Component. In the pop-up screen we’ll enter LargeGreenButton as a component name and we’ll pick Button from a dropdown as a base class for our component.

Flex Builder will generate the following code:

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

<mx:Button xmlns:mx=”http://www.adobe.com/2006/mxml”>

</mx:Button>

Next, we’ll make this button large and green with rounded corners (just to give it a Web 2.0 look). This component will dispatch an event named greenClickEvent. But when? You’ve got it: when someone clicks on large and green.

Learning Flex Through ApplicationsCHAPTER 4

Page 159: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 147

Custom events in MXML are annotated within the metadata tag to be visible to MXML. In Listing 4.41 we declared a custom event of the generic flash.events.Event type in the metadata tag. Since the purpose of this component is to notify the sibling objects that someone has clicked the button, we’ll define the greenClickEventHandler()event handler to create and dispatch our custom event.

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

<mx:Button xmlns:mx=”http://www.adobe.com/2006/mxml”

width=”104” height=”28” cornerRadius=”10” fillColors=”[#00ff00, #00B000]”

label=”Add Item” fontSize=”12” click=”greenClickEventHandler()”>

<mx:Metadata>

[Event(name=”addItemEvent”, type=”flash.events.Event”)]

</mx:Metadata>

<mx:Script>

<![CDATA[

private function greenClickEventHandler():void{

trace(“Ouch! I got clicked! Let me tell this to the world.”);

dispatchEvent(new Event(“addItemEvent”, true));// bubble to parent

}

]]>

</mx:Script>

</mx:Button>

Listing 4.41 LargeGreenButton.mxml

Please note that the LargeGreenButton component has no idea what will process its addItemEvent. It’s none of its business – loose coupling in action!

In dynamic languages the following naming conventions are common practice: to add an “Event” suffix to each of the custom events you declare, and a “Handler” suffix to each of the event-handler functions.

Here’s the application that will use the LargeGreenButton component:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:ctrl=”controls.*” layout=”absolute”>

<ctrl:LargeGreenButton greenClickEvent=”greenButtonHandler(event)”/>

<mx:Script>

<![CDATA[

private function greenButtonHandler(event:Event):void{

Learning Flex Through ApplicationsCHAPTER 4

Page 160: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

148 RIA WITH ADOBE FLEX AND JAVA

trace(“Someone clicked on the Large Green Button!”);

}

]]>

</mx:Script>

</mx:Application>

Listing 4.42 EventApplication.mxml

We have defined an extra namespace “ctrl” here to make the content of the “controls” directory vis-ible to this application. Run this application in debug mode, and it’ll display the window in Figure 4.17. When you click on the green button it will output the following on the console:

Ouch! I got clicked! Let me tell this to the world. Someone clicked on the Large Green Button.

While adding attributes to <ctrl:LargeGreenButton>, please note that code hints work and Flex Builder properly displays the greenClickEvent in the list of available events under the new custom component button.

Figure 4.17 The output of GreenApplication.xmxl

Our next component will be called BlindShoppingCart. This time we’ll create a component in the “cart” directory based on the TextArea:

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

<mx:TextArea xmlns:mx=”http://www.adobe.com/2006/mxml”

backgroundColor=”#ff0000” creationComplete=”init()”>

Learning Flex Through ApplicationsCHAPTER 4

Page 161: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 149

<mx:Script>

<![CDATA[

private function init():void{

parent.addEventListener(“addItemEvent”,addItemToCartEventHandler);

}

private function addItemToCartEventHandler(event:Event){

this.text+=”Yes! Someone has put some item inside me, but I do not know what

it is. \n”;

}

]]>

</mx:Script>

</mx:TextArea>

Listing 4.43 BlindShoppingCart.mxml

Note that the BlindShoppingCart component doesn’t expose any public properties or methods to the outside world. It’s a black box. The only way for other components to add something to the cart is by dispatching the addItemEvent event. The next question is how to map this event to the function that will process it. When someone instantiates the BlindShoppingCart, Flash Player will dispatch the creationComplete event on the component and our code will call the init() private method that adds the addItemEvent event listener mapping to the addItemToCartEventHandler function. This function just appends the text “Yes! Someone has put…” to its red TextArea.

The RedAndGreenApplication application uses the LargeGreenButton and BlindShoppingCart components.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:ctrl=”controls.*” xmlns:cart=”cart.*”>

<ctrl:LargeGreenButton addItemEvent=”greenButtonHandler(event)”/>

<cart:BlindShoppingCart width=”350” height=”150” fontSize=”14”/>

<mx:Script>

<![CDATA[

private function greenButtonHandler(event:Event):void{

trace(“Someone clicked on the Large Green Button!”);

}

]]>

</mx:Script>

</mx:Application>

Listing 4.44 RedAndGreenApplication.mxml

Learning Flex Through ApplicationsCHAPTER 4

Page 162: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

150 RIA WITH ADOBE FLEX AND JAVA

Let’s Go Through the Sequence of EventsWhen the green button is clicked, the greenButtonHandler is called and it creates and dispatches the addItemEvent event back to itself. The event bubbles to the parent container(s), notifying all listening parties of the event. The BlindShoppingCart listens for such an event and responds by adding text. Run this application, click on the button, and the window should look like this:

Figure 4.18 The output of RedAndGreenApplication.mxml

Now one more time: the green button component shoots the event to the outside world without knowing anything about it. That is very different from the case when we would write “glue” code like cart.addEventListener(“click”, applicationResponseMethodDoingSomethingInsideTheCart).

Sending Data Using Custom Events To make our blind shopping cart more useful, we have to be able not only to fire a custom event, but have this event deliver a description of the item that was passed to shopping cart. To do this, we’ll have to create a custom event class with an attribute that will store application-specific data.

This class has to extend flash.events.Event; override its method clone to support event bubbling; and call the constructor of the super-class, passing the type of the event as a parameter. The AS3 class below defines a itemDescription property that will store the application-specific data.

package cart {

import flash.events.Event;

public class ItemAddedEvent extends Event {

var itemDescription:String; //an item to be added to the cart

public static const ITEMADDEDEVENT:String =”ItemAddedEvent”;

public function ItemAddedEvent(description:String )

{

super(ITEMADDEDEVENT,true, true); //bubble by default

Learning Flex Through ApplicationsCHAPTER 4

Page 163: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 151

itemDescription=description;

}

override public function clone():Event{

return new ItemAddedEvent(itemDescription); // bubbling support inside

}

}

}

Listing 4.45 The custom event ItemAddedEvent

The new version of the shopping cart component is called ShoppingCart and its event handler extracts the itemDescription from the received event and adds it to the text area.

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

<mx:TextArea xmlns:mx=”http://www.adobe.com/2006/mxml”

backgroundColor=”#ff0000” creationComplete=”init()”>

<mx:Script>

<![CDATA[

private function init():void{

parent.addEventListener(ItemAddedEvent.ITEMADDEDEVENT,addItemToCartEventHandler);

}

private function addItemToCartEventHandler(event:ItemAddedEvent){

text+=”Yes! Someone has put “ + event.itemDescription + “\n”;

}

]]>

</mx:Script>

</mx:TextArea>

Listing 4.46 ShoppingCart.mxml

There’s a design pattern called Inversion of Control or Dependency Injection, which means that an object doesn’t ask other objects for required values, but assumes that someone will provide the re-quired values from outside. This is also known as the Hollywood principle or ”Don’t call me, I’ll call you.” Our ShoppingCart does exactly this – it waits until some unknown object triggers an event it listens to that carries an item description. Our component knows what to do with it, i.e., display in the red text area, validate it against the inventory, send it over to the shipping department, and so on.Next, we’ll completely rework our LargeGreenButton class into a NewItem component to include a label and a text field to enter some item description and the same old green button:

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

Learning Flex Through ApplicationsCHAPTER 4

Page 164: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

152 RIA WITH ADOBE FLEX AND JAVA

<mx:HBox xmlns:mx=”http://www.adobe.com/2006/mxml” >

<mx:Metadata>

[Event(name=”addItemEvent”, type=”flash.events.Event”)]

</mx:Metadata>

<mx:Label text=”Item name:”/>

<mx:TextInput id=”enteredItem” width=”300”/>

<mx:Button

width=”104” height=”28” cornerRadius=”10” fillColors=”[#00ff00, #00B000]”

label=”Add Item” fontSize=”12” click=”greenClickEventHandler()”/>

<mx:Script>

<![CDATA[

import cart.ItemAddedEvent;

private function greenClickEventHandler():void{

trace(“Ouch! I got clicked! Let me tell this to the world.”);

dispatchEvent(new ItemAddedEvent(enteredItem.text));

}

]]>

</mx:Script>

</mx:HBox>

When we look at our new application with its new ShoppingCart and NewItem components, it’s almost indistinguishable from the original one. If we kept the old class names, we could have used the old application.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:ctrl=”controls.*” xmlns:cart=”cart.*”>

<ctrl:NewItem />

<cart:ShoppingCart width=”350” height=”150” fontSize=”14”/>

</mx:Application>

Listing 4.47 RedAndGreenApplication2.mxml

When the user enters the item description and clicks the green one, the application creates a new instance of the ItemAddedEvent, passing the entered item to its constructor, and the ShoppingCart properly displays the selected ”New Item to Add” on the red carpet (see Figure 4.19).

Learning Flex Through ApplicationsCHAPTER 4

Page 165: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 153

Figure 4.19 The output of RedAndGreenApplication.mxml

Making components loosely bound simplifies development and distribution but comes at a higher cost in testing and maintenance. Depending on the delivery timeline, size, and lifespan of your ap-plication, you’d have to make a choice between loosely coupled or strongly typed components.

One last note. The itemDescription in Listing 4.45 doesn’t have an access-level qualifier. It’s so-called package-level protection. The ShoppingCart can access itemDescription directly, but the classes outside the “cart” package can’t.

SummaryThis was a large chapter and it just covered the basic concepts of ActionScript programming. As you start building your Flex application, the coding described will become routine. Making a choice of which approach to take will not. We hope that such a high-level overview will help you to make an informed choice about the path to take.

Learning Flex Through ApplicationsCHAPTER 4

Endnotes1. To be exact the appropriate Java classloader on each side should be able to find the class in the parental

chain of the classloaders.

2. The function object becomes a candidate for garbage collection.

Page 166: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 155

A Complete Application with RPC Communications and JMS

5CHAPTER

Page 167: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

156 RIA WITH ADOBE FLEX AND JAVA

<Value>1</Value>

</security>

<security>

<Symbol>IBM</Symbol>

<Quantity>3000</Quantity>

<Price>82.15</Price>

<Value>1</Value>

</security>

</portfolio>

Listing 5.1 A fragment of a portfolio.xml

Figure 5.1 Stock portfolio screen - the show grid view

When the user clicks on a row with a particular stock (i.e., ADBE as in Figure 5.1), it will populate the lower data grid with the headlines related to the selected stock. The news should be coming from http://finance.yahoo.com/rss/headline. The column Link will contain the URL of the news,

Multi-Tier Application Development with FlexThis chapter describes the process of creating a complete Flex-Java distributed application. Up-grading Flex applications to Java Enterprise Edition applications is done with Flex Data Services. FDS provides transparent access to POJO, EJBs, and JMS and comes with adapters for frameworks like Spring and Hibernate. These powerful capabilities come free for a single-CPU server, otherwise see your local Adobe dealer. Flex can also invoke any SOAP Web Service or send an HTTP request to any URL via Web Services and HTTPService components.

In this chapter we will illustrate Flex controls, HTTPService, RemoteObject, and Consumer via two versions of a stock portfolio application. The first version will show communications between Flash and a plain old Java object (POJO) using Flex remoting. We’ll also explain how to use the HTTPService to read the RSS news feed. In the other version we’ll add the stock (aka security) price feed using Flex Messaging and the Java Messaging Service (JMS).

While explaining the FDS capabilities, we will also walk you through a typical design with Flex containers.

Designing a Stock Portfolio ApplicationOur goal is to create a Web application that will receive and display a feed containing security pric-es and the latest financial news as in Figures 5.1 and 5.2. This section contains a sort of functional specification of such an application.

We’ll populate the top portion of the screen with the stocks included in the user’s portfolio. For simplicity’s sake, we’ll store the user’s portfolio in the XML file as in Listing 5.1.

<portfolio>

<security>

<Symbol>MSFT</Symbol>

<Quantity>10000</Quantity>

<Price>33.38</Price>

A Complete Application with RPC CommunicationsCHAPTER 5

A Complete Application with RPC Communications and JMS

Page 168: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 157

<Value>1</Value>

</security>

<security>

<Symbol>IBM</Symbol>

<Quantity>3000</Quantity>

<Price>82.15</Price>

<Value>1</Value>

</security>

</portfolio>

Listing 5.1 A fragment of a portfolio.xml

Figure 5.1 Stock portfolio screen - the show grid view

When the user clicks on a row with a particular stock (i.e., ADBE as in Figure 5.1), it will populate the lower data grid with the headlines related to the selected stock. The news should be coming from http://finance.yahoo.com/rss/headline. The column Link will contain the URL of the news,

Multi-Tier Application Development with FlexThis chapter describes the process of creating a complete Flex-Java distributed application. Up-grading Flex applications to Java Enterprise Edition applications is done with Flex Data Services. FDS provides transparent access to POJO, EJBs, and JMS and comes with adapters for frameworks like Spring and Hibernate. These powerful capabilities come free for a single-CPU server, otherwise see your local Adobe dealer. Flex can also invoke any SOAP Web Service or send an HTTP request to any URL via Web Services and HTTPService components.

In this chapter we will illustrate Flex controls, HTTPService, RemoteObject, and Consumer via two versions of a stock portfolio application. The first version will show communications between Flash and a plain old Java object (POJO) using Flex remoting. We’ll also explain how to use the HTTPService to read the RSS news feed. In the other version we’ll add the stock (aka security) price feed using Flex Messaging and the Java Messaging Service (JMS).

While explaining the FDS capabilities, we will also walk you through a typical design with Flex containers.

Designing a Stock Portfolio ApplicationOur goal is to create a Web application that will receive and display a feed containing security pric-es and the latest financial news as in Figures 5.1 and 5.2. This section contains a sort of functional specification of such an application.

We’ll populate the top portion of the screen with the stocks included in the user’s portfolio. For simplicity’s sake, we’ll store the user’s portfolio in the XML file as in Listing 5.1.

<portfolio>

<security>

<Symbol>MSFT</Symbol>

<Quantity>10000</Quantity>

<Price>33.38</Price>

A Complete Application with RPC CommunicationsCHAPTER 5

A Complete Application with RPC Communications and JMS

Page 169: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

158 RIA WITH ADOBE FLEX AND JAVA

and when the user clicks on the link, a new browser window pops up displaying the selected news article.

The top of the screen contains the toggle buttons Show Grid/Show Chart. When the Show Grid op-tion is selected, the user will see the screen as in Figure 5.1, and when Show Chart is selected, the top data grid will be replaced with the pie chart (see Figure 5.2), preserving the same functionality (clicking on the pie slice repopulates the news headlines according to the selected security). The market data is refreshed on the screen every second or so.

Figure 5.2 Stock portfolio screen – the show chart view

The first version of the application will query the server POJO that generates random numbers. Later in this chapter, we’ll subscribe to JMS topic and consume a real-time data feed from an ex-ternal Java application.

In this application we’ll use the basic MXML and ActionScript from the first chapters of this book. We assume that the reader has a basic knowledge of Java syntax. We’ve included mini-references on the Java Naming and Directory Interface and JMS. So let’s roll up our sleeves…

Adding a Data GridWe’ll start by adding the top grid displaying the stock portfolio. The Flex dataGrid component is a natural choice for data that can be presented as rows and columns. MXML is a great tool for modu-larizing development. The name of the new .mxml file automatically becomes the name of a new tag that can be reused in other files. For example, the code in Listing 5.2 assumes that there is an MXML file named PortfolioView1.mxml (in reality it can also be an ActionScript file named Portfo-lioView1.as or any other file that is assigned to this tag via the component library manifest):

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

<!--portfolio1.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” >

<PortfolioView1 id=”pv”/>

</mx:Application>

Listing 5.2 The first version – portfolio1.mxml

In case of a default namespace (xmlns=”*”), our PortfolioView1.mxml from Listing 5.3 will be co-located in the same directory with the application file portfolio.mxml.

Let’s discuss the design of PortfolioView1. It contains DataGrid portfolioGrid in a Flex Panel with the title “Portfolio.” XML from Listing 5.1 is loaded into the portfolioModel e4x object. The list of securities from that file is fed into portfolioGrid via a binding expression {portfolioModel.security}. This expression returns an XMLList of all nodes named “security” that are direct children of the root node:

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

<!-- PortfolioView1.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml”

title=”Portfolio” width=”100%” height=”100%” …>

<mx:XML format=”e4x” id=”portfolioModel” source=”portfolio.xml” />

<mx:DataGrid id=”portfolioGrid” width=”100%”

dataProvider=”{portfolioModel.security}”>

<mx:columns>

<mx:DataGridColumn dataField=”Symbol”/>

<mx:DataGridColumn dataField=”Quantity” textAlign=”right”/>

<mx:DataGridColumn dataField=”Price” textAlign=”right”/>

<mx:DataGridColumn dataField=”Value” textAlign=”right”/>

</mx:columns>

</mx:DataGrid>

</mx:Panel>

Listing 5.3 The first version of PortfolioView.mxml

A Complete Application with RPC CommunicationsCHAPTER 5

Page 170: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 159

and when the user clicks on the link, a new browser window pops up displaying the selected news article.

The top of the screen contains the toggle buttons Show Grid/Show Chart. When the Show Grid op-tion is selected, the user will see the screen as in Figure 5.1, and when Show Chart is selected, the top data grid will be replaced with the pie chart (see Figure 5.2), preserving the same functionality (clicking on the pie slice repopulates the news headlines according to the selected security). The market data is refreshed on the screen every second or so.

Figure 5.2 Stock portfolio screen – the show chart view

The first version of the application will query the server POJO that generates random numbers. Later in this chapter, we’ll subscribe to JMS topic and consume a real-time data feed from an ex-ternal Java application.

In this application we’ll use the basic MXML and ActionScript from the first chapters of this book. We assume that the reader has a basic knowledge of Java syntax. We’ve included mini-references on the Java Naming and Directory Interface and JMS. So let’s roll up our sleeves…

Adding a Data GridWe’ll start by adding the top grid displaying the stock portfolio. The Flex dataGrid component is a natural choice for data that can be presented as rows and columns. MXML is a great tool for modu-larizing development. The name of the new .mxml file automatically becomes the name of a new tag that can be reused in other files. For example, the code in Listing 5.2 assumes that there is an MXML file named PortfolioView1.mxml (in reality it can also be an ActionScript file named Portfo-lioView1.as or any other file that is assigned to this tag via the component library manifest):

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

<!--portfolio1.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” >

<PortfolioView1 id=”pv”/>

</mx:Application>

Listing 5.2 The first version – portfolio1.mxml

In case of a default namespace (xmlns=”*”), our PortfolioView1.mxml from Listing 5.3 will be co-located in the same directory with the application file portfolio.mxml.

Let’s discuss the design of PortfolioView1. It contains DataGrid portfolioGrid in a Flex Panel with the title “Portfolio.” XML from Listing 5.1 is loaded into the portfolioModel e4x object. The list of securities from that file is fed into portfolioGrid via a binding expression {portfolioModel.security}. This expression returns an XMLList of all nodes named “security” that are direct children of the root node:

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

<!-- PortfolioView1.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml”

title=”Portfolio” width=”100%” height=”100%” …>

<mx:XML format=”e4x” id=”portfolioModel” source=”portfolio.xml” />

<mx:DataGrid id=”portfolioGrid” width=”100%”

dataProvider=”{portfolioModel.security}”>

<mx:columns>

<mx:DataGridColumn dataField=”Symbol”/>

<mx:DataGridColumn dataField=”Quantity” textAlign=”right”/>

<mx:DataGridColumn dataField=”Price” textAlign=”right”/>

<mx:DataGridColumn dataField=”Value” textAlign=”right”/>

</mx:columns>

</mx:DataGrid>

</mx:Panel>

Listing 5.3 The first version of PortfolioView.mxml

A Complete Application with RPC CommunicationsCHAPTER 5

Page 171: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

160 RIA WITH ADOBE FLEX AND JAVA

Even if we won’t add any more code, isn’t it impressive that it takes only a dozen lines of code to read the XML file, parse it, and display it in a grid shown in Figure 5.3? But this application has a static nature: it does not connect to any price quote feed. In other words you would always see $33.38 as the price for Microsoft, and $82.15 for IBM.

Figure 5.3 A data grid populated from Portfolio.xml

In Listing 5.3, the curly braces surrounding the expression indicate that this expression is being used as a source in data binding. It is crucial for Flex programming to fully understand the strengths and weaknesses of binding. Binding is based on event listeners automatically generated by the Flex compiler as a response to declarative binding annotations. To initiate binding generation, de-velopers use a combination of curly braces, mx:Binding tags, and [Bindable] metadata directives. (Refer to the Adobe Flex manual for more detail.) In Chapter 4, we’ve given you some examples of data binding, and in this chapter we’ll keep emphasizing the convenience of binding for automatic asynchronous code invocation.

Next, we need to periodically connect to the server for new prices and update the Price and Value columns. So let’s use a special Flex component called RemoteObject:

<mx:RemoteObject id=”freshQuotes” destination=”Portfolio”…> .

The RemoteObject component allows calling methods of a specified remote POJO, which is config-ured on the server as a destination Portfolio in a special XML file. We’d like to emphasize that Flex transparently calls Java from ActionScript. The client needs to know the name of the destination and the method to call, for example, getQuotes(). All the dirty work of data marshaling between ActionScript and Java is done for you by the Flex framework. If, for example, a Java method returns an object of the StockQuoteDTO.java type, Flex de-serializes the Java object and builds its Action-Script peer on the client. However, for performance reasons, it is recommended that you create the peer ActionScript class and register it with the framework. We’ll show the StockQuoteDTO.as later in this chapter.

CHAPTER 5

Page 172: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 161

Please note that all RPC communications, including RemoteObject, are asynchronous. In other words, we don’t exactly call a remote method, but rather send a message to the server, requesting a call of the specific Java method. Not only is the client’s request(s) executed asynchronously, but even sending to the server is done asynchronously. And if you need to do multiple RemoteObject invocations in your script, Flex will batch them together and send in the end of the script execu-tion.

The results of remote invocations are returned via events. RemoteObject provides the result event for success or fault for failures. You should write the corresponding handler functions. Flex will call these methods, supplying an Event object as a parameter. It’s your responsibility to get the infor-mation from the event and act accordingly. Friendly advice: you will save yourself hours of time if you supply a fault handler.

In the next code snippet we set concurrency to last, because if Flex decides to batch the outgoing requests, we do not want to send out more then one request in a batch; if a user clicks on the screen sending more than one request in quick succession, the last request will suppress all previous ones. Similarly, when the results are coming back we are interested only in the one we sent last:

<mx:RemoteObject id=”freshQuotes” destination=”portfolio” fault=”onFault(event);”>

<mx:method name=”getQuotes” concurrency=”last” result=”onResult(event);”/>

</mx:RemoteObject>

Listing 5.4 RemoteObject Tag

The tag <mx:RemoteObject> allows using result and fault handling on both the object and method levels. The method settings will take precedence over the RemoteObject’s ones.

For server-side support, you have to download and install Flex Data Services 2 Express Edition (http://www.adobe.com/products/flex/), and deploy it as a Web application in the J2EE server of your choice, for example, in Tomcat. FDS comes with a set of XML files, which you will use to con-figure your server-side objects.

To register a POJO class with a Flex client we need to update the configuration file on the server side. This lets you hide details of the service provider (i.e., actual Java class names) by specifying so-called destinations where you specify access constraints, etc. The following section has to be added to the remoting-config.xml file.

<destination id=”Portfolio”>

<properties>

<source>com.theriabook.ro.Portfolio</source>

</properties>

</destination>

Listing 5.5 Configuring a Flex remoting service

A Complete Application with RPC Communications

Page 173: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

162 RIA WITH ADOBE FLEX AND JAVA

Clients won’t know that the actual name of our POJO is com.theriabook.ro.Portfolio, but they’ll be able to refer to it by the nickname Portfolio. Flex looks for classes specified in destination mappings on the Web Application classpath including jars inside WEB-INF/lib and classes under WEB-INF/classes. The Java class Portfolio.java (see Listing 5.6) is a simple random number generator simulat-ing market-like real-time price changes for several hard-coded securities.

package com.theriabook.ro;

import java.util.Random;

import com.theriabook.jms.dto.StockQuoteDTO;

public class Portfolio {

static Random random = new Random();

static StockQuoteDTO[] quotes = {

new StockQuoteDTO(“IBM”, 82.0),

new StockQuoteDTO(“MSFT”, 27.0),

new StockQuoteDTO(“ADBE”, 38.0),

new StockQuoteDTO(“ORCL”, 13.0)};

double volatility=.05;

public StockQuoteDTO[] getQuotes() {

for (int i = 0; i < quotes.length;i++){

quotes[i].last += random.nextGaussian()* volatility;

}

return quotes;

}

}

Listing 5.6 A simple stock quote generator – Portfolio.java

The StockQuoteDTO.Java (see Listing 5.7) contains the last price of a particular stock.

package com.theriabook.jms.dto;

import java.io.Serializable;

public class StockQuoteDTO implements Serializable {

private static final long serialVersionUID = 4672447577075475117L;

public String symbol;

public double last;

public StockQuoteDTO(String sym, double newPrice){

symbol = sym;

last = newPrice;

}

}

Listing 5.7 The Java DTO - StockQuoteDTO.java

CHAPTER 5

Page 174: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 163

However, the client can really benefit from knowledge of the structure and the datatypes of the returned DTOs. Listing 5.8 shows the ActionScript’s counterpart for the StockQuoteDTO.java ob-ject. While Flex does not need this definition in order to deserialize the Java object that includes member variables of standard types (by default it creates a dynamic object and adds the required properties of the Java object that’s being deserialized), it does help performance (since the deseri-alized object is immediately allocated in memory), ensures the output datatypes and enforces the type conversion. The [RemoteClass…] metadata tag above the class definition tells the Flex de-se-rialization method to use this particular class for de-serialization whenever the server sends com.theriabook.jms.dto.StockQuoteDTO object down.

package com.theriabook.jms.dto {

[RemoteClass(alias=”com.theriabook.jms.dto.StockQuoteDTO”)]

public dynamic class StockQuoteDTO

{

public var symbol:String;

public var last:Number;

}

Listing 5.8 The ActionScript on the client: StockQuoteDTO.as

When the client successfully gets the quotes, it processes them and asks for new ones:

private function onResult(event:ResultEvent):void {

var quotes:Array = event.result as Array;

applyQuotes(quotes);

// Pull the next set of quotes

pullQuotes();

}

private function applyQuotes(quotes: Array):void {

for (var i:int=0; i<quotes.length; i++) {

var quote:StockQuoteDTO = StockQuoteDTO(quotes[i]);

var list: XMLList = portfolioModel.security.(Symbol==quote.symbol);

if (list.length()!=0) {

var row:XML = XML(list);

row.Price = Math.round(100*quote.last)/100;

row.Value = Math.round(row.Price * row.Quantity);

}

}

}

Listing 5.9 Processing received quotes

A Complete Application with RPC Communications

Page 175: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

164 RIA WITH ADOBE FLEX AND JAVA

The E4X provides a very elegant solution for navigating an XML object here. The applyQuotes func-tion iterates though the quotes from our portfolio and gets the XMLList based on the matching Symbol attribute via evaluation of the portfolioModel.security expression. (Symbol==quote.symbol.) This looks similar to XPath, but it’s easier, isn’t it? Since there’s a chance that the E4X expression above will return an empty XMLList, it’s better to check for zero-length to avoid exceptions. We are modifying the same XMLList that has been set as the data provider of our grid. In fact, for data grids Flex maintains an internal XMLListCollection with this XMLList as a source. When the program changes the data in the XMLList, these changes are automatically reflected on the screen.

Error reporting is often done by calling Alert(), which brings up a pop-up window. But we suggest a less obtrusive way, whereby as an error condition disappears, the normal display restores without user interaction. Let’s put a simple red Label control right above the data grid. Later in this chapter we’ll embed it in the Panel’s title. An error, if any, will be displayed in this Label control, and to make it a bit fancier, the detailed error description will be displayed as a tooltip whenever the user moves the mouse over this field:

<mx:Label color=”red” toolTip=”{errorTip}” text=”{errorText}” width=”100%”/>

To implement this functionality, in the scripting section we will create two bindable variables:

[Bindable]

private var errorText:String;

[Bindable]

private var errorTip:String;

When an error occurs, the onFault function sets the values of the variables errorText and errorTip, and their bindable nature will immediately display these values in the <mx:Label>. But most im-portantly, we will attempt to recover by pulling the new quotes set.

private function onFault(event:FaultEvent):void {

errorText = “Portfolio feed failure...”;

errorTip = “Destination:” + event.currentTarget.destination + “\n” +

“Fault code:” + event.fault.faultCode + “\n” +

“Detail:” + event.fault.faultDetail;

// Try to pull the new quotes

pullQuotes();

}

Listing 5.10 Error processing

CHAPTER 5

Page 176: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 165

Figure 5.4 Error display

Do not forget to clean the errorText and errorTip variables in the function onResult, if the next at-tempt to pull the quotes will be successful.

Let’s spend some time discussing the process of initiating the quote request. The function pullQuotes() gets initially invoked upon creation of the Panel:

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Portfolio” . . .

creationComplete=”pullQuotes();”>

To generate a remote call of some anonymous function every second, we’ll use the setTimeout mechanism. The anonymous function initiates the call of the Java object proxied by the remoting destination freshQuotes: private function pullQuotes():void{

setTimeout(function ():void {freshQuotes.getQuotes();},1000);

}

Listing 5.11 has the complete code of the first version of PortfolioView1.mxml, which contains just a data grid.

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

<!-- PortfolioView1.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml”

title=”Portfolio”

A Complete Application with RPC Communications

Page 177: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

166 RIA WITH ADOBE FLEX AND JAVA

width=”100%” height=”100%”

creationComplete=”pullQuotes();”

>

<mx:XML format=”e4x” id=”portfolioModel” source=”portfolio.xml” />

<mx:Label color=”red” toolTip=”{errorTip}” text=”{errorText}” width=”100%”/>

<mx:DataGrid id=”portfolioGrid” width=”100%” dataProvider=”{portfolioModel.security}”

>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”Symbol”/>

<mx:DataGridColumn dataField=”Quantity” textAlign=”right”/>

<mx:DataGridColumn dataField=”Price” textAlign=”right”/>

<mx:DataGridColumn dataField=”Value” textAlign=”right”/>

</mx:Array></mx:columns>

</mx:DataGrid>

<mx:RemoteObject id=”freshQuotes” destination=”portfolio”

fault=”onFault(event);” >

<mx:method name=”getQuotes” concurrency=”last”

result=”onResult(event);”

/>

</mx:RemoteObject>

<mx:Script><![CDATA[

import mx.rpc.events.*;

import com.theriabook.jms.dto.StockQuoteDTO;

[Bindable]

private var errorText:String;

[Bindable]

private var errorTip:String;

private function onResult(event:ResultEvent):void {

errorText = “”;

errorTip = “”;

var quotes:Array = event.result as Array;

applyQuotes(quotes);

// Pull new quotes set

pullQuotes();

}

private function onFault(event:FaultEvent):void {

errorText = “Portfolio feed failure...”;

errorTip = “Destination:” + event.currentTarget.destination + “\n” +

“Fault code:” + event.fault.faultCode + “\n” +

“Detail:” + event.fault.faultDetail;

// Pull new quotes set

CHAPTER 5

Page 178: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 167

pullQuotes();

}

private function applyQuotes(quotes: Array):void {

for (var i:int=0; i<quotes.length; i++) {

var quote:StockQuoteDTO = StockQuoteDTO(quotes[i]);

var list: XMLList = portfolioModel.security.(Symbol==quote.symbol);

if (list.length()!=0) {

var row:XML = XML(list);

row.Price = Math.round(100*quote.last)/100;

row.Value = Math.round(row.Price * row.Quantity);

}

}

}

private function pullQuotes():void{

setTimeout( function ():void {freshQuotes.getQuotes(); },1000 );

}

]]></mx:Script>

</mx:Panel>

Listing 5.11 PortfolioView1.mxml

Adding the Charting ComponentThe population of the data grid is complete, and we are ready to work on adding a Flex charting component.

Figure 5.5 Adding a charting component

A Complete Application with RPC Communications

Page 179: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

168 RIA WITH ADOBE FLEX AND JAVA

Let’s create a simple application that adds the pie chart below the data grid and gives the grid and the chart 50% of the screen height each:

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

<!--portfolio2.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” >

<PortfolioView2 id=”pv”/>

</mx:Application>

The fragment of the PortfolioView2.mxml is shown below:

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

<!-- PortfolioView2.mxml -->

<mx:Panel xmlns:mx=http://www.adobe.com/2006/mxml>

<mx:DataGrid id=”portfolioGrid” width=”100%” height=”50%”

dataProvider=”{portfolioModel.security}”

</mx:DataGrid>

<mx:PieChart id=”portfolioPie” dataProvider=”{portfolioModel.security}”

showDataTips=”true” width=”100%” height=”50%”>

<mx:series>

<mx:Array>

<mx:PieSeries labelPosition=”callout” field=”Value”

labelFunction=”showPosition” nameField=”Symbol” explodeRadius=”2”/>

</mx:Array>

</mx:series>

</mx:PieChart>

<mx:Script>

private function showPosition(data:Object, field:String, index:Number,

percentValue:Number):String {

return data.Symbol + “\n” + “Shares:” + data.Quantity + “\n” +

“Price:” + data.Price + “\n” + “Value:” + data.Value ;

}

</mx:Script>

</mx:Panel>

Listing 5.12 Adding the pie chart to the PortfolioView

Flex Panel containers have a layout property (horizontal, vertical, and absolute). Since the vertical layout is the default, our chart is positioned right under the grid. Please note that chart’s dataProvid-er is based on the same XMLList portfolioModel.security as is the dataProvider of the portfolioGrid. In other words, we have two views of the same data model. The data binding feature results in im-

CHAPTER 5

Page 180: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 169

mediate updates of both controls on each change of the model. Java Swing developers will appreci-ate the benefits of this feature as opposed to the JavaBean and property listeners hassle.

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

<!-- PortfolioView2.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Portfolio”

width=”100%” height=”100%” creationComplete=”pullQuotes();” >

<mx:XML format=”e4x” id=”portfolioModel” source=”portfolio.xml” />

<mx:Label color=”red” toolTip=”{errorTip}” text=”{errorText}” width=”100%”/>

<mx:DataGrid id=”portfolioGrid” width=”100%” height=”50%”

dataProvider=”{portfolioModel.security}”>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”Symbol”/>

<mx:DataGridColumn dataField=”Quantity” textAlign=”right”/>

<mx:DataGridColumn dataField=”Price” textAlign=”right”/>

<mx:DataGridColumn dataField=”Value” textAlign=”right”/>

</mx:Array></mx:columns>

</mx:DataGrid>

<mx:PieChart id=”portfolioPie” dataProvider=”{portfolioModel.security}”

showDataTips=”true” width=”100%” height=”50%”>

<mx:series>

<mx:Array>

<mx:PieSeries labelPosition=”callout” field=”Value”

labelFunction=”showPosition” nameField=”Symbol” explodeRadius=”2” />

</mx:Array>

</mx:series>

</mx:PieChart>

<mx:RemoteObject id=”freshQuotes” destination=”portfolio”

fault=”onFault(event)”>

<mx:method name=”getQuotes” concurrency=”last” result=”onResult(event)”/>

</mx:RemoteObject>

<mx:Script><![CDATA[

import mx.rpc.events.*;

import com.theriabook.jms.dto.StockQuoteDTO;

[Bindable]

private var errorText:String;

[Bindable]

private var errorTip:String;

private function onResult(event:ResultEvent):void {

errorText = “”;

A Complete Application with RPC Communications

Page 181: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

170 RIA WITH ADOBE FLEX AND JAVA

errorTip = “”;

var quotes:Array = event.result as Array;

applyQuotes(quotes);

// Pull new quotes set

pullQuotes();

}

private function onFault(event:FaultEvent):void {

errorText = “Portfolio feed failure...”;

errorTip = “Destination:” + event.currentTarget.destination + “\n” +

“Fault code:” + event.fault.faultCode + “\n” +

“Detail:” + event.fault.faultDetail;

// Pull new quotes set

pullQuotes();

}

private function applyQuotes(quotes: Array):void {

for (var i:int=0; i<quotes.length; i++) {

var quote:StockQuoteDTO = StockQuoteDTO(quotes[i]);

var list: XMLList = portfolioModel.security.(Symbol==quote.symbol);

if (list.length()!=0) {

var row:XML = XML(list);

row.Price = Math.round(100*quote.last)/100;

row.Value = Math.round(row.Price * row.Quantity);

}

}

}

private function pullQuotes():void{

setTimeout( function ():void { freshQuotes.getQuotes(); },1000);

}

private function showPosition(data:Object, field:String, index:Number,

percentValue:Number):String {

return data.Symbol + “\n” + “Shares:” + data.Quantity +

“\n” + “Price:” + data.Price + “\n” + “Value:” + data.Value ;

}

]]></mx:Script>

</mx:Panel>

Listing 5.13 PortfolioView2.mxml

For the pie chart, we’ve selected a callout type of label positioning, specified so the size of the pie is proportional to the attribute Value, and we’ve added a 3D depth to the chart by setting explode

CHAPTER 5

Page 182: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 171

Radius to 0.02. Note how we’ve assigned the function name showPosition to the labelFunction at-tribute of the pie chart. The signature of the labelFunction assumes that the first argument brings the element of the dataProvider corresponding to the current wedge in the series.

Chart/DataGrid TogglingImagine a deck of playing cards: only the top card is visible. Hide the top card and you’ll see the next one. We’ll use the deck of two “cards”: one card will display the grid, and another one – the chart. To Java Swing developers this should look like the CardLayout. In Flex jargon it’s called <mx:ViewStack>. The screen snapshot in Figure 5.6 was made when the portfolioPie was on the top of the portfolioGrid:

Figure 5.6 Using ViewStack for toggling

A fragment of PortfolioView3.mxml introduces MXML components <mx:ViewStack>, <mx:Toggle-ButtonBar>, and <mx:Canvas>.

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

<!-- PortfolioView3.mxml -->

<mx:Panel…>

<mx:ViewStack id=”vs” width=”100%” height=”100%”>

<mx:Canvas label=”Show Grid” icon=”{iconGrid}” >

<mx:DataGrid id=”portfolioGrid” …height=”100%”/>

A Complete Application with RPC Communications

Page 183: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

172 RIA WITH ADOBE FLEX AND JAVA

</mx:Canvas>

<mx:Canvas label=”Show Chart” icon=”{iconChart}” >

<mx:PieChart id=”portfolioPie” … height=”100%”/>

</mx:Canvas>

</mx:ViewStack>

<mx:ToggleButtonBar dataProvider=”{vs}” horizontalGap=”5” />

<mx:RemoteObject id=”freshQuotes” …/>

<mx:Script><![CDATA[

[Embed(source=”images/icon_chart.png”)]

[Bindable]

public var iconChart : Class;

[Embed(source=”images/icon_grid.png”)]

[Bindable]

public var iconGrid : Class;

]]></mx:Script>

</mx:Panel>

Listing 5.14 Adding a stacked view

The most suitable Flex containers for toggling the views are ViewStack or its direct descendant TabNavigator. The latter uses more screen real estate to paint the tabs. So we’d rather put the view toggling controls on the unused area of the Panel’s title bar. The ViewStack component provides programmatic indexed access to the child containers and shows them one at a time.

The simplest Flex container is called Canvas, so we wrap up the DataGrid and the PieChart sepa-rately inside it. A Canvas is a descendant of the Container ActionScript class and has the properties label and icon. There are two “non-programmatic” ways to use these properties. First, certain con-tainers use them implicitly, for instance, TabNavigator automatically arranges its tabs to display labels and show icons from the nested child containers. The second way is to explicitly use the ViewStack as a data provider for the descendants of the NavBar control such as ButtonBar, LinkBar, and, in our case, ToggleButtonBar. When you use a ViewStack as a data provider, the label and icon properties of the ViewStack container’s children are used to populate the navigation items. The ViewStack feeds ToggleButtonbar and ToggleButtonbar controls the ViewStack in return.

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

<!-- PortfolioView3.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”Portfolio”

width=”100%” height=”100%” creationComplete=”pullQuotes();” >

<mx:XML format=”e4x” id=”portfolioModel” source=”portfolio.xml” />

<mx:Label color=”red” toolTip=”{errorTip}” text=”{errorText}” width=”100%”/>

<mx:ToggleButtonBar dataProvider=”{vs}” horizontalGap=”5” />

<mx:ViewStack id=”vs” width=”100%” height=”100%”>

CHAPTER 5

Page 184: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 173

<mx:Canvas label=”Show Grid” icon=”{iconGrid}” >

<mx:DataGrid id=”portfolioGrid” width=”100%” height=”100%”

dataProvider=”{portfolioModel.security}”>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”Symbol”/>

<mx:DataGridColumn dataField=”Quantity” textAlign=”right”/>

<mx:DataGridColumn dataField=”Price” textAlign=”right”/>

<mx:DataGridColumn dataField=”Value” textAlign=”right”/>

</mx:Array></mx:columns>

</mx:DataGrid>

</mx:Canvas>

<mx:Canvas label=”Show Chart” icon=”{iconChart}” >

<mx:PieChart id=”portfolioPie” dataProvider=”{portfolioModel.security}”

showDataTips=”true” width=”100%” height=”100%”>

<mx:series>

<mx:Array>

<mx:PieSeries labelPosition=”callout”

field=”Value” labelFunction=”showPosition”

nameField=”Symbol” explodeRadius=”0.02” />

</mx:Array>

</mx:series>

</mx:PieChart>

</mx:Canvas>

</mx:ViewStack>

<mx:RemoteObject id=”freshQuotes” destination=”portfolio”

fault=”onFault(event)”>

<mx:method name=”getQuotes” concurrency=”last”

result=”onResult(event)”

/>

</mx:RemoteObject>

<mx:Script><![CDATA[

import mx.rpc.events.*;

import com.theriabook.jms.dto.StockQuoteDTO;

[Bindable]

private var errorText:String;

[Bindable]

private var errorTip:String;

private function onResult(event:ResultEvent):void {

errorText = “”;

errorTip = “”;

var quotes:Array = event.result as Array;

A Complete Application with RPC Communications

Page 185: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

174 RIA WITH ADOBE FLEX AND JAVA

applyQuotes(quotes);

// Pull new quotes set

pullQuotes();

}

private function onFault(event:FaultEvent):void {

errorText = “Portfolio feed failure...”;

errorTip = “Destination:” + event.currentTarget.destination + “\n” +

“Fault code:” + event.fault.faultCode + “\n” +

“Detail:” + event.fault.faultDetail;

// Pull new quotes set

pullQuotes();

}

private function applyQuotes(quotes: Array):void {

for (var i:int=0; i<quotes.length; i++) {

var quote:StockQuoteDTO = StockQuoteDTO(quotes[i]);

var list: XMLList = portfolioModel.security.(Symbol==quote.symbol);

if (list.length()!=0) {

var row:XML = XML(list);

row.Price = Math.round(100*quote.last)/100;

row.Value = Math.round(row.Price * row.Quantity);

}

}

}

private function pullQuotes():void{

setTimeout( function ():void { freshQuotes.getQuotes(); },1000);

}

private function showPosition(data:Object, field:String, index:Number,

percentValue:Number):String {

return data.Symbol + “\n” + “Shares:” + data.Quantity + “\n” +

“Price:” + data.Price + “\n” + “Value:” + data.Value ;

}

[Embed(source=”images/icon_chart.png”)]

[Bindable]

public var iconChart : Class;

[Embed(source=”images/icon_grid.png”)]

[Bindable]

public var iconGrid : Class;

]]></mx:Script>

</mx:Panel>Listing 5.15 PortfolioView3.mxml

CHAPTER 5

Page 186: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 175

The toggle buttons Show Grid/Show Chart use images, and it’s a good idea to embed these resourc-es right into the SWF file. This way the browser can download the entire client in one HTTP request (but the size of the SWF file becomes larger). For the same reason, the multi-file Java applets are packaged into a single jar.

The next step is to move the toggle buttons up:

Figure 5.7 Using ViewStack for toggling

In the previous code sample, we use canvases to wrap, but now we’re going wrap up the entire Port-folio Panel. Yes, the Panel will become a child of the Canvas. Here is why we need it. The Canvas is the simplest container, and it’s also the absolute positioning container. In other words, if you place A and B as children of the Canvas, they overlap unless each of them has specific x and y coordinate settings. So we’re planning on overlapping the ToggleButtonBar and the PortfolioPanel in a single Canvas. As an additional measure, to make the ToggleButtonBar appear on the far right, we put the ToggleButtonBar inside the HBox container. Make sure that the RemoteObject and the XML are moved out to become children of the Canvas; they have to be at the outermost level – this is an MXML requirement.

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

<!-- PortfolioView4.mxml -->

<mx:Canvas xmlns:mx=”http://www.adobe.com/2006/mxml” width=”100%” height=”100%” >

<mx:RemoteObject id=”freshQuotes” destination=”portfolio” fault=”onFault(event)”>

<mx:method name=”getQuotes” concurrency=”last” result=”onResult(event)” />

A Complete Application with RPC Communications

Page 187: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

176 RIA WITH ADOBE FLEX AND JAVA

</mx:RemoteObject>

<mx:XML format=”e4x” id=”portfolioModel” source=”portfolio.xml” />

<mx:Panel title=”Portfolio” width=”100%” height=”100%” creationComplete=”pullQuotes();”

>

<mx:ViewStack id=”vs” width=”100%” height=”100%”>

<mx:Canvas label=”Show Grid” icon=”{iconGrid}” >

<mx:DataGrid id=”portfolioGrid” width=”100%” height=”100%”

dataProvider=”{portfolioModel.security}”>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”Symbol”/>

<mx:DataGridColumn dataField=”Quantity” textAlign=”right”/>

<mx:DataGridColumn dataField=”Price” textAlign=”right”/>

<mx:DataGridColumn dataField=”Value” textAlign=”right”/>

</mx:Array></mx:columns>

</mx:DataGrid>

</mx:Canvas>

<mx:Canvas label=”Show Chart” icon=”{iconChart}” >

<mx:PieChart id=”portfolioPie” dataProvider=”{portfolioModel.security}”

showDataTips=”true” width=”100%” height=”100%”>

<mx:series>

<mx:Array>

<mx:PieSeries labelPosition=”callout”

field=”Value” labelFunction=”showPosition”

nameField=”Symbol” explodeRadius=”0.02” />

</mx:Array>

</mx:series>

</mx:PieChart>

</mx:Canvas>

</mx:ViewStack>

<mx:Script><![CDATA[

import mx.rpc.events.*;

import com.theriabook.jms.dto.StockQuoteDTO;

[Bindable]

private var errorText:String;

[Bindable]

private var errorTip:String;

private function onResult(event:ResultEvent):void {

errorText = “”;

errorTip = “”;

var quotes:Array = event.result as Array;

CHAPTER 5

Page 188: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 177

applyQuotes(quotes);

// Pull new quotes set

pullQuotes();

}

private function onFault(event:FaultEvent):void {

errorText = “Portfolio feed failure...”;

errorTip = “Destination:” + event.currentTarget.destination + “\n” +

“Fault code:” + event.fault.faultCode + “\n” +

“Detail:” + event.fault.faultDetail;

// Pull new quotes set

pullQuotes();

}

private function applyQuotes(quotes: Array):void {

for (var i:int=0; i<quotes.length; i++) {

var quote:StockQuoteDTO = StockQuoteDTO(quotes[i]);

var list: XMLList = portfolioModel.security.(Symbol==quote.symbol);

if (list.length()!=0) {

var row:XML = XML(list);

row.Price = Math.round(100*quote.last)/100;

row.Value = Math.round(row.Price * row.Quantity);

} }

}

private function pullQuotes():void{

setTimeout( function ():void {freshQuotes.getQuotes(); },1000 );

}

private function showPosition(data:Object, field:String, index:Number,

percentValue:Number):String {

return data.Symbol + “\n” + “Shares:” + data.Quantity + “\n” +

“Price:” + data.Price + “\n” + “Value:” + data.Value ;

}

[Embed(source=”images/icon_chart.png”)]

[Bindable]

public var iconChart : Class;

[Embed(source=”images/icon_grid.png”)]

[Bindable]

public var iconGrid : Class;

]]></mx:Script>

</mx:Panel>

<mx:HBox horizontalAlign=”right” width=”100%” paddingRight=”5” paddingTop=”5”>

A Complete Application with RPC Communications

Page 189: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

178 RIA WITH ADOBE FLEX AND JAVA

<mx:Label color=”red” toolTip=”{errorTip}” text=”{errorText}” width=”150”/>

<mx:ToggleButtonBar dataProvider=”{vs}” horizontalGap=”5” />

</mx:HBox>

</mx:Canvas>

Listing 5.16 PortfolioView4.mxml

Dealing with Financial NewsLet’s set up the scene for the financial news first, and then we’ll add them to the screen that we’ve developed so far. The first cut of our financial news screen will look like Figure 5.8. The data you see here are provided by an RSS feed. RSS stands for Real Simple Syndication and is used for presenting such data as news, blogs, or other Web content in a form of XML that contains summaries of the articles as well links to the full version of the content. We use it simply to illustrate the client/server communication via the Flex component called <mx:HTTPService>, which facilitates HTTP POST and GET requests from the Flash player to a remote URL with automatic embedding of the param-eters.

Figure 5.8 Financial news - the first cut

In particular, we’ll be using the RSS news generator offered by Yahoo! Finance. Just enter the follow-ing URL in your browser: http://biz.yahoo.com/rss.html.

CHAPTER 5

Page 190: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 179

You should see a screen similar to the one depicted in Figure 5.9. This Web site lets you enter a stock symbol, for example, ADBE.

Figure 5.9 RSS news archive from Yahoo! Finance

You’ll see a little orange XML button, with a new URL that looks like http://finance.yahoo.com/rss/headline?s=adbe. Just follow this link, which will bring you to the RSS XML feed withthe latest news about the symbol ADBE, as in Figure 5.10.

A Complete Application with RPC Communications

Page 191: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

180 RIA WITH ADOBE FLEX AND JAVA

Figure 5.10 A sample RSS feed

By the end of this chapter we’ll be done with programming master-detail relationships; our users will click on the row in the portfolio grid (Figure 5.7) and see the news as in Figure 5.9, then click on one of the lines in the column Link, which opens a new Web browser’s window with the full news content. Let’s implement this chain of actions one step at a time.

Our new MXML application will contain the following HTTPService element:

<mx:HTTPService id=”newsFeed” useProxy=”true” destination=”YahooFinancialNews”

concurrency=”last” resultFormat=”e4x” >

</mx:HTTPService>

Your client application will be loaded from some server, which is expected to have a configured news destination named YahooFinancialNews, or whatever name you prefer.

CHAPTER 5

Page 192: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 181

A Complete Application with RPC Communications

Configuring the Server-Side Destination and ProxyFor security reasons (similar to the Java sandbox concept), Flash clients can only access the do-mains they come from, unless other servers declare, explicitly or implicitly, trust to SWF files down-loaded from our domain by a corresponding record in a crossdomain.xml file. But our portfolio SWF wasn’t loaded from finance.yahoo.com, and we aren’t allowed to install crossdomain.xml on the Yahoo! servers. We’ll use another technique called Flex proxy. When the user clicks on the News link in the data grid, the portfolio client will connect to our FDS Web application deployed under Tomcat (JRun, WebLogic), which will proxy our communication with Yahoo!. To configure the Flex proxy service, use the following section of the proxy-config.xml located in the \WEB-INF\flex di-rectory:

<destination id=”YahooFinancialNews”>

<properties>

<url>http://finance.yahoo.com/rss/headline</url>

</properties>

</destination>

Listing 5.17 Configuring the proxy to access an external server

Now Flex will contact http://finance.yahoo.com, get the news for the symbol specified, and return it back to the Flash client.

Processing the News FeedThe HTTPService converts the received data according to the value of the resultFormat property: e4x, flashvars, object (this is default), text, or xml.

If we knew that the results were coming back as name/value pairs concatenated with an amper-sand, we could have picked the flashvars format. The text format is suitable for any raw text, but it’s not easy to parse. The XML format is maintained for compatibility with pre-E4X versions of the ActionScript, so we’ll pass on this one as well. Considering the other formats listed above, for our application, we prefer e4X XML. An e4X expression newsFeed.lastResult.channel.item is all you need to populate the XMLListCollection with news headlines. This XMLListCollection will be used as the dataProvider of the news DataGrid.

As usual, we’ll separate the code that merely starts the application from the FinancialNews view in Listing 5.18:

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

<!--news1.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” >

<FinancialNews1 id=”fn”/>

</mx:Application>

Page 193: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

182 RIA WITH ADOBE FLEX AND JAVA

Our newsFeed will initiate the following request from the creationComplete event:

newsFeed.send({s:”ADBE”});

As part of the send method, Flex converts the object’s property into a GET URL parameter using the object’s properties as parameter names. Here s:”ADBE” will be concatenated in the form of “?s=ADBE” to the http://finance.yahoo.com/rss/headline specified by the news destination. Since RSS data can be verbose, we’ll set the relevant columns of the grid with wordWrap=”true” and the height of the Grid row to be flexible: variableRowHeight=”true”.

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

<!-- FinancialNews1.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml”

title=”News” width=”100%” height=”100%” creationComplete=”onCreationComplete()” >

<mx:DataGrid id=”newsGrid” width=”100%” height=”100%”

dataProvider=”{newsFeed.lastResult.channel.item}” variableRowHeight=”true”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn headerText=”Date” dataField=”pubDate” width=”200”/>

<mx:DataGridColumn headerText=”Title” dataField=”title” wordWrap=”true” />

<mx:DataGridColumn headerText=”Description” dataField=”description”

wordWrap=”true” />

<mx:DataGridColumn headerText=”Link” width=”130” dataField=”link”

wordWrap=”true” />

</mx:Array>

</mx:columns>

</mx:DataGrid>

<mx:HTTPService id=”newsFeed” useProxy=”true” destination=”YahooFinancialNews”

concurrency=”last” resultFormat=”e4x” fault=”onFault(event)” >

</mx:HTTPService>

<mx:Script>

<![CDATA[

private function onCreationComplete():void {

newsFeed.send({s:”ADBE”});

}

import mx.rpc.events.*;

private function onFault(event:FaultEvent):void {

mx.controls.Alert.show(

“Destination:” + event.currentTarget.destination + “\n” +

“Fault code:” + event.fault.faultCode + “\n” +

“Detail:” + event.fault.faultDetail, “News feed failure”

A Complete Application with RPC CommunicationsCHAPTER 5

);

}

]]>

</mx:Script>

</mx:Panel>

Lising 5.18 FinancialNews1.mxml

Introducing Item RenderersTo make the data grid’s column Link open a news story in a separate browser window, we need to customize the way of displaying (rendering) the cells of this column. We will use the so-called item-Renderer in the <mx:DataGridColumn> tag (see more samples of using item renderers in Chapters 8 and 9). There are two major kinds of item renderers:

drop-in, which is an ActionScript class that you specify as the value of the itemRenderer of any list-derived control.

inline, where you use an <mx:Component> tag to define a renderer component inside the <mx:itemRenderer> element.

To create an inline renderer, we’d need to change:

<mx:DataGridColumn headerText=”Link” width=”130” dataField=”link”

wordWrap=”true”/>

into the following code:

<mx:DataGridColumn headerText=”Link” width=”130”>

<mx:itemRenderer>

<mx:Component>

<mx:LinkButton label=”{data.link}” click=”navigateToURL(new

URLRequest(data.link), ‘_blank’)”/>

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

The ActionScript function flash.net.navigateToURL opens or replaces a window in the Flash Play-er’s container application – opens it, in our case, in a blank browser.

While inline renderers excel in readability, drop-in renderers are reusable. If you expect to have links with similar presentation and functionality you may decide to create a class out of it.

Let us introduce the write-only security property for the FinancialView2 class. Please note that we pro-

Page 194: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 183

Our newsFeed will initiate the following request from the creationComplete event:

newsFeed.send({s:”ADBE”});

As part of the send method, Flex converts the object’s property into a GET URL parameter using the object’s properties as parameter names. Here s:”ADBE” will be concatenated in the form of “?s=ADBE” to the http://finance.yahoo.com/rss/headline specified by the news destination. Since RSS data can be verbose, we’ll set the relevant columns of the grid with wordWrap=”true” and the height of the Grid row to be flexible: variableRowHeight=”true”.

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

<!-- FinancialNews1.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml”

title=”News” width=”100%” height=”100%” creationComplete=”onCreationComplete()” >

<mx:DataGrid id=”newsGrid” width=”100%” height=”100%”

dataProvider=”{newsFeed.lastResult.channel.item}” variableRowHeight=”true”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn headerText=”Date” dataField=”pubDate” width=”200”/>

<mx:DataGridColumn headerText=”Title” dataField=”title” wordWrap=”true” />

<mx:DataGridColumn headerText=”Description” dataField=”description”

wordWrap=”true” />

<mx:DataGridColumn headerText=”Link” width=”130” dataField=”link”

wordWrap=”true” />

</mx:Array>

</mx:columns>

</mx:DataGrid>

<mx:HTTPService id=”newsFeed” useProxy=”true” destination=”YahooFinancialNews”

concurrency=”last” resultFormat=”e4x” fault=”onFault(event)” >

</mx:HTTPService>

<mx:Script>

<![CDATA[

private function onCreationComplete():void {

newsFeed.send({s:”ADBE”});

}

import mx.rpc.events.*;

private function onFault(event:FaultEvent):void {

mx.controls.Alert.show(

“Destination:” + event.currentTarget.destination + “\n” +

“Fault code:” + event.fault.faultCode + “\n” +

“Detail:” + event.fault.faultDetail, “News feed failure”

A Complete Application with RPC CommunicationsCHAPTER 5

);

}

]]>

</mx:Script>

</mx:Panel>

Lising 5.18 FinancialNews1.mxml

Introducing Item RenderersTo make the data grid’s column Link open a news story in a separate browser window, we need to customize the way of displaying (rendering) the cells of this column. We will use the so-called item-Renderer in the <mx:DataGridColumn> tag (see more samples of using item renderers in Chapters 8 and 9). There are two major kinds of item renderers:

drop-in, which is an ActionScript class that you specify as the value of the itemRenderer of any list-derived control.

inline, where you use an <mx:Component> tag to define a renderer component inside the <mx:itemRenderer> element.

To create an inline renderer, we’d need to change:

<mx:DataGridColumn headerText=”Link” width=”130” dataField=”link”

wordWrap=”true”/>

into the following code:

<mx:DataGridColumn headerText=”Link” width=”130”>

<mx:itemRenderer>

<mx:Component>

<mx:LinkButton label=”{data.link}” click=”navigateToURL(new

URLRequest(data.link), ‘_blank’)”/>

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

The ActionScript function flash.net.navigateToURL opens or replaces a window in the Flash Play-er’s container application – opens it, in our case, in a blank browser.

While inline renderers excel in readability, drop-in renderers are reusable. If you expect to have links with similar presentation and functionality you may decide to create a class out of it.

Let us introduce the write-only security property for the FinancialView2 class. Please note that we pro-

Page 195: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

184 RIA WITH ADOBE FLEX AND JAVA

vide a setter method to send a newsFeed request every time the security (stock symbol) is updated:

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

newsFeed.send({s:value});

}

A bit later in this chapter, the value of the selected security will be passed to the news data grid based on the selected row in the portfolio data grid (see Figure 5.1) or the selected slice of the pie (see Figure 5.2). At this point let’s remove the creationComplete event handler in FinancialNews and hard-code the stock symbol ADBE as a valve of security for the FinancialNews2 tag:

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

<!--news2.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” >

<FinancialNews2 id=”fn” security=”ADBE”/>

</mx:Application>

Listing 5.19 An application file news2.mxml

The FinancialNews2 tag is spelled out in Listing 5.20.

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

<!-- FinancialNews2.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”News” width=”100%”

height=”100%” >

<mx:DataGrid id=”newsGrid” width=”100%” height=”100%”

dataProvider=”{newsList}” variableRowHeight=”true”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn headerText=”Date” dataField=”pubDate” width=”200”/>

<mx:DataGridColumn headerText=”Title” dataField=”title” wordWrap=”true” />

<mx:DataGridColumn headerText=”Description” dataField=”description”

wordWrap=”true” />

<mx:DataGridColumn headerText=”Link” width=”130”>

<mx:itemRenderer>

<mx:Component>

<mx:LinkButton label=”{data.link}”

click=”navigateToURL(new URLRequest(data.link), ‘_blank’)”/>

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

</mx:Array>

</mx:columns>

</mx:DataGrid>

A Complete Application with RPC CommunicationsCHAPTER 5

<mx:XMLListCollection id=”newsList” source=”{newsFeed.result.channel.item}” />

<mx:HTTPService id=”newsFeed” useProxy=”true” destination=”YahooFinancialNews”

concurrency=”last” resultFormat=”e4x” fault=”onFault(event)” >

</mx:HTTPService>

<mx:Script>

<![CDATA[

import mx.rpc.events.*;

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

this.title = “News: “ + value;

newsFeed.send({s:value});

}

private function onFault(event:FaultEvent):void {

mx.controls.Alert.show(

“Destination:” + event.currentTarget.destination + “\n” +

“Fault code:” + event.fault.faultCode + “\n” +

“Detail:” + event.fault.faultDetail, “News feed failure”

);

}

]]>

</mx:Script>

</mx:Panel>

Listing 5.20 FinancialNews2.mxml

Another minor modification in Listing 5.20 is that the XML received with Yahoo! Financial news is now a source property of the <mx:XMLListCollection>. The data grid’s dataProvider is populated from this collection. You don’t have to use a collection as the middleman between the data and the GUI component, but it does become handy for customization. In particular you can apply a sort or set a filter on the collection.

Programming Master-Detail RelationshipsIt’s time to start thinking about connecting the portfolio and news data grids, which represent typical master-detail relationships. The user selects one security in the grid or the pie, and the news data grid has to be repopulated with potentially multiple rows representing the latest financial news on selected security. Or, technically speaking, we are planning to convert the line:

<FinancialNews2 id=”fn” security=”ADBE”/>

from Listing 5.19 into a security bound to portfolioView.selectedSecurity. So in the scripting section of PorfolioView5.mxml we’ll declare a bindable variable selectedSecurity:

Page 196: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 185

vide a setter method to send a newsFeed request every time the security (stock symbol) is updated:

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

newsFeed.send({s:value});

}

A bit later in this chapter, the value of the selected security will be passed to the news data grid based on the selected row in the portfolio data grid (see Figure 5.1) or the selected slice of the pie (see Figure 5.2). At this point let’s remove the creationComplete event handler in FinancialNews and hard-code the stock symbol ADBE as a valve of security for the FinancialNews2 tag:

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

<!--news2.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” >

<FinancialNews2 id=”fn” security=”ADBE”/>

</mx:Application>

Listing 5.19 An application file news2.mxml

The FinancialNews2 tag is spelled out in Listing 5.20.

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

<!-- FinancialNews2.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”News” width=”100%”

height=”100%” >

<mx:DataGrid id=”newsGrid” width=”100%” height=”100%”

dataProvider=”{newsList}” variableRowHeight=”true”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn headerText=”Date” dataField=”pubDate” width=”200”/>

<mx:DataGridColumn headerText=”Title” dataField=”title” wordWrap=”true” />

<mx:DataGridColumn headerText=”Description” dataField=”description”

wordWrap=”true” />

<mx:DataGridColumn headerText=”Link” width=”130”>

<mx:itemRenderer>

<mx:Component>

<mx:LinkButton label=”{data.link}”

click=”navigateToURL(new URLRequest(data.link), ‘_blank’)”/>

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

</mx:Array>

</mx:columns>

</mx:DataGrid>

A Complete Application with RPC CommunicationsCHAPTER 5

<mx:XMLListCollection id=”newsList” source=”{newsFeed.result.channel.item}” />

<mx:HTTPService id=”newsFeed” useProxy=”true” destination=”YahooFinancialNews”

concurrency=”last” resultFormat=”e4x” fault=”onFault(event)” >

</mx:HTTPService>

<mx:Script>

<![CDATA[

import mx.rpc.events.*;

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

this.title = “News: “ + value;

newsFeed.send({s:value});

}

private function onFault(event:FaultEvent):void {

mx.controls.Alert.show(

“Destination:” + event.currentTarget.destination + “\n” +

“Fault code:” + event.fault.faultCode + “\n” +

“Detail:” + event.fault.faultDetail, “News feed failure”

);

}

]]>

</mx:Script>

</mx:Panel>

Listing 5.20 FinancialNews2.mxml

Another minor modification in Listing 5.20 is that the XML received with Yahoo! Financial news is now a source property of the <mx:XMLListCollection>. The data grid’s dataProvider is populated from this collection. You don’t have to use a collection as the middleman between the data and the GUI component, but it does become handy for customization. In particular you can apply a sort or set a filter on the collection.

Programming Master-Detail RelationshipsIt’s time to start thinking about connecting the portfolio and news data grids, which represent typical master-detail relationships. The user selects one security in the grid or the pie, and the news data grid has to be repopulated with potentially multiple rows representing the latest financial news on selected security. Or, technically speaking, we are planning to convert the line:

<FinancialNews2 id=”fn” security=”ADBE”/>

from Listing 5.19 into a security bound to portfolioView.selectedSecurity. So in the scripting section of PorfolioView5.mxml we’ll declare a bindable variable selectedSecurity:

Page 197: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

186 RIA WITH ADOBE FLEX AND JAVA

[Bindable]

public var selectedSecurity:String;

The metatag [Bindable] provides an elegant two-line solution for our master-detail parameter passing. The tag PortfolioView5 will go by the ID pv, hence a simple {pv.selectedSecurity} repre-sents the value of this bindable property:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

“xmlns=“*” >

<mx:VDividedBox width=“100%” height=“100%”>

<PortfolioView5 id=“pv”/>

<FinancialNews2 id=“fn” security=“{pv.selectedSecurity}”/>

</mx:VDividedBox>

</mx:Application>

Listing 5.21 A master-details application

To be precise, the curly braces denote binding between the FinancialNews2.security, the target of the binding, and the Portfolio5.selectedSecurity as the binding source.

Here is the short explanation of how it works. When Flex compiles PortfolioView5.mxml with the bindable selectedSecurity (see Listing 5.22), it will produce code that dispatches an event on every change of that property. Then, while compiling our application, Flex will notice that Financial-News2.security depends on the binding expression. But the expression, in turn, depends on the val-ue of PortfolioView5.selectedSecurity, doesn’t it? Accordingly, Flex will add a listener to the change event associated with pv.selectedSecurity. The task of this listener will be to keep the expression recalculated at every change of PortfolioView5.selectedSecurity. Following the same logic, Flex will keep FinancialNews2.security recalculated on every change of the expression. You can read more about binding in the Flex documentation.

Our application keeps the Portfolio on top of the financial news in a vertical box. To facilitate resiz-ing between the two parts, we’ve used the VDividedBox container instead of the VBox because it has a divider between the parts (similar to Java Swing split panes).

CHAPTER 5

Page 198: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 187

A Complete Application with RPC Communications

Figure 5.12 Applying the vertical box with divider VDividedBox

Each click on a wedge of the pieChart will generate an itemClick event (the sole parameter of this event will be of type CharItemEvent) that will include the attribute event.hitData.item correspond-ing to the selected item (an XML object) in the underlying data provider. That’s why, to populate the variable selectedSecurity we can write:

<mx:PieChart id=”portfolioPie” itemClick=”selectedSecurity=event.hitData.item.

Symbol …”/>

In the case of the DataGrid, we’ll be intercepting the change event, which is dispatched when the selectedIndex or selectedItem property changes as a result of the user’s actions. Note the selectable=”true” Listng 5.22. This is done to enable highlighting of the selected row.

The following fragment from PortfolioView5.mxml shows the changes/additions that we’ve made to PortfolioView4. A complete listing is included with the source code in the book samples.

<mx:Canvas xmlns:mx=”http://www.adobe.com/2006/mxml” width=”100%” height=”100%” >

Page 199: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

188 RIA WITH ADOBE FLEX AND JAVA

… <mx:DataGrid id=”portfolioGrid” dataProvider=”{portfolioModel.security}”

selectable=”true” change=”selectedSecurity=portfolioGrid.selectedItem.Symbol”>

</mx:DataGrid>

<mx:PieChart id=”portfolioPie” itemClick=”portfolioPieOnClick(event)”

dataProvider=”{portfolioModel.security}” showDataTips=”true” >

</mx:PieChart>

<mx:Script><![CDATA[

import mx.controls.Alert;

import mx.rpc.events.FaultEvent;

[Bindable]

public var selectedSecurity:String;

import mx.charts.events.*;

private function portfolioPieOnClick(event:ChartItemEvent):void {

selectedSecurity=event.hitData.item.Symbol;

}

]]></mx:Script>

</mx:Canvas>

Listing 5.22 Setting up selectedSecurity for binding

Finally, to add a visual effect of the selected security in the pie we will explode the clicked wedge a bit more (see Figure 5.11):

private function portfolioPieOnClick(event:ChartItemEvent):void {

selectedSecurity=event.hitData.item.Symbol;

var currentWedgeIndex:int = event.hitData.index;

var perWedgeExplosion:Array = [];

for (var i:int=0; i<portfolioPie.dataProvider.length; i++) {

perWedgeExplosion[i] = (i==currentWedgeIndex)?10:2;

}

portfolioPie.series[0].perWedgeExplodeRadius = perWedgeExplosion;

}

Listing 5.23 Exploding the pie wedge

This concludes our series of Stock Portfolio applications based on Flex remoting.

CHAPTER 5

Page 200: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 189

A Complete Application with RPC Communications

Adding the JMS Feed to the Stock PortfolioIn the second part of this chapter we will reuse the POJO that we used to remote to – Portfolio.java (Listing 5.6). This time, however, we will invoke it from inside a Java program, which will publish the quotes to a topic available to the Flex application. But first, let’s take a look at the Java tech-niques we will be relying on.

Introduction to the Java Naming and Directory Interface The Flex applications in the second part of our chapter will be subscribing to the messages pub-lished to a certain destination by the JMS-based Java program; hence the Flex Data Services have to be able to find the proper destination. In the Java world, the location of the objects is facilitated by JNDI, which exposes and locates objects via their names. JNDI decouples the physical implemen-tation of the naming services from the client API.

JNDI has one or more contexts, which comprise a naming tree similar to directories/sub-directo-ries in a PC file system. The “root directory” in JNDI vocabulary is called the initial context. The Tomcat server that we will use in our illustration supports it own JNDI tree. So does the ActiveMQ implementation of the JMS that we will use to complement Tomcat. (When this was written Ac-tiveMQ was available under the Apache 2.0 license at http://www.activemq.org/.)

Let’s consider an example that shows how to obtain and use the appropriate naming context.

A client program JndiExample (Listing 5.10) creates an instance of the InitialContext class passing it hard-coded properties relevant to the specific JNDI provider. In particular, the settings of tcp://localhost:61616 as the value of PROVIDER_URL and ActiveMQInitialContextFactory as the value of the INITIAL_CONTEXT_FACTORY are unique to ActiveMQ. (There is an alternative technique for keeping these settings in the jndi.properties on the execution classpath, but it is entirely up to the JNDI provider to make use of this file.):

public class JndiExample {

public static void main(String[] args) {

Context ctx = null;

try {

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,

“org.apache.activemq.jndi.ActiveMQInitialContextFactory”);

env.put(Context.PROVIDER_URL, “ tcp://localhost:61616”);

env.put(Context.SECURITY_PRINCIPAL, “admin”);

env.put(Context.SECURITY_CREDENTIALS , “admin”);

ctx = new InitialContext(env);

System.out.println(“ActiveMQ initial context is obtained”);

Page 201: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

190 RIA WITH ADOBE FLEX AND JAVA

// Now you can lookup for JMS objects, i.e. topic connection factory

// TopicConnectionFactory factory = (TopicConnectionFactory)

// ctx.lookup(“jms/flex/TopicConnectionFactory”);

} catch(Excaption e){…}

}

}

Listing 5.24 A sample JNDI program

In this program we assume that the ActiveMQ creates the JNDI tree and binds TopicConnectionFac-tory some time prior to the name lookup. Your program also can programmatically bind the object to the JNDI tree, provided you have enough access privileges to run the following line of code:

ctx.bind(someName, someObject);

This concludes our short introduction to JNDI, but you can find a more detailed JNDI tutorial at http://java.sun.com/products/jndi/tutorial.

Introduction to the Java Messaging Service People are sending messages to each other via e-mail and applications can send messages to each other using Message-Oriented-Middleware (MOM). One of the major advantages of MOM is that it provides asynchronous communication between the applications. It’s somewhat similar to e-mail operations – you don’t have to be online when someone sends you a message – you can read it later.

JMS is the standard API to work with MOM. JMS does not transport messages. JMS-to-MOM is the same as JDBC-to-a-database management system. Java applications could use the same JMS classes with any MOM vendor.

Two Modes of Message DeliveryA program could either send a message to the queue or publish a message to the topic. The first sce-nario is called Point-to-Point (P2P) messaging. In this mode a message is deleted from the queue as soon as it is successfully received. The second scenario is called Publish/Subscribe (Pub/Sub) messag-ing. With Pub/Sub many recipients can get the same message as long as they stay subscribed to it.

In this chapter we’ll use the Pub/Sub mode for publishing the stock quotes. Do we need to persist our messages so the subscribers who are not online at the moment can eventually get them? Ap-parently not, since it does not make sense to store the price of MSFT as of 11:53:59a.m. and show it an hour later when the subscriber decides to go online. So our subscribers (and topics) will be non-durable. Do we need to guarantee the successful delivery of each and every quote, given their frequency? Again, the answer is, most likely, no. So our publishing will not be transacted.

CHAPTER 5

Page 202: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 191

A Complete Application with RPC Communications

JMS Classes and TermsBelow are the names and a short description of the relevant JMS classes: • TopicPublisher: An object that publishes messages to topics• TopicSubscriber: An object that receives messages• Topic: An object used to store messages in the Pub/Sub mode• TopicPublisher: An object that publishes messages to a topic• Message: An wrapper object that contains some data; it can be put in a queue or published to

a topic

Types of MessagesEvery message created by the JMS provider contains a header, a body (aka payload), and properties. The header contains standard message identification such as message ID, destination, etc. Proper-ties and body are optional.

In a typical scenario, properties – name/value pairs – can be used as a “mark” to filter messages by business purpose. Accordingly, body is best suited to deliver the content. JMS offers the following message types:

• TextMessage: This could be any Java String (any text, CVS, XML, etc.)• ObjectMessage: This could be any serializable Java object• BytesMessage: An array of bytes• StreamMessage: A stream of Java primitives for sequential reading• MapMessage: Any key/value pair, for example, id=123• Message: A message that contains the header and maybe properties, but no body

When you’re configuring Flex messaging destinations specific to JMS, keep in mind that at the time of writting, Flex only supported TextMessage and ObjectMessage types.

How to Publish a MessagePrograms publish messages to topics. For a given topic, multiple subscribers can get messages published in a “one-to-many” mode (as opposed to “one-to-someone” mode for a queue).

The snippet of code in Listing 5.25 illustrates the steps that will be included in our TickerFeed Java program: look up the topic factory, create the topic connection, session, and publisher and, finally, publish a serialized object:

// Lookup factory and create topic connection

TopicConnectionFactory factory = (TopicConnectionFactory) context.lookup(“topicConnectio

nFactory”);

Topic connection = factory.createTopicConnection();

// Create publisher session

boolean transacted = false;

Page 203: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

192 RIA WITH ADOBE FLEX AND JAVA

pubSession = connection.createTopicSession(transacted, Session.AUTO_ACKNOWLEDGE);

// Lookup topic

Topic topic = (Topic) context.lookup(“ticker”);

// Create a publisher

publisher = pubSession.createPublisher(topic);

ObjectMessage message = pubSession.createObjectMessage( mySerializableObject);

publisher.publish(message);

Listing 5.25 A code fragment of a message publisher

How to Subscribe for a TopicIn our scenario, the Java TickerFeed program will do the publishing and the Flex application, via the mx:Consumer tag, will act as the subscriber. The code in the section, therefore, is purely educa-tional. And yet, you may find it useful, particularly to unit-test your publisher without leaving the confines of the Java side of the wire.

Subscribers can be durable and non-durable. A durable topic subscriber gets all of the messages sent to a destination, including those sent while the consumer is inactive. A non-durable message consumer gets messages from its chosen destination only if the messages are available while the consumer is active. This mode is similar to the way the chat rooms operate – you must be online to get the messages. Please be aware that you cannot possibly create a durable subscriber on the non-durable transport. In the case of Flex, you’d have to declare your messaging destination (more on that later in this chapter) as durable.

The code snippet in Listing 5.26 creates a non-durable subscriber:

TopicConnectionFactory factory = (TopicConnectionFactory) context.lookup(“topicConnectionFa

ctory”);

TopicSession subSession = connection.createTopicSession(false,

Session.AUTO_ACKNOWLEDGE);

Topic topic = (Topic) ctx.lookup(“ticker”);

TopicSubscriber subscriber = subSession.createSubscriber(topic);

connection.start();

subscriber.setMessageListener(this);

public void onMessage(Message message) {

= // Work with your message here

// Cast it to your specific type, etc. ge”);

}

Listing 5.26 A code snippet of a non-durable subscriber

CHAPTER 5

Page 204: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 193

A Complete Application with RPC Communications

Now that we’ve touched on the subject of JMS, let’s look at the integration of JMS and native Flex messaging.

Integrating Flex and Java Messaging ServicesFlex provides a Messaging Service of its own, which enables messaging between many clients via a Flex server component. Unlike JMS, Flex Messaging Services are not simply an API, but a full imple-mentation as well. Flex Messaging Services in and of itself are completely suffi cient to establish messaging between clients in your enterprise application. Flex Messaging integrates with external messaging via an adapter architecture. In particular, Flex provides a specifi c adapter class for JMS. This adapter acts as a middleman between the two messaging components, translating message fl ows from the Java destinations to Flex ones and vice versa.

An important point here is that Flex and Java have separate message destinations. The mapping between the Flex destinations and external messaging destinations has to be confi gured in the XML confi guration fi le (under the default confi guration scenario – WEB-INF/fl ex/messaging-confi g.xml). Figure 5.12 shows an integration between Flex and Java Messaging using a JMS adapter.

Figure 5.12 A Flex-MOM integration

Flex clients can connect to servers using different transport protocols: the Real-Time Messaging Protocol ( RTMP) and the Action Message Format (AMF) that runs over HTTP (you’d need polling in the latter case). When you confi gure a destination in the messaging-confi g.xml, you can specify more than one channel and Flex will try to access the destination using the protocols in the order specifi ed. We’d like to emphasize that Flex clients just need to know the names of the FDS destina-tions. They are decoupled from MOM servers and JMS names. All features of the Flex messaging are described in the Flex manual called the Flex Developer’s Guide. Now we’ll get back to our stock portfolio application, but this time we’ll make it JMS-enabled.

Confi guring Flex Messaging DestinationLet’s start by confi guring the topic on the Flex side. We will modify the fi le messaging-confi g.xml in

Page 205: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

194 RIA WITH ADOBE FLEX AND JAVA

/WEB-INF/flex, and create the destination for the “ticker-topic-jms” as in Listing 5.27. Please note the difference between the destination ID “ticker-topic-jms,” which will be used by the Flex client, and the jndi-name “ticker,” which the Flex JMS adapter will use for look up in the JNDI tree. The middleman subscriber created by the Flex JMS adapter won’t be durable; it’s okay to lose the quote if you aren’t online.

<?xml version=”1.0” encoding=”UTF-8”?>

<service id=”message-service” class=”flex.messaging.services.MessageService”

messageTypes=”flex.messaging.messages.AsyncMessage”>

<adapters>

<adapter-definition id=”actionscript” class=”flex.messaging.services.messaging.adapters.

ActionScriptAdapter” default=”true” />

<adapter-definition id=”jms”

class=”flex.messaging.services.messaging.adapters.JMSAdapter”/>

</adapters>

<destination id=”ticker-topic-jms”>

<properties>

<server>

<durable>false</durable>

<durable-store-manager>flex.messaging.durability.FileStoreManager</durable-store-

manager>

</server>

<jms>

<destination-type>Topic</destination-type>

<message-type>javax.jms.ObjectMessage</message-type>

<connection-factory>topicConnectionFactory</connection-factory>

<destination-jndi-name>ticker</destination-jndi-name>

<destination-name>Ticker</destination-name>

<durable-consumers>false</durable-consumers>

<delivery-mode>NON_PERSISTENT</delivery-mode>

<message-priority>DEFAULT_PRIORITY</message-priority>

<acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>

<transacted-sessions>false</transacted-sessions>

<initial-context-environment>

<property>

<name>java.naming.factory.initial</name>

<value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</

value>

</property>

<property>

<name>java.naming.provider.url</name>

<value>tcp://localhost:61616</value>

</property>

CHAPTER 5

Page 206: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 195

A Complete Application with RPC Communications

</initial-context-environment>

</jms>

</properties>

<channels>

<channel ref=”my-rtmp”/>

<channel ref=”my-polling-amf”/>

</channels>

<adapter ref=”jms”/>

</destination>

</service>

Listing 5.27 A fragment of the flex-message-services.xml

Note the channel tags. The RTMP channel is listed first, and the AMF polling is the second option. This means the following: try the push first, but if it doesn’t work (because of firewalls), start polling.

Configuring ActiveMQ JMSThere are different ways to start ActiveMQ. For purposes of this chapter it’s convenient to start ActiveMQ with the same process that’s running the Tomcat when the Web application starts. Ac-cordingly, we use a Web application context listener and start the ActiveMQ broker from the con-textInitialized() method:

public void contextInitialized(ServletContextEvent arg0) {

try{

broker.addConnector(“tcp://localhost:61616?trace=true”);

broker.start();

}catch(Exception e){

e.printStackTrace();

throw new RuntimeException(e);

}

}

Naturally, we have to guarantee the availability of the ActiveMQ classes on the Web application class path. To that end we dropped activemq-4.0-M4.jar into the common/lib folder off the Tomcat root. Here is the full listing of the application listener:

package com.theriabook.jms;

import javax.servlet.ServletContextEvent;

import javax.servlet.ServletContextListener;

import org.apache.activemq.broker.BrokerService;

Page 207: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

196 RIA WITH ADOBE FLEX AND JAVA

public class ActiveMQBrokerListener implements ServletContextListener {

BrokerService broker = new BrokerService();

public void contextInitialized(ServletContextEvent arg0) {

try{

broker.addConnector(“tcp://localhost:61616?trace=true”);

broker.start();

}catch(Exception e){

e.printStackTrace();

throw new RuntimeException(e);

}

}

public void contextDestroyed(ServletContextEvent arg0) {

try{

broker.stop();

}catch(Exception e){

e.printStackTrace();

throw new RuntimeException(e);

}

}

}

Listing 5.28 Message Broker for Web application server

To register the listener with the Web application we modify the web.xml file and add the following XML snippet (according to the DTD it should go right after the filter mappings. If your app does not have a filter, the placement is right after the application context parameters):

<listener>

<listener-class>com.theriabook.jms.ActiveMQBrokerListener</listener-class>

</listener>

Writing the TickerFeed Java Program In our data feeding program the quotes will be published to a JMS topic, but we are still going to reuse the class Porfolio.java, which we used to pull quotes via the RemoteObject. When it comes to sending the message, we will be putting the return of the Portofolio getQuotes() method into the serializable QuotesHolder:

Portfolio port = new Portfolio();

. . . .

StockQuoteDTO[] quotes = port.getQuotes();

CHAPTER 5

Page 208: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 197

A Complete Application with RPC Communications

QuotesHolder holder = new QuotesHolder();

The listing for the QuotesHolder class is below:

package com.theriabook.jms;

import java.io.Serializable;

import com.theriabook.jms.dto.StockQuoteDTO;

public class QuotesHolder implements Serializable{

private static final long serialVersionUID = -8823588238987890758L;

public StockQuoteDTO[] quotes;

}

Listing 5.29 The quotes wrapper QuotesHolder.java

To avoid hard-coding the JNDI properties in the TickerFeed program, we will make sure that the file jndi.properties as shown in Listing 5.30 belongs to the application classpath:

#jndi.properties for TickerFeed program running against ActiveMQ

#

java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory

# use the following property to configure the default connector

java.naming.provider.url = tcp://localhost:61616

# use the following property to specify the JNDI name the connection factory

connectionFactoryNames = connectionFactory, queueConnectionFactory, topicConnectionFac-

tory

# register some topics in JNDI using the form

# topic.[jndiName] = [physicalName]

topic.ticker = chat.TicketTopicListing 5.30 Content of jndi.properties for the TickerFeed program

Now we’re ready to complete our TickerFeed.java program. It continuously publishes the Quotes-Holder objects to the JMS topic called “ticker.” On your console you should see output similar to this one:

Publishing quotes... Publishing quotes...

The complete code for TickerFeed.java is in Listing 5.31:

// TickerFeed.java

Page 209: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

198 RIA WITH ADOBE FLEX AND JAVA

package com.theriabook.jms;

import com.theriabook.ro.Portfolio;

import java.util.*;

import javax.jms.*;

import javax.naming.*;

import com.theriabook.jms.dto.StockQuoteDTO;

public class TickerFeed {

public static void main(String args[]) {

TopicSession pubSession;

TopicPublisher publisher;

TopicConnection connection;

try {

// Obtain JNDI Context

Context context = new InitialContext();

// Lookup a JMS connection factory

TopicConnectionFactory factory = (TopicConnectionFactory) context.lookup(“topicCon

nectionFactory”);

// Create a JMS connection

connection = factory.createTopicConnection();

// Create publisher session

boolean transacted = false;

pubSession = connection.createTopicSession(transacted, Session.AUTO_ACKNOWLEDGE);

// or lookup a JMS topic

Topic topic = (Topic) context.lookup(“ticker”);

// Create a publisher and a subscriber

publisher = pubSession.createPublisher(topic);

Portfolio port = new Portfolio();

while (true) {

StockQuoteDTO[] quotes = port.getQuotes();

QuotesHolder holder = new QuotesHolder();

holder.quotes = quotes;

Thread.sleep(1000); // just to slow down the feed

CHAPTER 5

Page 210: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 199

A Complete Application with RPC Communications

ObjectMessage message = pubSession.createObjectMessage( holder);

publisher.publish(message);

System.out.println(“Publishing quotes...” );

}

} catch (InterruptedException e) {

e.printStackTrace();

} catch (NamingException e) {

e.printStackTrace();

} catch (JMSException e) {

e.printStackTrace();

}

}

}

Listing 5.31 The marked datafeed – TickerFeed.java

Modifying the Flex Client to Consume MessagesThe final part is to make changes to the Flex client. We will use the last version of PortfolioView from the remoting series and make the following modifications.

First, we will replace the <mx:RemoteObject> tag with the following <mx:Consumer> tag:

<mx:Consumer id=”consumer” destination=”ticker-topic-jms” message=”applyQuotes(eve

nt.message.body.quotes)” />

This is an example of decoupling the application components. If you need to change the MOM provider, or you decide to use message queues instead of topics, no code modification is needed. Just change the destination parameter and you’re set.

The second change is to actually start consuming messages. The modified startQuotes will look as follows:

private function startQuotes():void{

consumer.subscribe();

}

The third and last change is in the processFault method, due to the differences in mx.messaging.events.ChannelFaultEvent instead of mx.rpc.events.FaultEvent:

private function processFault(evt:ChannelFaultEvent):void{

errorText = “Server Error”;

errorTip = evt.faultDetail;

Page 211: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

200 RIA WITH ADOBE FLEX AND JAVA

startQuotes();

}

In all other respects the JMS code specific to the PortfolioView is the same as in its remoting coun-terpart:

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

<!—PortflolioViewJMS.mxml ‡

<mx:Canvas xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*”

width=”100%” height=”100%” creationComplete=”startQuotes();” >

<mx:XML format=”e4x” id=”portfolioModel” source=”portfolio.xml” />

<mx:Panel width=”100%” height=”100%” title=”Portfolio”>

<mx:ViewStack id=”vs” width=”100%” height=”100%”>

<mx:VBox label=”Show Grid” icon=”{iconGrid}” >

<mx:DataGrid id=”portfolioGrid” width=”100%” height=”100%”

dataProvider=”{portfolioModel.security}”

change=”selectedSecurity = portfolioGrid.selectedItem.Symbol;”>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”Symbol”/>

<mx:DataGridColumn dataField=”Quantity” textAlign=”right”/>

<mx:DataGridColumn dataField=”Price” textAlign=”right”/>

<mx:DataGridColumn dataField=”Value” textAlign=”right”/>

</mx:Array></mx:columns>

</mx:DataGrid>

</mx:VBox>

<mx:HBox label=”Show Chart” icon=”{iconChart}” horizontalAlign=”center”

verticalAlign=”middle”>

<mx:PieChart id=”portfolioPie” dataProvider=”{portfolioModel.security}”

showDataTips=”true”

itemClick=”selectedSecurity=event.hitData.item.Symbol” height=”90%”>

<mx:series><mx:Array>

<mx:PieSeries labelPosition=”callout” field=”Value” labelFunction=”showP

osition” nameField=”Symbol”

explodeRadius=”2”/>

</mx:Array>

</mx:series>

</mx:PieChart>

<mx:Legend verticalAlign=”middle” dataProvider=”{portfolioPie}” label=”{data.Sym-

bol}”/>

</mx:HBox>

</mx:ViewStack>

</mx:Panel>

<mx:HBox horizontalAlign=”right” width=”98%” >

<mx:Label color=”red” toolTip=”{errorTip}” text=”{errorText}” width=”100”/>

CHAPTER 5

Page 212: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 201

A Complete Application with RPC Communications

<mx:ToggleButtonBar dataProvider=”{vs}” paddingTop=”4” />

</mx:HBox>

<mx:Consumer id=”consumer” destination=”ticker-topic-jms” message=”applyQuotes(event.

message.body.quotes)”

channelFault=”processFault(event)” />

<mx:Script><![CDATA[

import mx.controls.Alert;

[Bindable] public var selectedSecurity:String;

private function showPosition(data:Object, field:String, index:Number, percentVal

ue:Number):String {

return data.Symbol + “\n” + “Shares:” + data.Quantity + “\n” + “Price:” +

data.Price + “\n” + “Value:” + data.Value ;

}

[Embed(source=”./images/icon_chart.png”)] [Bindable] public var iconChart :

Class;

[Embed(source=”./images/icon_grid.png”)] [Bindable] public var iconGrid :

Class;

import com.theriabook.jms.dto.StockQuoteDTO;

internal var row:XML;

[Bindable]

private var errorText:String=””;

[Bindable]

private var errorTip:String=””;

import mx.messaging.events.ChannelFaultEvent;

private function processFault(evt:ChannelFaultEvent):void{

errorText = “Server Error”;

errorTip = evt.faultDetail;

startQuotes();

}

private function applyQuotes(quotes: Array):void {

errorText = “”;

errorTip = “”;

for (var i:int=0; i<quotes.length; i++) {

var quote:StockQuoteDTO = StockQuoteDTO(quotes[i]);

var list: XMLList = portfolioModel.security.(Symbol==quote.symbol);

if (list.length()!=0) {

var row:XML = XML(list);

row.Price = Math.round(100*quote.last)/100;

row.Value = Math.round(row.Price * row.Quantity);

}

}

}

Page 213: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

202 RIA WITH ADOBE FLEX AND JAVA

internal var quote:StockQuoteDTO = null;

private function startQuotes():void{

consumer.subscribe();

}

]]></mx:Script>

</mx:Canvas>

Listing 5.32 The JMS-based version of PortfolioView

SummaryIn this chapter we went through different ways of establishing RPC communications between Flex and Java. In particular, we’ve been using RemoteObject, HTTPService, and Flex JMS adapters to establish communications.

CHAPTER 5

Page 214: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 205

End-to-End Rapid Application Development with Flex Data

Management Services

6CHAPTER

Page 215: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

206 RIA WITH ADOBE FLEX AND JAVA

Flex Data Management Services: Flex Remoting on SteroidsFlex Data Services include Flex Remoting and Flex Data Management Services. We have already illustrated Remoting in Chapter 5. For the purposes of this chapter, dedicated to Data Manage-ment Services, we will be referring to them as DS, omitting the “Management” part. Thus herein DS should be treated in a “narrow” context, as the counterpart of Flex Remoting and not as Flex Data Services, which encompass both components.

The simplest way to explain Data Management Services (DS) is to compare them with Remoting. Whereas Flex Remoting enables one-way requests, FDS combines one-way requests with the pub-lish/subscribe mechanism so that besides the original result set DS sends the client live updates produced by other clients of the same destination. And there’s one more dimension in which Data Services depart from Flex Remoting – support for hierarchical collections, but we won’t be covering that subject in this book.

In other words, DS resolves the task of programming data collaboration: Several users may edit different rows and columns of the “same” DataGrid and see each other’s changes automatically pushed by the server. Now, what if they overlap each other’s work? In terms of DS that’s called a conflict and the DS API provides for flexible conflict resolution, which may require the user’s intervention.

A DS destination can be configured for working with the data that is persisted to a data store as well as supporting scenarios that persist the data in the server’s memory. To that end, FDS provides Java and ActionScript data adapters that are responsible for reading and updating a persistent data store according to its type. In this chapter we’ll focus on use cases involving Java adapters.

Flex Data Services and Automation: Problem Statement and SolutionRobust in enabling collaborative manipulation of data, DS demand a substantial development ef-fort in case of persistent data stores. In particular, you need to build:

• A Java Data Access Object class that implements retrieve, update, delete, and insert of the data• Java Data Transfer Objects (DTO)

CHAPTER 6

End-to-End Rapid Application Development with Flex Data Management Services

Page 216: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 207

• A matching ActionScript data transfer object class• A configuration file, which registers identity columns of the result set and, optionally, argu-

ment types for every retrieval method and other parameters

We just mentioned four classes/files containing hard-coded names of the fields and there are more. To function properly, these hard-coded values have to be kept in sync, which is an additional main-tenance effort whenever the data structures change.

Instead of this complexity, the main idea of this chapter is not to cover every twist of the DS API, but rather automate the development effort that DS takes for granted. We’ll start with a “manual,” albeit simplified, example of using DataServices. Then we’ll introduce you to the methodology of complete code generation based on the pre-written XSL templates and DS-friendly XML metadata, which will be extracted from the annotated Java abstract classes.

This methodology is fully implemented in DAOFlex – an open source utility that’s a complemen-tary addition to this book. We’ll gradually introduce this tool by leading you through a process of creating the most comprehensive template that generates a complete DataServices Data Access Object (DAO). Finally, we’ll show you how to run and customize DAOFlex in your development environment so that writing and synchronizing routine DataServices support classes becomes a task of the Ant building tool and not yours!

A “Manual” FDS ApplicationLet’s handcraft the application presented in Figure 6.1. This application displays a Panel with a scrollable DataGrid that we consciously did not size in the horizontal dimension, so that all col-umns can be viewed without shrinking. The database result set is ultimately produced by the fol-lowing SQL query that will use a bound variable in place of the question mark:

select * from employee where start_date < ?

There are two buttons below the DataGrid: Fill and Commit. As the names imply, these buttons pull the original data from the database table and submit the data changes back to an DS destination. A separate Parameters panel permits entering parameters of the back-end method behind the Fill button, which, in our case, is the employee start date :

End-to-End Rapid Application Development

Page 217: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

208 RIA WITH ADOBE FLEX AND JAVA

Figure 6.1 Stock portfolio screen – the show grid view

Building the Client ApplicationLet’s build the client application first. The full listing of the application is presented in Listing 6.1. We start with defining the mx:DataServices object (aka ds), which points to the destination “Em-ployee.” Later, when we get to the server components, we’ll discuss mapping this destination to the backing Java class: <mx:DataService id=”ds” destination=”Employee” fault=”onFault(event)” />

We provide only a rudimentary handler of the fault event, that’s sufficient to keep us aware of any anomalies that may occur along the way. Dynamic referencing of fault and faultString properties will spare us from casting to a specific event:

private function onFault(evt:Event):void {

Alert.show(evt[“fault”][“faultString”], “Fault”);

}

Then we define a handler of the application’s onCreationComplete event, where we instantiate a collection to be eventually associated with our mx:DataService object and, most important, set both autoCommit and autoSyncEnabled of the ds to false:

End-to-End Rapid Application Development CHAPTER 6

Page 218: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 209

private function onCreationComplete() : void {

collection = new ArrayCollection();

ds.autoCommit=false;

ds.autoSyncEnabled=false;

}

By setting autoCommit to false we state that all updates have to be batched and explicitly submit-ted to the server as a single transaction during the ds.commit() call. By setting autoSyncEnabled to false we effectively protect our local instance of data from the delivery of messages caused by other clients connected to destination “Employee.” Setting autoSyncEnabled to false is entirely op-tional, and we use it to avoid dealing with application specific conflict resolution. In particular, in the handler of the Commit button’s click event you might uncomment the first line to support the “optimistic” way of handling the conflicts:

private function commit_onClick():void {

//ds.conflicts.acceptAllClient(); // Optimistic conflict handling, as oppose

to ds.conflicts.acceptAllServer();

ds.commit();

}

Last, we have to initiate the population of the local collection with the ds.fill() method, which we do inside the click event handler of the button Fill:

private function fill_onClick():void {

ds.release();

ds.fill(collection, param_getEmployees_startDate.selectedDate);

}

The scripting portion of the application is completed so let’s build the UI. We create a DataGrid with the dataProvider bound to our collection in Listing 6.1. For brevity’s sake, we didn’t list all the columns here: you’ll have a chance to scrutinize them in the subsequent section of this chapter. The DataGrid and ControlBar with Fill and Commit buttons are put inside a Panel, with DataGrid’s title bearing the name of the destination and a specific getEmployees method of that destination, which will ultimately be invoked during the ds.fill() call. The second panel, titled Parameters, con-tains a form with a single item mx:DateField. Both panels are embraced by the VDividedBox.

We’ve included a linkage variable of the data transfer type to ensure that the corresponding Action-Script class (EmployeeDTO) will be linked into the generated SWF file.

<?xml version=”1.0” encoding=”UTF-8”?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” backgroundColor=”#FFFFFF”

creationComplete=”onCreationComplete()”>

<mx:DataService id=”ds” destination=”Employee” fault=”onFault(event)” />

<mx:VDividedBox width=”800” height=”100%”>

End-to-End Rapid Application Development CHAPTER 6

Page 219: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

210 RIA WITH ADOBE FLEX AND JAVA

<mx:Panel title=”Employee::getEmployees()” width=”800” height=”70%”>

<mx:DataGrid id=”dg” dataProvider=”{collection}” editable=”true” height=”100%”>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”EMP_ID” headerText=”Emp Id” />

<mx:DataGridColumn dataField=”MANAGER_ID” headerText=”Manager Id” />

<mx:DataGridColumn dataField=”EMP_FNAME” headerText=”Emp Fname” />

. . . .

</mx:Array></mx:columns>

</mx:DataGrid>

<mx:ControlBar>

<mx:Button label=”Fill” click=”fill_onClick()”/>

<mx:Button label=”Commit” click=”commit_onClick()” enabled=”{ds.commitRe

quired}”/>

</mx:ControlBar>

</mx:Panel>

<mx:Panel title=”Parameters” width=”100%” height=”30%”>

<mx:HBox height=”100%” width=”100%”>

<mx:Form label=”getEmployees()”>

<mx:FormItem label=”startDate:”>

<mx:DateField id=”param_getEmployees_startDate” selectedDate=”{new

Date()}”/>

</mx:FormItem>

</mx:Form>

</mx:HBox>

</mx:Panel>

</mx:VDividedBox>

<mx:Script>

<![CDATA[

import mx.controls.Alert;

import mx.collections.ArrayCollection;

import com.theriabook.datasource.dto.EmployeeDTO;

private var linkage:com.theriabook.datasource.dto.EmployeeDTO = null;

[Bindable]

private var collection : ArrayCollection;

private function fill_onClick():void {

ds.release();

ds.fill(collection, param_getEmployees_startDate.selectedDate);

}

private function onCreationComplete() : void {

collection = new ArrayCollection();

ds.autoCommit=false;

ds.autoSyncEnabled=false;

}

private function commit_onClick():void {

End-to-End Rapid Application Development CHAPTER 6

Page 220: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 211

ds.conflicts.acceptAllClient();

ds.commit();

}

private function onFault(evt:Event):void {

Alert.show(evt[“fault”][“faultString”], “Fault”);

}

]]>

</mx:Script>

</mx:Application>

Listing 6.1 The handcrafted DataServices sample application

The application above doesn’t cover all use cases of the DS API. We tried to keep it as small as pos-sible for one reason: to enable metadata-based code generation. Ultimately, it will be entirely up to you which code you’d elect to generate by modifying the DAOFlex templates.

Finally, we present the listing of the ActionScript class EmployeeDTO that our collection uses in communicating with the Employee destination:

package com.theriabook.datasource.dto

{

[Managed]

[RemoteClass(alias=”com.theriabook.datasource.dto.EmployeeDTO”)]

public dynamic class EmployeeDTO

{

// Properties

public var EMP_ID : Number;

public var MANAGER_ID : Number;

public var EMP_FNAME : String;

public var EMP_LNAME : String;

public var DEPT_ID : Number;

public var STREET : String;

public var CITY : String;

public var STATE : String;

public var ZIP_CODE : String;

public var PHONE : String;

public var STATUS : String;

public var SS_NUMBER : String;

public var SALARY : Number;

public var START_DATE : Date;

public var TERMINATION_DATE : Date;

End-to-End Rapid Application Development CHAPTER 6

Page 221: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

212 RIA WITH ADOBE FLEX AND JAVA

public var BIRTH_DATE : Date;

public var BENE_HEALTH_INS : String;

public var BENE_LIFE_INS : String;

public var BENE_DAY_CARE : String;

public var SEX : String;

public function EmployeeDTO() {

}

} //EmployeeDTO

}

Listing 6.2 The ActionScript DTO class – EmployeeDTO

Creating Assembler and DTO ClassesThe time has come to work on the Java side, which is a rather tedious process, so we’ll gradually go top-down.

Our first stop is an Assembler class that the DS Employee destination should map to. As the Flex documentation suggests, you can implement the methods on your Assembler class in several ways:

• Extend flex.data.assemblers.AbstractAssembler and override the fill(), getItem(), createItem(), updateItem(), and deleteItem() methods as needed.

• Configure these methods via XML definitions against a class that doesn’t extend the AbstractAs-sembler class.

• Combined approach, where methods defined via XML declarations are used if defined, other-wise the AbstractAssembler methods are invoked.

We’ll take an XML approach that lets us declare a so-called sync-method. The XML contract of the des-tination’s sync-method prescribes that it accepts a single parameter: a List of flex.data.ChangeObject elements. We find it convenient to control how we want to process data changes. In particular, we’d like to maintain the following order: all deletes, then all updates, and then all inserts. After all, if the user deletes a record for an employee with EMP_ID= 123 and then inserts a new record with EMP_ID=123, we certainly wouldn’t want our sync-method to issue the INSERT, followed by DELETE FROM employee WHERE EMP_ID=123 during the batched DS data modifications.

Let’s keep in mind that our ultimate focus is the metadata-based code generation. Should you de-cide to have your Assemblers as descendants of the AbstractAssembler, you’d have the liberty of modifying the corresponding DAOFlex template.

Listing 6.3 presents the complete XML describing the destination Employee. Under the default configuration scenario, this XML would go inside the <services> node of the flex-data-services.xml file, located in the WEB-INF/lib/flex folder of your Web application.

End-to-End Rapid Application Development CHAPTER 6

Page 222: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 213

We set com.theriabook.datasource.EmployeeAssembler as the exact name of the class mapped by our destination, with the methods java.util.List getEmployees_fill(java.util.Date dt) and the List getEmployees_sync(List lst) acting as the fill and sync methods, respectively.

You’d be able to configure more than one fill-method, although all of them should operate with the same type of DTO. In the <metadata> node we specified that the EMP_ID property of the DTO has to be considered as a single key, or identity property of the elements distributed by the destination. You could use a generated Universal Unique Identifier (UUID) instead of the real data-store field in place of the identity, which is arguably more flexible, because DS didn’t support updates to the identity fields when this piece was written.

Even though XML doesn’t explicitly declare that the fill-method returns a List or that the sync-method takes a List, this is a part of the XML contract for Assembler classes in destina-tions:

<destination id=”Employee”>

<adapter ref=”java-dao”/>

<properties>

<source>com.theriabook.datasource.EmployeeAssembler</source>

<scope>application</scope>

<metadata>

<identity property=”EMP_ID”/>

</metadata>

<network>

<session-timeout>0</session-timeout>

<paging enabled=”false”/>

<throttle-inbound policy=”ERROR” max-frequency=”500”/>

<throttle-outbound policy=”ERROR” max-frequency=”500”/>

</network>

<server>

<fill-method>

<name>getEmployees_fill</name>

<params>java.util.Date</params>

</fill-method>

<sync-method>

<name>getEmployees_sync</name>

</sync-method>

</server>

</properties>

</destination>

Listing 6.3 The destination Employee for flex-data-services.xml

End-to-End Rapid Application Development CHAPTER 6

Page 223: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

214 RIA WITH ADOBE FLEX AND JAVA

The structure of the EmployeeAssembler Java class is pretty straightforward. This class delegates the actual data retrieval and update of the data store to the EmployeeDataServiceDAO class, which we’ll discuss next:

package com.theriabook.datasource;

import java.util.*;

public final class EmployeeAssembler

{

public EmployeeAssembler()

{ }

public final List getEmployees_fill(Date startDate) {

return new EmployeeDataServiceDAO().getEmployees(startDate);

}

public final List getEmployees_sync(List items) {

return new EmployeeDataServiceDAO().getEmployees_sync(items);

}

}

Listing 6.4 EmployeeAssembler.java

Finally, here’s the EmployeeDTO class that the EmployeeAssembler-based destination will be op-erating with. It offers a simplistic approach to UUID generation that has to be replaced by a UUID generator of your choice:

package com.theriabook.datasource.dto;

import java.io.Serializable;

public class EmployeeDTO implements Serializable

{

private static final long serialVersionUID = 1L;

public int EMP_ID;

public int MANAGER_ID;

public String EMP_FNAME;

public String EMP_LNAME;

public int DEPT_ID;

public String STREET;

public String CITY;

public String STATE;

public String ZIP_CODE;

End-to-End Rapid Application Development CHAPTER 6

Page 224: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 215

public String PHONE;

public String STATUS;

public String SS_NUMBER;

public double SALARY;

public java.util.Date START_DATE;

public java.util.Date TERMINATION_DATE;

public java.util.Date BIRTH_DATE;

public String BENE_HEALTH_INS;

public String BENE_LIFE_INS;

public String BENE_DAY_CARE;

public String SEX;

public String DEPT_NAME;

private String _uid;

private static long _UID = 1L;

public EmployeeDTO() {

_uid = getUUID();

}

public String getUid() {

return _uid;

}

public void setUid(String value) {

_uid = value;

}

public static synchronized String getUUID() {

return “” + _UID++;

}

}

Listing 6.5 Listing of the Java DTO class – EmployeeDTO

Implementing the Fill-Method of the DataServices Data Access ObjectOur next stop is the EmployeeDataServicesDAO class, which is responsible for actual data manipu-lation in the data store. This section will cover its fill-method, and the sync-method will be covered next. We outsource the utility functions of getting a JDBC connection, converting values from java.util.Date to java.sql.Date and vice versa to a handful of classes from the com.theriabook.DAOFlex package, which takes less than 200 lines of source code. For brevity’s sake we’ll omit the listings of these classes; you can find the complete source code in the DVDD accompanying the book (look for the folder TheRiaBook/tools/DaoFlex/dist/runtime/src).

End-to-End Rapid Application Development CHAPTER 6

Page 225: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

216 RIA WITH ADOBE FLEX AND JAVA

The code for the fill portion of the EmployeeDataServiceDAO is presented in Listing 6.6. By wrap-ping any Throwable into com.theriabook.DAOFlex.DAOException, a descendant of RuntimeExcep-tion, we avoid unnecessary throws in both the DAO and Assembler implementation, since fatal exceptions will bubble up to the Flex framework classes and show up on the client side as a Da-taServices fault event. Other than that, most of this code is generic, which is precisely why it’s an excellent candidate for template-based code-generation:

public final List /*EmployeeDTO[]*/ getEmployees(java.util.Date startDate){

String sql = “select * from employee where start_date < ?”;

ArrayList list = new ArrayList();

PreparedStatement stmt = null;

ResultSet rs = null;

Connection conn = null;

try{

conn = JDBCConnection.getConnection(“jdbc/theriabook”);

stmt = conn.prepareStatement(sql);

stmt.setDate(1, DateTimeConversion.toSqlDate(startDate));

rs = stmt.executeQuery();

while( rs.next() )

{

EmployeeDTO dto = new EmployeeDTO();

dto.EMP_ID = (rs.getInt(“EMP_ID”));

dto.MANAGER_ID = (rs.getInt(“MANAGER_ID”));

dto.EMP_FNAME = (rs.getString(“EMP_FNAME”));

dto.BIRTH_DATE = DateTimeConversion.toUtilDate(rs.getDate(“BIRTH_DATE”));

. . . . . . . .

list.add(dto);

}

return list;

} catch(Throwable te) {

te.printStackTrace();

throw new DAOException(te);

} finally {

try {rs.close(); rs = null;} catch (Exception e){//your error logging code goes

here}

try {stmt.close(); stmt = null;} catch (Exception e){ //your error logging code

goes here }

JDBCConnection.releaseConnection(conn);

}

}

Listing 6.6 Fill-method of the EmployeeDataServiceDao

End-to-End Rapid Application Development CHAPTER 6

Page 226: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 217

Implementing the Sync-Method of FDS Data Access ObjectBy definition, the sync-method gets a List of flex.data.ChangeObject elements as an argument. A single ChangeObject can carry the information in an updated original record or, alternatively, a record that is supposed to be deleted or inserted. Since we want to process all changes as a single unit of work, we’ll iterate over the List three times: on the first pass we’ll pick and execute all deletes, then we’ll proceed to all updates, and finally we perform all inserts. This logic is presented in List-ing 6.7. Similar to the implementation of the getEmployees() above, our getEmployees_sync() throws only RuntimeExceptions:

public final List getEmployees_sync(List items)

{

Connection conn = null;

ChangeObject co = null;

try {

conn = JDBCConnection.getConnection(“jdbc/theriabook”);

Iterator iterator = items.iterator();

while (iterator.hasNext() ) { // Do all deletes first

co = (ChangeObject)iterator.next();

if(co.isDelete()) doDelete_getEmployees(conn, co);

}

iterator = items.iterator();

while (iterator.hasNext()) { // Perform updates

co = (ChangeObject)iterator.next();

if(co.isUpdate()) doUpdate_getEmployees(conn, co);

}

iterator = items.iterator();

while (iterator.hasNext()) { // Finish with inserts

co = (ChangeObject)iterator.next();

if (co.isCreate()) doCreate_getEmployees(conn, co);

}

} catch(DataSyncException dse) {

dse.printStackTrace();

throw dse;

} catch(Throwable te) {

te.printStackTrace();

throw new DAOException(te.getMessage(), te);

} finally {

if( conn!=null ) JDBCConnection.releaseConnection(conn);

}

return items;

}

Listing 6.7 Sync-method of the EmployeeDataServiceDao

End-to-End Rapid Application Development CHAPTER 6

Page 227: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

218 RIA WITH ADOBE FLEX AND JAVA

The next topic is the methods doUpdate…(), doDelete…() and doInsert…().

Implementing Update, Delete and Insert MethodsLet’s start with doUpdate_getEmployees(). Ultimately, we have to dynamically build a JDBC SQL string for the PreparedStatement. As an example, given a change of salary, phone number, and in-surance coverage, we need to build a string UPDATE EMPLOYEE SET SALARY=?, PHONE=?, BENE_HEALTH_INS=? WHERE EMP_ID=?. After that, we have to execute a preparedStatement.setXXX() for all parameters to substitute the question marks.

Conveniently, the creators of DS enabled the ChangedObject to return an array of all property names that underwent modification, which lets us build a SET clause by iterating over the array returned by the co.getChangedPropertyNames():

StringBuffer sql = new StringBuffer(“UPDATE EMPLOYEE SET “);

String [] names = co.getChangedPropertyNames();

for (int ii=0; ii < names.length; ii++) {

sql.append((ii!=0?”, “:””) + names[ii] +” = ? “);

}

Now let’s set the values for all the modified fields. Here we’ll take advantage of another function of the ChangeObject – getChangedValues(). This method returns a map of the new values and based on this map and the array names we can execute relevant setXXX() methods against our prepared statement:

Map values = co.getChangedValues();

int ii=0;

for (ii=0; ii < names.length; ii++) {

stmt.setObject( ii+1, values.get(names[ii]));

}

ii++;

To set the value of the WHERE clause-based parameter EMP_ID, we’ll use another ChangeObject’s method – getPreviousVersion(), which returns a copy of the old DTO:

stmt.setObject(ii++, co.getPreviousValue(“EMP_ID”));

The complete listing of doUpdate_getEmployees() is presented in Listing 6.8:

private void doUpdate_getEmployees(Connection conn, ChangeObject co) throws SQLExcep-

tion{

PreparedStatement stmt = null;

try {

StringBuffer sql = new StringBuffer(“UPDATE EMPLOYEE SET “);

String [] names = co.getChangedPropertyNames();

End-to-End Rapid Application Development CHAPTER 6

Page 228: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 219

for (int ii=0; ii < names.length; ii++) {

sql.append((ii!=0?”, “:””) + names[ii] +” = ? “);

}

sql.append( “ WHERE (EMP_ID=?)” );

stmt = conn.prepareStatement(sql.toString());

Map values = co.getChangedValues();

int ii=0;

for (ii=0; ii < names.length; ii++) {

stmt.setObject( ii+1, values.get(names[ii]));

}

ii++;

stmt.setObject(ii++, co.getPreviousValue(“EMP_ID”));

if (stmt.executeUpdate()==0) throw new DataSyncException(co);

} finally {

try { if( stmt!=null) stmt.close(); stmt = null;} catch (Exception e){}

}

}

Listing 6.8 Update section of the sync-method of EmployeeDataServiceDao

The implementation of the doDelete_getEmployees() is trivial and is presented in Listing 6.9:

private void doDelete_getEmployees(Connection conn, ChangeObject co) throws SQLExcep-

tion{

PreparedStatement stmt = null;

try {

StringBuffer sql = new StringBuffer(“DELETE FROM EMPLOYEE WHERE (EMP_ID=?)”);

stmt = conn.prepareStatement(sql.toString());

EmployeeDTO item = (EmployeeDTO) co.getPreviousVersion();

stmt.setInt(1, item.EMP_ID);

if (stmt.executeUpdate()==0) throw new DataSyncException(co);

} finally {

try { if( stmt!=null) stmt.close(); stmt = null;

} catch (Exception e){// error processing goes here}

}

}

Listing 6.9 The Delete section of the sync-method of the EmployeeDataServiceDao

End-to-End Rapid Application Development CHAPTER 6

Page 229: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

220 RIA WITH ADOBE FLEX AND JAVA

Implementation of the doCreate_getEmployees(), in turn, is based on the ChangeObject’s method getNewVersion(), which returns the copy of the new DTO:

EmployeeDTO item = (EmployeeDTO) co.getNewVersion();

Please note how we rely on Double.isNaN() to distinguish nulls from non-nulls (the alternative and, arguably, more reliable approach applicable to all nullable types would be to supply explicit null indicators as part of the DTO from ActionScript to Java and vice versa):

if (Double.isNaN(item.SALARY))

stmt.setNull(13,Types.DOUBLE);

else

stmt.setDouble(13, item.SALARY);

The abbreviated listing of doCreate_getEmployees() is presented in Listing 6.10:

private ChangeObject doCreate_getEmployees(Connection conn, ChangeObject co) throws

SQLException{

PreparedStatement stmt = null;

try {

String sql = “INSERT INTO EMPLOYEE “ +

“(EMP_ID,MANAGER_ID,EMP_FNAME,EMP_LNAME,DEPT_ID,STREET,CITY,STATE,ZIP_

CODE,PHONE,STATUS,SS_NUMBER,SALARY,START_DATE,TERMINATION_DATE,BIRTH_DATE,BENE_HEALTH_

INS,BENE_LIFE_INS,BENE_DAY_CARE,SEX)”+

“ values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)”;

stmt = conn.prepareStatement(sql);

EmployeeDTO item = (EmployeeDTO) co.getNewVersion();

stmt.setInt(1, item.EMP_ID);

. . . .

stmt.setString(12, item.SS_NUMBER);

if (Double.isNaN(item.SALARY))

stmt.setNull(13,Types.DOUBLE);

else

stmt.setDouble(13, item.SALARY);

stmt.setDate(14, DateTimeConversion.toSqlDate(item.START_DATE));

. . . .

if (stmt.executeUpdate()==0) throw new DAOException(“Failed inserting.”);

co.setNewVersion(item);

return co;

} finally {

try { if( stmt!=null) stmt.close(); stmt = null;} catch (Exception e){}

End-to-End Rapid Application Development CHAPTER 6

Page 230: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 221

}

}

Listing 6.10 The “Insert” section of the sync-method of the EmployeeDataServiceDao

And this concludes the handcrafting of our DataServices-based example. Now that we’ve been through the whole process, let’s see how it could have been avoided and automated.

Introducing MetadataLet’s look at the snippet from the XML file generated by the DAOFlex utility – Employee.xml. Please note the name of the Java package – com.theriabook.datasource, the name of the Assembler’s fill-method – getEmployees(), names on the transferring structures on the Java and ActionsScript side, both pointing to the array of com.theriabook.dto.EmployeeDTO objects, the name of the connec-tion pool – jdbc/theriabook, and the name of the method’s parameter – startDate:

<?xml version=”1.0” encoding=”UTF-8”?>

<WEBSERVICE NAME=”Employee” PACKAGE=”com.theriabook.datasource” TYPE=”DAOFlex” >

<SERVER LANGUAGE=”Java” MODE=”JEE”>

<SQL ACTION=”SELECT”

NAME=”getEmployees” POOL=”jdbc/theriabook” SCOPE=”public”

ASTYPE=”com.theriabook.dto.EmployeeDTO[]” JAVATYPE=”com.theriabook.dto.

EmployeeDTO[]”

>

<PARAM IN=”Y” INDEX=”1” JAVATYPE=”Date” NAME=”startDate”/>

</SQL>

</SERVER>

</WEBSERVICE>

Starting at this point, we’ll be working our way through this XML while building the complete XSL stylesheet from scratch. Once we make this stylesheet, it’ll manufacture any DataServiceEmploy-eeDAO, DataServiceDepartmentDAO, etc. – as long as we have the metadata XMLs like the above one. You’re probably wondering at this point: “What’s the input of the DAOFlex that lets it generate this XML?”

The input for DAOFlex is an annotated Java class, like the one presented in Listing 6.11:

package com.theriabook.datasource;

import java.util.Date;

import java.util.List;

/**

* @DAOFlex:webservice pool=jdbc/theriabook

End-to-End Rapid Application Development CHAPTER 6

Page 231: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

222 RIA WITH ADOBE FLEX AND JAVA

*/

public abstract class Employee {

/**

* @DAOFlex:sql

* sql=select * from employee where start_date < :startDate or start_date=:start-

Date

* transferType=com.theriabook.dto.EmployeeDTO[]

* updateTable=employee

* keyColumns=emp_id

*/

public abstract List getEmployees(Date startDate);

}

Listing 6.11 Source of the DAOFlex code-generation process – An annotated Java class

Introducing TemplatesLet’s put out our first iteration of the SimpleDataServiceDao.xsl stylesheet, which will eventually auto-matically build DataServiceDao objects for us. We assume that the reader is familiar with XSL basics, but if you haven’t had a chance to use XSL yet, here’s a good place to start: http://www.w3schools.com/xsl/.

We’ll be using XSL transformations to produce not an XML, but a plain text (Java code), so let’s start our stylesheet as follows: <?xml version=”1.0”?>

<xsl:stylesheet

version=”1.0”

xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”

>

<xsl:output omit-xml-declaration=”yes” method=”text”/>

….

</xsl:stylesheet>

For the topmost element in our metadata (WEBSERVICE) we’ll print out the value of the package, required utility imports, declaration of the class, and its constructor (Listing 6.12):

<xsl:template match=”/WEBSERVICE”>

/* Generated by SimpleDataServiceDao.xsl */

package <xsl:value-of select=”@PACKAGE”/>;

import java.util.*;

import java.sql.*;

import flex.data.*;

End-to-End Rapid Application Development CHAPTER 6

Page 232: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 223

import com.theriabook.DAOFlex.JDBCConnection;

import com.theriabook.DAOFlex.DAOException;

import com.theriabook.DAOFlex.DateTimeConversion;

public final class <xsl:value-of select=”@NAME”/>SimpleDataServiceDAO extends <xsl:

value-of select=”@NAME”/>

{

public <xsl:value-of select=”@NAME”/>SimpleDataServiceDAO()

{

}

<xsl:apply-templates select=”SERVER[@MODE=’JEE’]/SQL”/>

} //<xsl:value-of select=”@NAME”/>SimpleDataServiceDAO

</xsl:template>

Listing 6.12. The first cut of the SimpleDataServiceDao.xsl

We’ll discuss the <xsl:apply-templates select=”SERVER[MODE=’JEE’]/SQL”/> a bit later. For now, let’s look at the output that we’d get if we applied the stylesheet to the Employee.XML:

/* Generated by SimpleDataServiceDao.xsl */

package com.theriabook.datasource;

import java.util.*;

import flex.data.*;

import java.sql.*;

import com.theriabook.DAOFlex.JDBCConnection;

import com.theriabook.DAOFlex.DAOException;

import com.theriabook.DAOFlex.DateTimeConversion;

public final class EmployeeDataServiceDAO extends Employee

{

public EmployeeDataServiceDAO()

{

}

} //EmployeeDataServiceDAO

Listing 6.13 The output of the transformation per the SimpleDataServiceDao.xsl

The tag <xsl:apply-templates …/> from Listing 6.11 effectively delegates the processing of all nodes that can be located relative to the WEBSERVICE via the XPath expression “SERVER[MODE=’JEE’]/SQL” to a template that matches “SQL.” Before adding such a template, let’s look a bit deeper at the Employee.xml metadata file.

End-to-End Rapid Application Development CHAPTER 6

Page 233: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

224 RIA WITH ADOBE FLEX AND JAVA

Metadata for Input ParametersA closer look at the metadata produced by the DAOFlex utility against the source file Employee.java from Listing 6.10 will reveal two sections with SQL. The first one – WEBSERVICE/SERVICE/SQL/BODY – contains the SQL in its source form, while the other – WEBSERVICE/SERVICE/SQL/BODY/COMPILED – contains the same SQL in a form applicable for the JDBC PreparedStatement. The COMPILED section also contains the result of the matching of the original parameters against the JDBC question mark placeholders:

<?xml version=”1.0” encoding=”UTF-8”?>

<WEBSERVICE NAME=”Employee” PACKAGE=”com.theriabook.datasource” TYPE=”DAOFlex” VER-

SION=”2.0”>

<SERVER LANGUAGE=”Java” MODE=”JEE”>

<SQL ACTION=”SELECT”

NAME=”getEmployees” POOL=”jdbc/theriabook” SCOPE=”public”

ASTYPE=”com.theriabook.dto.EmployeeDTO[]” JAVATYPE=”com.theriabook.dto.Employ-

eeDTO[]”

>

<PARAM IN=”Y” INDEX=”1” JAVATYPE=”Date” NAME=”startDate”/>

<BODY >

<![CDATA[ select * from employee where start_date < :startDate or start_date=:start-

Date ]]>

</BODY>

<COMPILED>

<PARAM IN=”Y” INDEX=”1” JAVATYPE=”Date” NAME=”startDate” />

<PARAM IN=”Y” INDEX=”2” JAVATYPE=”Date” NAME=”startDate” />

<BODY>

<![CDATA[ select * from employee where start_date < ? or start_date=? ]]>

</BODY>

</COMPILED>

</SQL>

</SERVER>

</WEBSERVICE>

Listing 6.14 A second look at the Employee.xml metadata with input parameters

Templates for Implementing the Fill MethodLet’s modify the stylesheet to generate the fill-method by adding the template matching the SQL context. The abbreviated form of this template is presented further down in Listing 6.16. First have a look at the code that this template generates:

public final List /*com.theriabook.datasource.dto.EmployeeDTO[]*/ getEmployees(java.

util.Date startDate)

{

End-to-End Rapid Application Development CHAPTER 6

Page 234: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 225

String sql = “select * from employee where start_date < ? or start_date=?”;

ArrayList list = new ArrayList();

PreparedStatement stmt = null;

ResultSet rs = null;

Connection conn = null;

try {

conn = JDBCConnection.getConnection(“jdbc/theriabook”);

stmt = conn.prepareStatement(sql);

// ....

}

return list;

} catch(Throwable te) {

te.printStackTrace();

throw new DAOException(te);

} finally

{

try {rs.close(); rs = null;} catch (Exception e){// log your errors here}

try {stmt.close(); stmt = null;} catch (Exception e){ // log your errors here }

JDBCConnection.releaseConnection(conn);

}

}

Listing 6.15 The output of the <xsl:template match=”SQL”/> against Employee.xml

Listing 6.16 presents a template that generates the code above. Please note that this template in turn delegates the processing of the input parameters to the named template declareFillParam-eters as seen in Listing 6.17. The template is abbreviated and we’ll be looking at what’s hidden be-hind the commented ellipses “//…” in the next section of the chapter:

<xsl:template match=”SQL”>

public final List /*<xsl:value-of select=”@JAVATYPE”/>*/ <xsl:value-of select=”@NAME”/

>(<xsl:call-template name=”declareFillParameters”/>

String sql = “<xsl:value-of select=”COMPILED/BODY”/>”;

public final List /*<xsl:value-of select=”@JAVATYPE”/>*/ <xsl:value-of select=”@NAME”/

>(<xsl:call-template name=”declareFillParameters”/>)

{

String sql = “<xsl:value-of select=”COMPILED/BODY”/>”;

ArrayList list = new ArrayList();

PreparedStatement stmt = null;

ResultSet rs = null;

Connection conn = null;

try {

conn = JDBCConnection.getConnection(“<xsl:value-of select=”@POOL”/>”);

End-to-End Rapid Application Development CHAPTER 6

Page 235: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

226 RIA WITH ADOBE FLEX AND JAVA

stmt = conn.prepareStatement(sql);

// . . . .

return list;

} catch(Throwable te) {

te.printStackTrace();

throw new DAOException(te);

} finally

{

try {rs.close(); rs = null;} catch (Exception e){ // log your errors here }

try {stmt.close(); stmt = null;} catch (Exception e){ // log your errors here }

JDBCConnection.releaseConnection(conn);

}

}

</xsl:template>

Listing 6.16 This template generates the fill method for each SQL context

Here is the auxiliary template that helped us generate a declaration of parameters for the fill-meth-od. It puts a comma after each parameter, except the latest and narrows the definition of the Date to java.util.Date to avoid ambiguity between java.sql and java.util packages imported at the begin-ning of the class:

<xsl:template name=”declareFillParameters”>

<xsl:for-each select=”PARAM[@IN=’Y’]”>

<xsl:if test=”position()!=1”>, </xsl:if>

<xsl:choose>

<xsl:when test=”@JAVATYPE=’Date’”>java.util.Date</xsl:when>

<xsl:otherwise><xsl:value-of select=”@JAVATYPE”/></xsl:otherwise>

</xsl:choose>

<xsl:text> </xsl:text>

<xsl:value-of select=”@NAME”/>

</xsl:for-each>

</xsl:template>

Listing 6.17 The template declareFillParameters, used by <xsl:template match=”SQL”/>

Completing the Fill MethodLet’s upgrade our stylesheet to a state where it can generate a fully functional fill-method. In the case of the Employee.xml metadata, we’d like to see our template generate the code presented in Listing 6.18:

public final List /*com.theriabook.datasource.dto.EmployeeDTO[]*/ getEmployees(java.

util.Date startDate)

End-to-End Rapid Application Development CHAPTER 6

Page 236: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 227

{

String sql = “select * from employee where start_date < ? or start_date=?”;

ArrayList list = new ArrayList();

PreparedStatement stmt = null;

ResultSet rs = null;

Connection conn = null;

try {

conn = JDBCConnection.getConnection(“jdbc/theriabook”);

stmt = conn.prepareStatement(sql);

stmt.setDate(1, DateTimeConversion.toSqlDate(startDate));

stmt.setDate(2, DateTimeConversion.toSqlDate(startDate));

rs = stmt.executeQuery();

while( rs.next() ) {

com.theriabook.datasource.dto.EmployeeDTO dto = new com.theriabook.data

source.dto.EmployeeDTO();

dto.EMP_ID = (rs.getInt(“EMP_ID”));

dto.MANAGER_ID = (rs.getInt(“MANAGER_ID”));

dto.EMP_FNAME = (rs.getString(“EMP_FNAME”));

dto.EMP_LNAME = (rs.getString(“EMP_LNAME”));

dto.DEPT_ID = (rs.getInt(“DEPT_ID”));

dto.STREET = (rs.getString(“STREET”));

dto.CITY = (rs.getString(“CITY”));

dto.STATE = (rs.getString(“STATE”));

dto.ZIP_CODE = (rs.getString(“ZIP_CODE”));

dto.PHONE = (rs.getString(“PHONE”));

dto.STATUS = (rs.getString(“STATUS”));

dto.SS_NUMBER = (rs.getString(“SS_NUMBER”));

dto.SALARY = (rs.getDouble(“SALARY”));

dto.START_DATE = DateTimeConversion.toUtilDate(rs.getDate(“START_DATE”));

dto.TERMINATION_DATE = DateTimeConversion.toUtilDate(rs.getDate(“TERMINATION_

DATE”));

dto.BIRTH_DATE = DateTimeConversion.toUtilDate(rs.getDate(“BIRTH_DATE”));

dto.BENE_HEALTH_INS = (rs.getString(“BENE_HEALTH_INS”));

dto.BENE_LIFE_INS = (rs.getString(“BENE_LIFE_INS”));

dto.BENE_DAY_CARE = (rs.getString(“BENE_DAY_CARE”));

dto.SEX = (rs.getString(“SEX”));

list.add(dto);

}

return list;

} catch(Throwable te) {

End-to-End Rapid Application Development CHAPTER 6

Page 237: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

228 RIA WITH ADOBE FLEX AND JAVA

te.printStackTrace();

throw new DAOException(te);

} finally {

try {rs.close(); rs = null;} catch (Exception e){}

try {stmt.close(); stmt = null;} catch (Exception e){}

JDBCConnection.releaseConnection(conn);

}

}

Listing 6.18 Complete fill-method getEmployees() generated by SimpleDataServiceDao.xsl

To “teach” our stylesheet to produce this code, we’ll replace the try clause generated by the <tem-plate match=”SQL”> currently containing:

try {

conn = JDBCConnection.getConnection(“<xsl:value-of select=”@POOL”/>”);

stmt = conn.prepareStatement(sql);

// . . . .

return list;

}

with the following:

try {

conn = JDBCConnection.getConnection(“<xsl:value-of select=”@POOL”/>”);

stmt = conn.prepareStatement(sql);

<xsl:call-template name=”setParameters”/>

rs = stmt.executeQuery();

while( rs.next() ) {

<xsl:variable name=”itemType” select=”substring(string(@JAVA

TYPE), 1, string-length(string(@JAVATYPE))-2)”/> <xsl:value-of

select=”$itemType”/> dto = new <xsl:value-of select=”$itemType”/>();

<xsl:call-template name=”readRecord”/>

list.add(dto);

}

return list;

}

As you can see, we’ve delegated the work of calling the setXXX() methods to the named template setParameters and reading of the result set – to the named template readRecord. Let’s walk through these templates one at a time.

End-to-End Rapid Application Development CHAPTER 6

Page 238: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 229

Setting JDBC Statement ParametersWhile setting the input arguments of the prepared statement, we’ll have to convert the Java types of the parameters listed in the COMPILED node into JDBC types. In particular, we have to convert the Date in our use case to java.sql.Date to accommodate the following lines:

stmt.setDate(1, DateTimeConversion.toSqlDate(startDate));

stmt.setDate(2, DateTimeConversion.toSqlDate(startDate));

We’ve centralized this and similar conversions under the named template convertJavaArgument-ToJDBC:

<xsl:template name=”convertJavaArgumentToJDBC”>

<xsl:choose>

<xsl:when test=”@JAVATYPE=’Date’”>DateTimeConversion.toSqlDate(<xsl:value-of

select=”@NAME”/>)</xsl:when>

<xsl:when test=”@JAVATYPE=’Time’”>DateTimeConversion.toSqlTime(<xsl:value-of

select=”@NAME”/>)</xsl:when>

<xsl:otherwise>

<xsl:value-of select=”@NAME”/>

</xsl:otherwise>

</xsl:choose>

</xsl:template>

After taking care of the Java-to-JDBC conversion, the template setParameters becomes easy:

<xsl:template name=”setParameters”>

<xsl:for-each select=”COMPILED/PARAM[@IN=’Y’]”>

stmt.set<xsl:value-of select=”@JAVATYPE”/>(<xsl:value-of select=”@INDEX”/>,

<xsl:call-template name=”convertJavaArgumentToJDBC”/>);</xsl:for-each>

</xsl:template>

Reading the Result Set RecordReading the result set record would require another look at the metadata. For each SQL annotated method, the DAOFlex utility generates a description of the result set. To do that, DAOFlex con-nects to the target database during the generation of the metadata. Connection credentials are expected in the properties file named exactly as the JNDI data source name, and our file is called theriabook.properties and is located in the jdbc folder1. Here is the snippet of metadata that repre-sents the description of the result set associated with the getEmployees() method :

<SQL … NAME=”getEmployees” . . .>

<DATASET>

<FIELDS>

<FIELD key=”yes” name=”EMP_ID” precision=”11” scale=”0” tableName=”employee”

End-to-End Rapid Application Development CHAPTER 6

Page 239: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

230 RIA WITH ADOBE FLEX AND JAVA

type=”integer” updatable=”yes” />

<FIELD name=”MANAGER_ID” precision=”11” scale=”0” tableName=”employee” type=”integer”

updatable=”yes” />

<FIELD name=”EMP_FNAME” precision=”20” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”EMP_LNAME” precision=”20” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”DEPT_ID” precision=”11” scale=”0” tableName=”employee” type=”integer”

updatable=”yes” />

<FIELD name=”STREET” precision=”40” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”CITY” precision=”20” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”STATE” precision=”4” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”ZIP_CODE” precision=”9” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”PHONE” precision=”10” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”STATUS” precision=”1” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”SS_NUMBER” precision=”11” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”SALARY” precision=”20” scale=”3” tableName=”employee” type=”decimal”

updatable=”yes” />

<FIELD name=”START_DATE” precision=”10” scale=”0” tableName=”employee” type=”date”

updatable=”yes” />

<FIELD name=”TERMINATION_DATE” precision=”10” scale=”0” tableName=”employee”

type=”date” updatable=”yes” />

<FIELD name=”BIRTH_DATE” precision=”10” scale=”0” tableName=”employee” type=”date”

updatable=”yes” />

<FIELD name=”BENE_HEALTH_INS” precision=”1” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”BENE_LIFE_INS” precision=”1” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”BENE_DAY_CARE” precision=”1” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

<FIELD name=”SEX” precision=”1” scale=”0” tableName=”employee” type=”char”

updatable=”yes” />

</FIELDS>

</DATASET>

</SQL>

Listing 6.19 The description of the result set produced by DAOFlex

End-to-End Rapid Application Development CHAPTER 6

Page 240: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 231

There is one more point to make before we can look at the implementation of the readRecord tem-plate. The data types per DATASET/FIELDS/FIELD nodes are database-specific types, not JDBC ones. For example, we have to apply additional mapping to produce getString() for the char data-base columns and getDouble() for the decimal ones. This mapping is provided by the named tem-plate mapDBtoJDBC (you may need to tweak it a bit for your DBMS):

<xsl:template name=” mapDBtoJDBC”>

<xsl:choose>

<xsl:when test=”@type=’boolean’”>Boolean</xsl:when>

<xsl:when test=”@type =’byte’”>Byte</xsl:when>

<xsl:when test=”@type =’byte[]’”>Bytes</xsl:when>

<xsl:when test=”@type =’char’”>String</xsl:when>

<xsl:when test=”@type=’date’”>Date</xsl:when>

<xsl:when test=”@type=’datetime’”>Timestamp</xsl:when>

<xsl:when test=”@type=’decimal’”>Double</xsl:when>

<xsl:when test=”@type=’double’”>Double</xsl:when>

<xsl:when test=”@type=’float’”>Float</xsl:when>

<xsl:when test=”@type=’int’”>Int</xsl:when>

<xsl:when test=”@type=’integer’”>Int</xsl:when>

<xsl:when test=”@type=’lvarchar’”>String</xsl:when>

<xsl:when test=”@type=’money’”>Double</xsl:when>

<xsl:when test=”@type=’nchar’”>String</xsl:when>

<xsl:when test=”@type=’nvarchar’”>String</xsl:when>

<xsl:when test=”@type=’nvarchar2’”>String</xsl:when>

<xsl:when test=”@type=’number’ and @scale=’0’”>Long</xsl:when>

<xsl:when test=”@type=’number’ and @scale!=’0’”>Double</xsl:when>

<xsl:when test=”@type=’numeric’”>Double</xsl:when>

<xsl:when test=”@type=’smallint’”>Int</xsl:when>

<xsl:when test=”@type=’smallfloat’”>Float</xsl:when>

<xsl:when test=”@type=’text’”>String</xsl:when>

<xsl:when test=”@type=’time’”>Time</xsl:when>

<xsl:when test=”@type=’timestamp’”>Timestamp</xsl:when>

<xsl:when test=”@type=’varchar’”>String</xsl:when>

<xsl:when test=”@type=’varchar2’”>String</xsl:when>

<xsl:otherwise>Object</xsl:otherwise>

</xsl:choose></xsl:template>

Listing 6.20 Template mapping database types to JDBC ones

And now, let’s look at the readRecord template, which iterates over all the fields of the result set and generates lines like:

dto.EMP_ID = (rs.getInt(“EMP_ID”));

dto.SALARY = (rs.getDouble(“SALARY));

End-to-End Rapid Application Development CHAPTER 6

Page 241: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

232 RIA WITH ADOBE FLEX AND JAVA

In addition, JDBC-related date/time types get converted from java.sql to java.util form:

dto.START_DATE = DateTimeConversion.toUtilDate(rs.getDate(“START_DATE”));

Here is the readRecord template: <xsl:template name=”readRecord”>

<xsl:for-each select=”DATASET/FIELDS/FIELD”>dto.<xsl:value-of

select=”@name”/> = <xsl:choose>

<xsl:when test=”string(@type)=’date’”>DateTimeConversion.toUtilDate</xsl:when>

<xsl:when test=”string(@type)=’datetime’”>DateTimeConversion.toUtilDate</xsl:

when>

<xsl:when test=”string(@type)=’time’”>DateTimeConversion.toUtilDate</xsl:when>

<xsl:when test=”string(@type)=’timestamp’”>DateTimeConversion.toUtilDate</xsl:

when>

<xsl:otherwise></xsl:otherwise>

</xsl:choose>(rs.get<xsl:call-template name=”mapDBtoJDBC”/>(“<xsl:value-of

select=”@name”/>”));

</xsl:for-each>

</xsl:template>

This concludes the complete implementation of the fill-method. The sync-method templating comes next.

Templates for Implementing Sync-MethodWe’ll be generating the sync-method using precisely the same metadata that we’ve extracted from the annotated Java class presented in Listing 6.10. We’re particularly interested in the attribute updateTable=employee of the @DAOFlex:sql tag that tells us which table from the SELECT state-ment should be used as an update target (your select statement can have more than one table, but the current version of the DAOFlex can update only one).

Listing 6.21 presents the XML metadata we’ve been working with in this chapter, but it’s the first time that we show the UPDATE node. As expected, the UPDATE node holds the name of the table to update. It also indicates that the WHERE clause of the generated INSERT/DELETE/UPDATE state-ments should be based on the key fields (as opposed to the other alternatives: modified fields and the combination key-and-modified):

<?xml version=”1.0” encoding=”UTF-8”?>

<WEBSERVICE NAME=”Employee” PACKAGE=”com.theriabook.datasource” TYPE=”DAOFlex” VER-

SION=”2.0”>

<SERVER LANGUAGE=”Java” MODE=”JEE”>

<SQL ACTION=”SELECT”

NAME=”getEmployees” POOL=”jdbc/theriabook” SCOPE=”public”

End-to-End Rapid Application Development CHAPTER 6

Page 242: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 233

ASTYPE=”com.theriabook.dto.EmployeeDTO[]” JAVATYPE=”com.theriabook.dto.

EmployeeDTO[]”

>

<PARAM IN=”Y” INDEX=”1” JAVATYPE=”Date” NAME=”startDate”/>

. . . .

</COMPILED >

<UPDATE TARGET=”EMPLOYEE”>

<TABLE NAME=”EMPLOYEE” UPDATEMETHOD=”key”/>

</UPDATE>

<DATASET><FIELDS>

<FIELD key=”yes” name=”EMP_ID” tableName=”employee” type=”integer”

updatable=”yes”/>

. . . .

</FIELDS></DATASET>

</SQL>

</SERVER>

</WEBSERVICE>

Listing 6.21 The DAOFlex metadata with information for the sync-method

Interestingly, the top-level code for the sync-method doesn’t really depend on any of this. Indeed, we need to process the entire input List of ChangeObjects. We’ll do it in three passes (as we did in a manual mode in Listing 6.7) and execute all DELETEs first, with UPDATEs and INSERTs, but again, the code will be pretty agnostic relative to the underlying SQL. Listing 6.22 presents the template that automatically generates such a sync-method given the content of the <SQL /> metadata node as the context:

<xsl:template match=”SQL” mode=”update”>

public final List <xsl:value-of select=”@NAME”/>_sync(List items)

{

Connection conn = null;

ChangeObject co = null;

try {

conn = JDBCConnection.getConnection(“<xsl:value-of select=”@POOL”/>”);

Iterator iterator = items.iterator();

while (iterator.hasNext() ) { // Do all deletes first

co = (ChangeObject)iterator.next();

if(co.isDelete()) doDelete_<xsl:value-of select=”@NAME”/>(conn, co);

}

iterator = items.iterator();

while (iterator.hasNext()) { // Proceed to all updates next

co = (ChangeObject)iterator.next();

if(co.isUpdate()) doUpdate_<xsl:value-of select=”@NAME”/>(conn, co);

}

End-to-End Rapid Application Development CHAPTER 6

Page 243: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

234 RIA WITH ADOBE FLEX AND JAVA

iterator = items.iterator();

while (iterator.hasNext()) { // Finish with inserts

co = (ChangeObject)iterator.next();

if (co.isCreate()) doCreate_<xsl:value-of select=”@NAME”/>(conn, co);

}

} catch(DataSyncException dse) {

dse.printStackTrace();

throw dse;

} catch(Throwable te) {

te.printStackTrace();

throw new DAOException(te.getMessage(), te);

} finally {

if( conn!=null ) JDBCConnection.releaseConnection(conn);

}

return items;

</xsl:template>

Listing 6.22 The template to generate the“top-level” of the sync-method

Now, to make sure this template accompanies the original <xsl:template match=”SQL”> we’ll mod-ify the latter with the <xsl:apply-templates> as shown in the following snippet:

<xsl:template match=”SQL”>

<xsl:variable name=”itemType” select=”substring(string(@JAVATYPE), 1, string-length(st

ring(@JAVATYPE))-2)”/>

public final List /*<xsl:value-of select=”@JAVATYPE”/>*/ <xsl:value-of select=”@NAME”/

>(<xsl:call-template name=”declareFillParameters”/>)

{

. . . .

}

<xsl:if test=”@ACTION=’SELECT’ and boolean(UPDATE)”> <xsl:apply-templates select=”.”

mode=”update”/></xsl:if>

</xsl:template>

Since we’re transforming our Employee.xml metadata with the SimpleDataServiceDao.xsl in its current state, the relevant fragment of the output will look like Listing 6.23:

public final List searchEmployees_sync(List items)

{

logger.debug(“searchEmployees_sync(...)”);

Connection conn = null;

End-to-End Rapid Application Development CHAPTER 6

Page 244: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 235

ChangeObject co = null;

try {

conn = JDBCConnection.getPooledConnection(“jdbc/theriabook”);

Iterator iterator = items.iterator();

while (iterator.hasNext() ) { // Do all deletes first

co = (ChangeObject)iterator.next();

if(co.isDelete()) doDelete_searchEmployees(conn, co);

}

iterator = items.iterator();

while (iterator.hasNext()) { // Proceed to all updates next

co = (ChangeObject)iterator.next();

if(co.isUpdate()) doUpdate_searchEmployees(conn, co);

}

iterator = items.iterator();

while (iterator.hasNext()) { // Finish with inserts

co = (ChangeObject)iterator.next();

if (co.isCreate()) doCreate_searchEmployees(conn, co);

}

} catch(DataSyncException dse) {

dse.printStackTrace();

throw dse;

} catch(Throwable te) {

te.printStackTrace();

throw new DAOException(te.getMessage(), te);

} finally {

if( conn!=null ) JDBCConnection.releasePooledConnection(conn);

}

return items;

}

Listing 6.23 Top-level sync-method code generated by our template

Our next task is to generate doUpdate(), doDelete(), and doInsert() methods.

Completing the Sync MethodWe’ll delegate the generation process of the doUpdate(), doDelete(), and doInsert() methods to three correspondingly named templates. We’ll alter the template presented in Listing 6.22 as shown below:

<xsl:template match=”SQL” mode=”update”>

public final List <xsl:value-of select=”@NAME”/>_sync(List items)

{

. . . .

End-to-End Rapid Application Development CHAPTER 6

Page 245: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

236 RIA WITH ADOBE FLEX AND JAVA

}

<xsl:call-template name=”doUpdate” />

<xsl:call-template name=”doDelete” />

<xsl:call-template name=”doInsert” />

</xsl:template>

Let’s start with doUpdate. Remember our exercise with handcrafted DataService-based code? That doUpdate() contained a StringBuffer of the UPDATE statement. Given that our XML context is the <SQL> node we can generate the required line as

StringBuffer sql = new StringBuffer(“UPDATE <xsl:value-of select=”UPDATE/

@TARGET”/> SET “);

To produce the WHERE clause, which will enumerate all the key columns in the form “key1=?, key2=?” we can do the following:

sql.append( “ WHERE (<xsl:for-each select=”DATASET/FIELDS/

FIELD[@key=’yes’]”><xsl:if test=”position()!=1”> AND </xsl:if><xsl:value-of

select=”@name”/>=?</xsl:for-each>)” );

Then, to substitute the “?” symbols with the key values we will employ the similar <xsl:for-each/>: combined with what we learned about the ChangedObject’s getPreviousValue() method:

<xsl:for-each select=”DATASET/FIELDS/FIELD[@key=’yes’]”>

stmt.setObject(ii++, co.getPreviousValue(“<xsl:value-of select=”@name”/>”));</xsl:

for-each>

Ultimately we’ll arrive at the text of the doUpdate template as shown in Listing 6.24:

<xsl:template name=”doUpdate” >

private void doUpdate_<xsl:value-of select=”@NAME”/>(Connection conn, ChangeObject co)

throws SQLException{

PreparedStatement stmt = null;

try {

StringBuffer sql = new StringBuffer(“UPDATE <xsl:value-of select=”UPDATE/

@TARGET”/> SET “);

String [] names = co.getChangedPropertyNames();

for (int ii=0; ii &lt; names.length; ii++) {

sql.append((ii!=0?”, “:””) + names[ii] +” = ? “);

}

sql.append( “ WHERE (<xsl:for-each select=”DATASET/FIELDS/

FIELD[@key=’yes’]”><xsl:if test=”position()!=1”> AND </xsl:if><xsl:value-of

select=”@name”/>=?</xsl:for-each>)” );

End-to-End Rapid Application Development CHAPTER 6

Page 246: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 237

stmt = conn.prepareStatement(sql.toString());

Map values = co.getChangedValues();

int ii=0;

for (ii=0; ii &lt; names.length; ii++) {

stmt.setObject( ii+1, values.get(names[ii]));

}

ii++;

<xsl:for-each select=”DATASET/FIELDS/FIELD[@key=’yes’]”>

stmt.setObject(ii++, co.getPreviousValue(“<xsl:value-of select=”@name”/>”));</

xsl:for-each>

if (stmt.executeUpdate()==0) throw new DataSyncException(co);

} finally {

try { if( stmt!=null) stmt.close(); stmt = null;} catch (Exception e){}

}

}

</xsl:template>

Listing 6.24 The template of the doUpdate() method

The template implementing the doDelete method is very similar, except that, due to the syntax of the DELETE statement it ventures into enumerating the WHERE-hosted keys right off the bat while preparing the StringBuffer:

<xsl:template name=”doDelete” >

private void doDelete_<xsl:value-of select=”@NAME”/>(Connection conn, ChangeObject co)

throws SQLException{

PreparedStatement stmt = null;

try {

StringBuffer sql = new StringBuffer(“DELETE FROM <xsl:value-of select=”UPDATE/

@TARGET”/> WHERE (<xsl:for-each select=”DATASET/FIELDS/FIELD[@key=’yes’]”><xsl:if

test=”position()!=1”> AND </xsl:if><xsl:value-of select=”@name”/>=?</xsl:for-each>)”);

stmt = conn.prepareStatement(sql.toString());

<xsl:for-each select=”DATASET/FIELDS/FIELD[@key=’yes’]”>

stmt.setObject(<xsl:value-of select=”position()”/>, co.getPreviousValue(“<x

sl:value-of select=”@name”/>”));

</xsl:for-each>

if (stmt.executeUpdate()==0) throw new DataSyncException(co);

} finally {

try { if( stmt!=null) stmt.close(); stmt = null;} catch (Exception e){}

}

End-to-End Rapid Application Development CHAPTER 6

Page 247: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

238 RIA WITH ADOBE FLEX AND JAVA

}

</xsl:template>

Listing 6.25 The template of the doDelete() method

We did not list the results of the XSL transformation here because they are literally identical to the handcrafted code we modeled our templates after.

We’re almost there. The only remaining part of the SimpleDataServiceDao.xsl to discuss is the do-Create template, which will be covered next.

The Template for the doCreate() MethodIt’s time to do another kind of mapping exercise. We’ve been through such an exercise when reading the ResultSet with the getXXX() functions. Knowing the database type of the result set element we had to determine the proper choice of the getInt(), getString(), etc., and on top of that, perform the conversions like converting java.sql.Date into java.util.Date. On the same note, we have to pick the right setInt(), setString(), and the like, based on the database data type and do the proper conversions.

The named template “mapDBtoJDBC” (see Listing 6.20) comes in handy again. Provided the con-text is a metadata node <FIELD>, we can generate the proper setXXX() call with the line:

stmt.set<xsl:call-template name=”mapDBtoJDBC”/>(arguments);

To perform the type conversion, we’ll add another named template, convertJavaColumnToJDBC. It expects to use the value of the database column’s type and an expression for conversion. For the date/time/timestamp types it converts the Java value of the DTO property into the corresponding value from the java.sql package leaving other expressions intact:

<xsl:template name=”convertJavaColumnToJDBC”>

<xsl:param name=”type”/>

<xsl:param name=”expression”/>

<xsl:when test=”type=’date’”>DateTimeConversion.toSqlDate(<xsl:value-of

select=”$expression”/>)</xsl:when>

<xsl:when test=”type=’datetime’”>DateTimeConversion.toSqlTimestamp(<xsl:value-of

select=”$expression”/>)</xsl:when>

<xsl:when test=”type=’time’”>DateTimeConversion.toSqlTime(<xsl:value-of

select=”$expression”/>)</xsl:when>

<xsl:when test=”type=’timestamp’”>DateTimeConversion.toSqlTimestamp(<xsl:value-of

select=”$expression”/>)</xsl:when>

<xsl:otherwise><xsl:value-of select=”$expression”/></xsl:otherwise></xsl:choose></xsl:

template>

Listing 6.26 The template of converting date/time targeting values to java.sql.* values

End-to-End Rapid Application Development CHAPTER 6

Page 248: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 239

And here is the last component. In the absence of dedicated null indicators traveling along with the DTO, the only way to set NULL values for types like double and float is to have an if statement similar to:

if (Double.isNaN(item.SALARY))

stmt.setNull(4,Types.DOUBLE);

else

stmt.setDouble(4, item.SALARY);

Putting it all together, we arrive at the template writeRecord. It starts with converting source ex-pressions like item.BIRTH_DATE, item.START_DATE, etc., into a jdbcValue. Then, depending on the FIELD type, it generates an if-statement like the one above or outputs a setXXX() statement with the code:

stmt.set<xsl:call-template name=”mapDBtoJDBC”/>(<xsl:value-of

select=”position()”/>,

<xsl:value-of

select=”$jdbcValue”/>);

The full listing of the writeRecord template follows:

<xsl:template name=”writeRecord”>

<xsl:for-each select=”DATASET/FIELDS/FIELD”>

<xsl:variable name=”jdbcValue”>

<xsl:call-template name=”convertJavaColumnToJDBC”>

<xsl:with-param name=”type” select=”@type”/>

<xsl:with-param name=”exp”>item.<xsl:value-of select=”@name”/>

</xsl:with-param>

</xsl:call-template>

</xsl:variable>

<xsl:choose>

<xsl:when test=”@type=’double’ or @type=’decimal’ or @type=’money’ or

(@type=’number’ and @scale!=’0’) or @type=’numeric’”>

if (Double.isNaN(item.<xsl:value-of select=”@name”/>))

stmt.setNull(<xsl:value-of select=”position()”/>,Types.DOUBLE);

else

stmt.setDouble(<xsl:value-of select=”position()”/>, item.<xsl:value-of

select=”@name”/>);</xsl:when>

<xsl:when test=”@type=’float’ or @type=’smallfloat’”>

if (Float.isNaN(item.<xsl:value-of select=”@name”/>))

stmt.setNull(<xsl:value-of select=”position()”/>,Types.FLOAT);

else

stmt.setFloat(<xsl:value-of select=”position()”/>, item.<xsl:value-of

select=”@name”/>);</xsl:when>

End-to-End Rapid Application Development CHAPTER 6

Page 249: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

240 RIA WITH ADOBE FLEX AND JAVA

<xsl:otherwise>

stmt.set<xsl:call-template name=”mapDBtoJDBC”/>(<xsl:value-of

select=”position()”/>, <xsl:value-of select=”$jdbcValue”/>);</xsl:otherwise>

</xsl:choose>

</xsl:for-each>

</xsl:template>

Listing 6.27 The writeRecord template

If you’ve made it so far, the XSL template for doCreate will look trivial. It builds the SQL INTO clause of the INSERT iterating over the result set fields, and iterates over it again to place an adequate number of comma-separated “?” characters. Then, on calling conn.prepareStatement() it invokes the template writeRecord. That’s it.

<xsl:template name=”doCreate” >

<xsl:variable name=”itemType” select=”substring(string(@JAVATYPE), 1, string-length(st

ring(@JAVATYPE))-2)”/>

private ChangeObject doCreate_<xsl:value-of select=”@NAME”/>(Connection conn, Chan-

geObject co) throws SQLException{he

PreparedStatement stmt = null;

try {

String sql = “INSERT INTO <xsl:value-of select=”UPDATE/@TARGET”/> “ +

“(<xsl:for-each select=”DATASET/FIELDS/FIELD”>

<xsl:value-of select=”@name”/><xsl:if test=”not(position()=last())”>,</xsl:if>

</xsl:for-each>)”+

“ values (<xsl:for-each select=”DATASET/FIELDS/FIELD”>?<xsl:if test=”not(positio

n()=last())”>,</xsl:if></xsl:for-each>)”;

stmt = conn.prepareStatement(sql);

<xsl:value-of select=”$itemType”/> item = (<xsl:value-of select=”$itemType”/>)

co.getNewVersion();

<xsl:call-template name=”writeRecord”/>

if (stmt.executeUpdate()==0) throw new DAOException(“Failed inserting.”);

co.setNewVersion(item);

return co;

} finally {

try { if( stmt!=null) stmt.close(); stmt = null;} catch (Exception e){}

}

}

</xsl:template>

Listing 6.28 The template of the doCreate() method

End-to-End Rapid Application Development CHAPTER 6

Page 250: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 241

Here is the output of the doCreate() template against the Employee.xml metadata:

private ChangeObject doCreate_getEmployees(Connection conn, ChangeObject co) throws

SQLException{

PreparedStatement stmt = null;

try {

String sql = “INSERT INTO EMPLOYEE “ +

“(EMP_ID,MANAGER_ID,EMP_FNAME,EMP_LNAME,DEPT_ID,STREET,CITY,STATE,ZIP_

CODE,PHONE,STATUS,SS_NUMBER,SALARY,START_DATE,TERMINATION_DATE,BIRTH_DATE,BENE_HEALTH_

INS,BENE_LIFE_INS,BENE_DAY_CARE,SEX)”+

“ values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)”;

stmt = conn.prepareStatement(sql);

com.theriabook.dto.EmployeeDTO item = (com.theriabook.dto.EmployeeDTO)

co.getNewVersion();

stmt.setInt(1, item.EMP_ID);

stmt.setInt(2, item.MANAGER_ID);

stmt.setString(3, item.EMP_FNAME);

stmt.setString(4, item.EMP_LNAME);

stmt.setInt(5, item.DEPT_ID);

stmt.setString(6, item.STREET);

stmt.setString(7, item.CITY);

stmt.setString(8, item.STATE);

stmt.setString(9, item.ZIP_CODE);

stmt.setString(10, item.PHONE);

stmt.setString(11, item.STATUS);

stmt.setString(12, item.SS_NUMBER);

if (Double.isNaN(item.SALARY))

stmt.setNull(13,Types.DOUBLE);

else

stmt.setDouble(13, item.SALARY);

stmt.setDate(14, DateTimeConversion.toSqlDate(item.START_DATE));

stmt.setDate(15, DateTimeConversion.toSqlDate(item.TERMINATION_DATE));

stmt.setDate(16, DateTimeConversion.toSqlDate(item.BIRTH_DATE));

stmt.setString(17, item.BENE_HEALTH_INS);

stmt.setString(18, item.BENE_LIFE_INS);

stmt.setString(19, item.BENE_DAY_CARE);

stmt.setString(20, item.SEX);

if (stmt.executeUpdate()==0) throw new DAOException(“Failed inserting.”);

co.setNewVersion(item);

return co;

} finally {

End-to-End Rapid Application Development CHAPTER 6

Page 251: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

242 RIA WITH ADOBE FLEX AND JAVA

try { if( stmt!=null) stmt.close(); stmt = null;} catch (Exception e){}

}

}

Listing 6.29 The output produced by the doCreate template

This makes out SimpleDataServiceDao.xsl complete.

Who Owns the DAOFlex Templates?The DAOFlex is an open source utility distributed under the GPL license and you can use and mod-ify it as you see fit as long as you mention Farata Systems as its original creator. The SimpleDataSer-viceDao.xsl, described here, is a somewhat simplified version of the real one – Dao.xsl – distributed with the DAOFlex.

We need to emphasize why we went to such lengths explaining how these stylesheets work.

As we said earlier the rapid application development tools (RAD) that use automated code genera-tion assume that you feel comfortable with XSL templates. For instance, a JDBC-related template like the one we’ve been making so far may work slightly differently for a particular JDBC driver. The metadata reported by different JDBC drivers may also be slightly different. For instance we’ve omit-ted BLOB and CLOB support from our template. While working with a specific database, you can tune the stylesheet to match your driver and forget the tedious process of handcrafting, debugging, and testing the code doing the routine database processing.

Rapid Application Development with DAOFlexThe rest of the chapter describes the open source version of DAOFlex. (You can outright cut-down these configuration efforts and automate entire Flex/Java development by downloading a com-mercial DAOFlex Eclipse plugin developed by FarataSystems, LLC. The download URL is http://www.myflex.org.) Since DAOFlex is nothing but a metadata extractor with a bunch of XSL transfor-mations, you already know how to customize it – you have to modify or add the stylesheets. Now let’s look at DAOFlex’s directory structure shown in Figure 6.2.

End-to-End Rapid Application Development CHAPTER 6

Page 252: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 243

DAOFlex Directory Structure and Configuration Files

Figure 6.2 The DAOFlex directory structure

Start with the welcome.html file located in the root directory to get a brief intro to DAOFlex, its setup and usage. Other documents – setup.htm and syntax.html – are located in the docs folder.

Please note file JRun4.zip and Tomcat5.5.zip. These files contain “deltas” – everything you need to unzip on top of existing Tomcat or JRun to have DAOFlex up and running, including the database connectivity. You’d also need to install the MySQL test database, applying the backup file test.sql from mysql_backup folder. All required steps are described in /docs/setup.htm.

The folder dist contains DAOFlex’s Java sources. The Ant build.xml files, residing in dist/generator and dist/runtime, produce relevant JARS in the build folder. These Ant files rely on the JARS from the lib folder explained below.

The folder examples contains sample Java source files annotated with the DAOFlex:sql tag. It also contains a build.xml file to run DAOFlex code generation, with dependencies on the build/dao-Flex-generator.jar and other files from the /lib folder. While setting up your DAOFlex project you’ll be copying and modifying this build.xml file. Folder examples also contain the OrderManagement subfolder, with the source files relevant to the transactional application from Chapter 7.

(Please note that due to differences between Java4 and Java5 we supply two distinct versions of build files. These build files correspond to two distinct versions or daoflex-runtime.jar and daoflex-generator.jar from the lib folder. In particular, you’d use Java5 jars and build.xml for Tomcat5.5 and Java4 versions for JRun4.)

The folder lib contains jars required to build DAOFlex and run code generation by build.xml from inside the examples folder or your own DAOFlex-based project. You may need to extend the con-

End-to-End Rapid Application Development CHAPTER 6

Page 253: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

244 RIA WITH ADOBE FLEX AND JAVA

tents of the lib with your specific JDBC driver. We provided late drivers, as of the time of writing, drivers for Oracle, Sybase, and MySQL. Pre-built daoflex-generator.jar and daoflex-runtime.jar are also stored here.

The xsl folder contains XSL stylesheets used by the DAOFlex generator. Since the stylesheets’ look-up is done along the classpath of the DAOFlex doclet, you can overload standard templates with your own without physically replacing the originals in the DAOFlex/xsl folder.

The root folder of the DAOFlex distribution also contains two properties files: daoflex.properties and daoflex-runtime.properties.

The file daoflex.properties lists all of the templates that are being applied against the metadata ex-tracted from the annotated Java files. Here you can explicitly tell DAOFlex to go through your own template, instead of the standard one, or add templates that, perhaps, generate Grid descendant controls, etc.

The file DAOFlex-runtime.properties has to be visible to the classloader’s chain of your Web ap-plication. At present it contains one line with the JNDI prefix of the JDBC namespace and isn’t required if your application server is JRun or WebLogic (but you would need it for Tomcat and WebSphere):

datasource.jndi.root=java:comp/env

If you’re planning to use DS under the Apache Tomcat server, you’ll have to install and config-ure Java Open Transaction Manager (JOTM), an implementation of the full distributed transaction support for Java applications. You’ll have to drop the JOTM jars into the <tomcat>/common/lib or WEB-INF/lib of your Web Application. You also would have to add the following lines to the appli-cation configuration file <tomcat>/conf/Catalina/localhost/theriabook.xml:

<Context docBase=”c:/theriabook/www” privileged=”true” antiResourceLocking=”false”

antiJARLocking=”false”>

....

<Transaction factory=”org.objectweb.jotm.UserTransactionFactory” jotm.time-

out=”60”/>

....

</Context>

You can get details on JOTM/Tomcat configuration in the documentation section at http://jotm.objectweb.org/index.html. Again, if you simply unzip Tomcat5.5.zip from the DAOFlex root on top of your Tomcat installation, every configuration step will have been taken care of. You can browse this zip file to study the required changes in detail.

End-to-End Rapid Application Development CHAPTER 6

Page 254: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 245

DAOFlex Project SetupTo set up your DAOFlex project, copy and modify the content of the examples folder. You’d need to modify the build.xml file, create the xyz.properties file(s), where xyz is the name of your dataSource(s), and create the source Java file annotated with the @DAOFlex:sql tag.

Let’s start with the configuration properties contained in the build.xml. (The up-to-date version of the examples/build.xml file may be different from the one described in this section.)

The first group of properties relates to original and generated text files. They are:

source.java.root - root location of DAOFlex-annotated abstract Java classes;

generated.meta.root – root location of extracted metadata;

generated.web.root - root location of ActionScript DTO

generated.java.root – root location of Java DTOs, DAOs & Assemblers

generated.test.root – root location of Flex Test Applications

Here’s how these values are set up in examples/build.xml:

<property name=”source.java.root” value=”c:/theriabook/tools/DAOFlex/exam-

ples/src” />

<property name=”generated.meta.root” value=”c:/theriabook/tools/DAOFlex/

examples/generated/meta”/>

<property name=”generated.web.root” value=”c:/theriabook/tools/DAOFlex/

examples/generated/web” />

<property name=”generated.java.root” value=”c:/theriabook/tools/DAOFlex/

examples/generated/java”/>

<property name=”generated.test.root” value=”c:/theriabook/tools/DAOFlex/

examples/generated/web/test”/>

You can change these values any way you want, but take into account that it doesn’t make a lot of sense to keep generated Java files or compiled classes in the source control system. For that reason, we find it convenient to keep generated Java files in the separate subtree and, for the same reason, build.xml creates two separate JAR files for compiled source classes and compiled generated classes.

The second group of properties relates to the location of the DAOFlex home directory and the de-ployment location of the application as a whole and output JAR files in particular:

• DAOFlex.home: Location of DAOFlex root directory• deployment.web.root: Location of the context root of your Web application• original.jar.destination: Name/location of the JAR with compiled abstract classes• generated.jar.destination: Name/location of the JAR with compiled generated classes

These values are set up in examples/build.xml as follows:

End-to-End Rapid Application Development CHAPTER 6

Page 255: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

246 RIA WITH ADOBE FLEX AND JAVA

<property name=”DAOFlex.home” value=”c:/TheRIABook/tools/DAOFlex”/>

<property name=”deployment.web.root” value=”C:/fds3jrun/jrun4/servers/default/

theriabook”/>

<property name=”original.jar.destination” value=”${deployment.web.root}/WEB-INF/

lib/DAOFlex-examples.jar”/>

<property name=”generated.jar.destination” value=”${deployment.web.root}/WEB-

INF/lib/DAOFlex-examples-generated.jar”/>

Once you figure out the values of the above configuration properties you can proceed to setting up the dataSource (database connection pool)-related files. These files contain the driver URL, userid, and password to enable the DAOFlex generator to connect to your database and get the SQL metadata. No harm will be done to the database, and the DAOFlex generator will make sure that design-time queries return no data. Just to remind you, the file theriabook.properties is inside the examples/jdbc folder. It corresponds to the connection pool (dataSource name) jdbc/theri-abook per the annotation in examples/src/com/theriabook/datasource/Employee.java.

Similar to the stylesheets, connection pool properties files are looked up via the DAOFlex Doclet classpath. According to examples/build.xml, the Doclet classpath includes examples and DAOFlex home, so if you have multiple projects sharing the same connection parameters you can provide JDBC connection files relative to DAOFlex’s home directory.

Finally, before running the DAOFlex generator, put the annotated files under the folder pointed to by the property source.java.root (see above).

Running the DAOFlex Code GeneratorRunning DAOFlex is pretty simple: you execute the Ant script on the command prompt provided that the current working directory contains the build.xml file that we described in the previous sec-tion. Did we mention you’ve got to have ANT.EXE on the system PATH?

As a result, you’ll see the files generated, the Java code compiled, and JARred split into two separate JARs, and placed as per your settings.

Alternatively, you may use Eclipse. You’d need to go to Java Perspective and create a new Java proj-ect from the existing Ant file defaulting to the javac task build-original, as shown in Figure 6.3:

End-to-End Rapid Application Development CHAPTER 6

Page 256: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 247

Figure 6.3 Setting the DAOFlex code-generating project in Eclipse

Then you’d be able to run and rerun the code-generation process, compilation, and jarring – every-thing your build.xml does with the Run Ant right-mouse menu off the highlighted build.xml.

Here’s a sample extract from the console output of the DAOFlex code generator:

Figure 6.4 Console output of the DAOFlex code generator

End-to-End Rapid Application Development CHAPTER 6

Page 257: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

248 RIA WITH ADOBE FLEX AND JAVA

Testing and Using DAOFlex OutputOnce the DAOFlex generation is complete you can move the ActionScript classes from the fold-er pointed by generated.web.root of your build.xml. Better yet, you can have generated.web.root point to the final destination of these classes so you don’t have to move them.

Next, you can find a complete generated MXML single-grid application in the folder pointed by generated.test.root. Specifically in case of the Employee.java provided in our examples, the Data Services testing application Employee__GridTest.mxml will be located under the folder:

examples/generated/web/test/ds/com/theriabook/datasource

Let’s assume again that you allow DAOFlex to output test MXML files directly inside the Flex project you’re working on or, conversely, that you set up a Flex project using the folder with the generated file. If you set up the project to be compiled locally, you’ll need one additional step to run Employee__GridTest.mxml – copy the contents to the root of your project. Otherwise, if your project is set up to compile on the server, you can run the Employee__GridTest.mxml from its current location.

Either way Figure 6.5 shows how the application will look. Look familiar? Yep, that’s the applica-tion we started the chapter with, with one very essential difference: we didn’t write a single line of code other than a simple annotated Java class as in Listing 6.10. We just processed this class with DAOFlex.

Figure 6.5 Application generated with the DAOFlex code generator

SummaryIn this chapter not only have we shown you a tool that can automate tedious programming of the

End-to-End Rapid Application Development CHAPTER 6

Page 258: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 249

datagrid/database communication, but have also explained how you could create such a tool on your own. In other words, we’ve both given you a fish and taught you how to fish. We generated Java, MXML, and ActionScript code with Java doclets. With Java5 we could have used Java annota-tions and the Annotation Processing Tool (APT) that are beyond the scope of this book.

In addition to the open source version of DAOFlex described in this chapter, there is a commercial DaoFlex plugin for Eclipse, developed by FarataSystems, LLC. You can download this and other plugins for Flex at http://www.myflex.org.

End-to-End Rapid Application Development CHAPTER 6

Endnote1. In this case DaoFlex looks for jdbc/theriabook.properties in the project folder and then in the DaoFlex home

folder.

Page 259: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 251

How to Write Your Own Data Management Services

7CHAPTER

Page 260: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

252 RIA WITH ADOBE FLEX AND JAVA

This chapter is about data management, client/server data synchronization, and transaction pro-cessing: the classic topics that represent a large portion of the budget of any enterprise project whether your industry is e-commerce, financial, manufacturing, or healthcare – any application centered around data.

We touched on this subject in the previous chapter, when we discussed Flex Data Services, which are focused on peer-server-peer data replication at the price of server-side caching complexity. At the same time Flex offers an elegant yet powerful mechanism of Flex Remoting, which has zero impact on server architecture and, by virtue of absolving the server from data persistence chores, allows your systems to scale practically without limits.

We’ll start by upgrading a regular Flex Collection to what we call a “destination-aware” collection, capable of populating itself from a remote destination. Through a detailed study of the mechanics of Flex Collections and the anatomy of Managed ActionScript objects we will then lead you to the creation of a “change-tracking” and “updatable” Data Collection. This collection accumulates all changes in a controlled manner and, at your command, sends them to the server-side Java sync() method packaged exactly as a DataService would package them for commit().

Finally, we will teach you how you can batch the execution of any arbitrary remote calls so that, for instance, your ActionScript client can control sophisticated transactions spanning multiple desti-nations, including calls to sync() methods, calls to stored procedures or, in general, anything that your application may need to incorporate in a transaction executed by the server.

At the end of this chapter, we will illustrate the use of Flex Remoting for data synchronization tasks with a sample OrderEntryDemo application.

Setting the SceneWhile Flex Data Management Services is focused on server-side caching and peer-server-peer data replication, Flex Remoting enables a straight Remote Procedure Call (RPC) from an ActionScript client to the server. Reiterating the points brought up in Chapter 5, we should remind our readers

How to Write Your Own Data Management ServicesCHAPTER 7

How to Write Your Own Data Management Services1

Page 261: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 253

that the AMF 3 protocol is by far the fastest Flash Player/Server communication mechanism, and this is why invoking a Java POJO via Flex Remoting can yield an order of magnitude performance gain over XML/Web Services, depending on the data volume.

In this chapter we will be reusing the DAO class com.theriabook.datasource.EmployeeDataSer-viceDAO that we built in Chapter 6. You might ask, if that is a _DataService_ DAO, how we can use it for remoting? The answer is: we can use it as is without even a tiny change. This is possible because of the thoughtful FDMS API design.

To employ the EmployeeDataServiceDao class for Flex Remoting we will configure WEB-INF/flex/remoting-config.xml to contain the destination “Employee” as shown in Listing 7.1 be-low:

<?xml version=”1.0” encoding=”UTF-8”?>

<service id=”remoting-service”

class=”flex.messaging.services.RemotingService”

messageTypes=”flex.messaging.messages.RemotingMessage”>

<adapters>

<adapter-definition id=”java-object”

class=”flex.messaging.services.remoting.adapters.JavaAdapter” default=”true”/>

</adapters>

<default-channels>

<channel ref=”my-amf”/>

</default-channels>

<destination id=”Employee”>

<properties>

<source>com.theriabook.datasource.EmployeeDataServiceDAO</source>

</properties>

</destination>

</service>

Listing 7.1 Flex Remoting configuration with EmployeeDataServiceDAO

Now our class is a legitimate remoting destination – we will use it a bit later.

Introducing Destination-Aware CollectionsRemember the DataService fill() method? To populate an ArrayCollection via a DataService you’d write something like this:

var ac:ArrayCollection = new ArrayCollection();

How to Write Your Own Data Management ServicesCHAPTER 7

Page 262: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

254 RIA WITH ADOBE FLEX AND JAVA

var ds:DataService = new DataService(“Employee”);

ds.fill(ac);

How about eliminating the middleman – ds? We are talking about a destination-aware collection capable of taking care of filling itself:

var ac:DestinationAwareCollection = new DestinationAwareCollection();

ac.destination = “Employee”;

ac.method = “getEmployees”;

ac.fill();

It took us one extra line of ActionScript:

ac.method = “getEmployees”;

Importantly, we brought back to ActionScript what had been hardwired on the server-side by the DataService approach. Let’s see how the application code can benefit from any data-awareness of our collection. Listing 7.2 presents an MXML application that we called DestinationAwareCol-lectionDemo:

<?xml version=”1.0” encoding=”UTF-8”?>

<!-- DestinationAwareCollectionDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

creationComplete=”onCreationComplete()”>

<mx:DataGrid id=”dg” dataProvider=”{collection}”>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”EMP_FNAME” headerText=”Emp Fname” />

<mx:DataGridColumn dataField=”EMP_LNAME” headerText=”Emp Lname” />

<mx:DataGridColumn dataField=”STREET” headerText=”Street” />

<mx:DataGridColumn dataField=”CITY” headerText=”City” />

<mx:DataGridColumn dataField=”STATE” headerText=”State” />

</mx:Array></mx:columns>

</mx:DataGrid>

<mx:Script>

<![CDATA[

import com.theriabook.collections.DataCollection;

import DestinationAwareCollection;

[Bindable] private var collection:DestinationAwareCollection;

private function onCreationComplete():void {

collection = new DestinationAwareCollection();

collection.destination = “Employee”;

collection.method = “getEmployees”;

collection.fill(new Date());

How to Write Your Own Data Management ServicesCHAPTER 7

Page 263: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 255

}

]]>

</mx:Script>

</mx:Application>

Listing 7.2 DestinationAwareCollectionDemo application

Figure 7.1 A snapshot of DestinationAwareDemo output

This application contains a data grid with a dataProvider bound to an instance of Destination-AwareCollection. There is no RemoteObject in sight, no event handlers for ResultEvent and Fault-Event. The only data-related code contained in our application is the initialization of collection parameters and the invocation of its fill() method:

collection = new DestinationAwareCollection();

collection.destination = “Employee”;

collection.method = “getEmployees”;

collection.fill(new Date());

Simple, isn’t it? But what’s inside this black box called DestinationAwareCollection?

Making a Destination-Aware CollectionThe simplicity of DestinationAwareCollection takes its roots in the way it’s constructed. To make DestinationAwareCollection, Listing 7.3, we extend an ArrayCollection and add two public vari-ables: destination and method:

public class DestinationAwareCollection extends ArrayCollection {

public var destination:String=null;

How to Write Your Own Data Management ServicesCHAPTER 7

Page 264: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

256 RIA WITH ADOBE FLEX AND JAVA

public var method : String = null;

}

Then we introduce a public method fill(). Since all AMF invocations are asynchronous messages, we design fill() to return an AsyncToken, which is a locally cached object. In your typical scenario, Async-Token gets accessed twice: you use it to store some data related to the particular request during the send and then, when the response comes, you look up the same data for a just-in-time perusal:

public function fill(... args): AsyncToken {

var act:AsyncToken = invoke(method, args);

act.method = “fill”;

return act;

}

Now let’s get to the implementation details. The only parameter of the fill() method is defined as…rest array, which in turn is passed to the invocation of the remoting operation:

protected function invoke(method:String, args:Array):AsyncToken {

if( ro==null ) ro = createRemoteObject();

ro.showBusyCursor = true;

var operation:AbstractOperation = ro.getOperation(method);

operation.arguments = args;

var act:AsyncToken = operation.send();

return act;

}

The createRemoteObject() function ties a dynamically created RemoteObject with its_onResult() and onFault() methods. Please notice that we enforced “last” as ro’s concurrency setting, which will result in canceling pending outgoing requests, if any2:

protected function createRemoteObject():RemoteObject {

var ro:RemoteObject;

if( destination==null || destination.length==0 )

throw new Error(“No destination specified”);

ro = new RemoteObject();

ro.destination = destination;

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

return ro;

}

In the case of FaultEvent, we just pop up an optional Alert. But in the ResultEvent handler we

How to Write Your Own Data Management ServicesCHAPTER 7

Page 265: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 257

change the underlying data source of the collection, and that’s why we call the ArrayCollection’s refresh() method updating all the views associated with the collection:

protected function ro_onResult(evt:ResultEvent):void {

CursorManager.removeBusyCursor();

dispatchEvent(evt);

source = evt.result.source;

refresh();

}

In both event handlers we re-dispatch the event to the collection itself. You may want to add more logic here, allowing your collection to prevent the default processing. For instance, you may have some new content appended to the collection instead of wiping out the existing rows. The full list-ing of DestinationAwareCollection is presented in Listing 7.3:

// DestinationAwareCollection.as

package {

import mx.collections.ArrayCollection;

import mx.controls.Alert;

import mx.managers.CursorManager;

import mx.rpc.AbstractOperation;

import mx.rpc.AsyncToken;

import mx.rpc.events.FaultEvent;

import mx.rpc.events.ResultEvent;

import mx.rpc.remoting.mxml.RemoteObject;

[Event(name=”result”, type=”mx.rpc.events.ResultEvent”)]

[Event(name=”fault”, type=”mx.rpc.events.FaultEvent”)]

public class DestinationAwareCollection extends ArrayCollection {

public var destination:String=null;

public var method : String = null;

public var alertOnFault:Boolean=true;

protected var ro:RemoteObject = null;

public function DestinationAwareCollection(source:Array=null){

super(source);

}

public function fill(... args): AsyncToken {

var act:AsyncToken = invoke(method, args);

act.method = “fill”;

return act;

}

How to Write Your Own Data Management ServicesCHAPTER 7

Page 266: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

258 RIA WITH ADOBE FLEX AND JAVA

protected function invoke(method:String, args:Array):AsyncToken {

if( ro==null ) ro = createRemoteObject();

ro.showBusyCursor = true;

var operation:AbstractOperation = ro.getOperation(method);

operation.arguments = args;

var act:AsyncToken = operation.send();

return act;

}

protected function createRemoteObject():RemoteObject {

var ro:RemoteObject;

if( destination==null || destination.length==0 )

throw new Error(“No destination specified”);

ro = new RemoteObject();

ro.destination = destination;

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

return ro;

}

protected function ro_onResult(evt:ResultEvent):void {

CursorManager.removeBusyCursor();

dispatchEvent(evt);

source = evt.result.source;

refresh();

trace(“RESULT: “ + destination + “::” + method + “() returned “ +

evt.result.source.length + “ records”);

}

protected function ro_onFault(evt:FaultEvent):void {

CursorManager.removeBusyCursor();

dispatchEvent(evt);

if (alertOnFault) {

Alert.show(evt.fault.faultString + evt.fault.faultDetail, “Error calling

destination “ + evt.message.destination);

}

}

}

}

Listing 7.3 DestinationAwareCollection.as

How to Write Your Own Data Management ServicesCHAPTER 7

Page 267: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 259

Sensing Collection ChangesWhenever the underlying data of an ArrayCollection changes, it dispatches a CollectionEvent with an event.kind property containing the values “add,” “remove,” “update,” “reset,” etc. By tapping into the mechanism of the CollectionEvent, we are going to improve our destination-aware col-lection. Ultimately we will be maintaining a clone of the original record for each record that’s been modified as well as keeping an exact roll of all new and deleted records. First, though, let’s illustrate the CollectionEvent. Suppose we upgrade our testing application by adding the two buttons Re-move and Add as shown in Figure 7.2:

Figure 7.2 Snapshot of ChangeSensitiveCollectionDemo

The code of the new ChangeSensitiveCollectionDemo testing application is presented in Listing 7.4. We’ve made the data grid editable and changed the collection references from Destination-AwareCollection to ChangeSensitiveCollection:

<?xml version=”1.0” encoding=”UTF-8”?>

<!-- ChangeSensitiveCollectionDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

creationComplete=”onCreationComplete()”>

<mx:DataGrid id=”dg” dataProvider=”{collection}” editable=”true”>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”EMP_FNAME” headerText=”Emp Fname” />

<mx:DataGridColumn dataField=”EMP_LNAME” headerText=”Emp Lname” />

<mx:DataGridColumn dataField=”STREET” headerText=”Street” />

<mx:DataGridColumn dataField=”CITY” headerText=”City” />

<mx:DataGridColumn dataField=”STATE” headerText=”State” />

</mx:Array></mx:columns>

</mx:DataGrid>

How to Write Your Own Data Management ServicesCHAPTER 7

Page 268: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

260 RIA WITH ADOBE FLEX AND JAVA

<mx:ControlBar>

<mx:Button label=”Remove” click=”collection.removeItemAt(dg.selectedIndex)”

enabled=”{dg.selectedIndex != -1}”/>

<mx:Button label=”Add” click=”collection.addItemAt(new EmployeeDTO(),

Math.max(0,dg.selectedIndex+1)) “/>

</mx:ControlBar>

<mx:Script>

<![CDATA[

import ChangeSensitiveCollection;

import com.theriabook.datasource.dto.EmployeeDTO;

[Bindable] private var collection:ChangeSensitiveCollection;

private function onCreationComplete():void {

collection = new ChangeSensitiveCollection();

collection.destination = “Employee”;

collection.method = “getEmployees”;

collection.fill(new Date());

}

]]>

</mx:Script>

</mx:Application>

Listing 7.4 ChangeSensitiveCollectionDemo.mxml

The ChangeSensitiveCollection, a descendant of DestinationAwareCollection, handles the Collec-tionEvent differently (see Listing 7.5). Here, we just trace removed, added, or modified items. As far as added or removed items go, we get them directly via the event.items array. In case of updates, the event.items property carries the array of the PropertyChangeEvent elements, and the currentTarget property of each PropertyChangeEvent element should be used to access the modified item:

// ChangeSensitiveCollection.as

package {

import mx.events.CollectionEvent;

import mx.events.PropertyChangeEvent;

import mx.utils.ObjectUtil;

public class ChangeSensitiveCollection extends DestinationAwareCollection {

override public function set source(s:Array):void {

super.source = s;

list.addEventListener( CollectionEvent.COLLECTION_CHANGE, onCollectionEvent);

}

private function onCollectionEvent(event:CollectionEvent) :void {

How to Write Your Own Data Management ServicesCHAPTER 7

Page 269: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 261

switch(event.kind) {

case “remove”:

for (var i:int = 0; i < event.items.length; i++) {

var item:Object = event.items[i];

trace (“REMOVED:” + mx.utils.ObjectUtil.toString(item));

}

break;

case “add”:

for ( i = 0; i < event.items.length; i++) {

item = event.items[i];

trace (“ADDED:” + mx.utils.ObjectUtil.toString(item) );

}

break;

case “update”:

for (i = 0; i < event.items.length; i++) {

item = null;

var pce:PropertyChangeEvent = event.items[i] as PropertyChangeEvent;

if ( pce != null) {

item = pce.currentTarget;

}

if (item != null) {

trace (“MODIFIED: “ + mx.utils.ObjectUtil.toString(item));

}

}

break;

}

}

}

}

Listing 7.5 ChangeSensitiveCollection.as

If we debug the ChangeSensitiveCollectionDemo and, say, delete one row by clicking “Remove” and type something into another row,3 we’ll see the following trace output:

[SWF] /theriabook/DataManagement/ChangeSensitiveCollectionDemo-debug.swf - 871,671

bytes after decompression

RESULT: Employee::getEmployees() returned 73 records

REMOVED:(com.theriabook.datasource.dto::EmployeeDTO)#0

EMP_FNAME = “Matthew”

EMP_LNAME = “Cobbs”

. . . .

MODIFIED: (com.theriabook.datasource.dto::EmployeeDTO)#0

EMP_FNAME = “Phil”

How to Write Your Own Data Management ServicesCHAPTER 7

Page 270: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

262 RIA WITH ADOBE FLEX AND JAVA

EMP_LNAME = “Chin”

. . . .

As you can see, the CollectionEvent is pretty talkative and we are going to tap into that pretty soon, building a collection that’s capable of managing its state.

Anatomy of Managed ActionScript ObjectsWe need to clarify one very important point: dispatching the CollectionEvent shouldn’t be taken for granted. Earlier we mentioned that the CollectionEvent is dispatched whenever an ArrayCollection “feels” the changes to the underlying data. While this is certainly true, the grim reality is that Array-Collection on its own is pretty senseless. It doesn’t feel anything unless it’s told to. We’re not talking here about collection methods like addItem(x) or removeItem(x). Whenever you use these, the col-lection will obediently dispatch the CollectionEvent with the kind property set to add or remove.

But take a simple assignment like x.firstName=”Joe” where x is the record in the array that backs a collection. Such an assignment might go completely unnoticed by the collection. Luckily, the col-lection API offers an itemUpdated() method to give the application a way to notify the collection of the data modifications.

Please notice the word “might” above; if an array’s elements dispatch the PropertyChangeEvent, collection is notified automatically and the idiosyncratic itemUpdated() is out of a job. Flex collec-tion classes live and breath PropertyChangeEvent so your best bet is to make sure that you “collect” objects that follow the pattern.

Obviously, you can dispatch PropertyChange events explicitly, but this book isn’t about how to make you work harder. Flex automates this task for you. One way to automatically dispatch the PropertyChangeEvent is to use a [Bindable] metadata tag. If you place [Bindable] in front of a class declaration, Flex will generate the code to dispatch a PropertyChange event for all public proper-ties; optionally you may annotate individual properties of the class as [Bindable].

An alternative way of ensuring the dispatch of the PropertyChangeEvent is to use managed classes. If you put [Managed] in front of the class declaration, Flex generates a wrapper class that turns all your properties [Bindable] and implements an IManaged interface comprised of three other inter-faces as shown below:

public interface IManaged extends IPropertyChangeNotifier, IEventDispatcher, IUID

The IPropertyChangeNotifier interface, as the name suggests, mandates dispatching the Property-ChangeEvent. Actually, it goes further. Not only are you supposed to dispatch the event to the proper-ties of your class, but you have to do it for the properties of complex data types as well.

An implementation of this interface is expected to construct update events using a special method of the PropertyChangeEvent class called createUpdateEvent().

How to Write Your Own Data Management ServicesCHAPTER 7

Page 271: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 263

The second interface – IEventDispatcher – is the main interface required to enable the dis-patching of any event. The simplest way to implement IEventDispatcher is to extend the flash.events.EventDispatcher class; another solution is to aggregate an instance of the EventDispatcher acting as a wrapper around it.

Finally, implementers of the IUID interface have to provide a getter/setter pair implementing a uid property.

Loaded with this knowledge, let’s look at the implementation of the EmployeeDTO class, Listing 7.6:

/* Generated by DAOFLEX Utility (ActionScriptDTO_IManaged.xsl) */

package com.theriabook.datasource.dto

{

import flash.events.EventDispatcher;

import mx.data.IManaged;

import mx.events.PropertyChangeEvent;

import mx.core.IUID;

import mx.utils.UIDUtil;

[RemoteClass(alias=”com.theriabook.datasource.dto.EmployeeDTO”)]

[Bindable(event=”propertyChange”)]

public dynamic class EmployeeDTO extends EventDispatcher implements IManaged

{

// Properties

private var _EMP_ID : Number;

private var _EMP_FNAME : String;

. . . .

public function get EMP_ID() : Number{

return _EMP_ID;

}

public function set EMP_ID( value : Number):void{

var oldValue:Object = this._EMP_ID;

if (oldValue !== value) {

this._EMP_ID = value;

dispatchUpdateEvent(“EMP_ID”, oldValue, value);

}

}

public function get EMP_FNAME() : String{

return _EMP_FNAME;

}

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

How to Write Your Own Data Management ServicesCHAPTER 7

Page 272: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

264 RIA WITH ADOBE FLEX AND JAVA

var oldValue:Object = this._EMP_FNAME;

if (oldValue !== value) {

this._EMP_FNAME = value;

dispatchUpdateEvent(“EMP_FNAME”, oldValue, value);

}

}

. . . .

private var _uid:String;

public function get uid():String {

return _uid;

}

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

_uid = value;

}

public function EmployeeDTO() {

_uid = UIDUtil.createUID();

}

private function dispatchUpdateEvent(

propertyName:String, oldValue:Object, value:Object):void {

dispatchEvent(

PropertyChangeEvent.createUpdateEvent(this, propertyName, oldValue, value)

);

}

}//EmployeeDTO

}

Listing 7.6 An example of the AS class implementing an IManaged interface

The class starts with a [RemoteClass] annotation, stating that this class should be marshaled and re-created as its peer com.theriabook.datasource.dto.EmployeeDTO on the server side. Then we have a [Bindable] class-scope annotation. Notice that we’ve implemented all the properties as getter/setter pairs: we couldn’t just let them stay public variables since we’ve been contracted to use createUp-dateEvent(). In other words, we couldn’t let Flex auto-generate the event dispatching code. Besides functional properties like EMP_ID and EMP_FNAME, our class also contains a setter and getter for the uid property; this qualifies the class as an IUID implementer. Since our class is extending the EventDispatcher, it already implements inherently IEventDispatcher, and that completes the list of requirements to qualify the class as an implementation of the IManaged interface.

Everything we’ve done so far can be also achieved by putting a [Managed] annotation in front of the class as shown in Listing 7.7. As you might remember, having your classes managed is a must-have

How to Write Your Own Data Management ServicesCHAPTER 7

Page 273: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 265

requirement with DataServices. That’s why [Managed] or, alternatively, the explicit implementa-tion of IManaged, becomes an ideal common denominator between DataServices and handmade just-in-time collections notified by underlying objects:

/* Generated by DAOFLEX Utility (ActionScriptDTO_Managed.xsl) */

package com.theriabook.datasource.dto

{

import mx.core.IUID;

import mx.utils.UIDUtil;

[RemoteClass(alias=”com.theriabook.datasource.dto.EmployeeDTO”)]

[Managed]

public dynamic class EmployeeDTO

{

// Properties

public var EMP_ID : Number;

public var EMP_FNAME : String;

. . . .

private var _uid:String;

public function get uid():String {

return _uid;

}

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

_uid = value;

}

public function EmployeeDTO() {

_uid = UIDUtil.createUID();

}

}//EmployeeDTO

}

Listing 7.7 An example of a class with a “managed”metadata tag

As a reminder, if you use the DAOFlex utility introduced in Chapter 6, you don’t have to write Ac-tionScript DTO classes at all. DAOFlex generates them based on the SQL metadata that it, in turn, extracts from SQL query or the stored procedure annotating the signature of the Java method. You can also use the daoFlex Eclipse plugin available at http://www.myflex.org. It has many extra fea-tures; in particular, it supports the generation of ActionsScript DTO from existing Java DTO.

How to Write Your Own Data Management ServicesCHAPTER 7

Page 274: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

266 RIA WITH ADOBE FLEX AND JAVA

The Two Faces of ChangedObject Remember the two faces of the Roman god Janus? Janus faces the past and the future at the same time. It may be a stretch, but you can spot traces of this divine idea in the Flex Data Services’ Change-Object interface. Whenever Flex commits accumulated client-side changes, an array of flex.data.ChangeObjects gets sent to the DataService destination on the server. Each ChangeObject carries a pair (previousVersion, newVersion) representing the object (DTO) in two incarnations. Obviously, you don’t need the previousVersion for inserting and the newVersion for deleting.

Now important things: a DataServiceDAO class from Chapter 6 contains a sync() method that takes a single parameter – java.util.List of ChangeObjects. Packaging ChangeObjects and the very invo-cation of sync() have so far been internal FDS affairs. But what stops us from calling sync() methods from ActionScript directly? Nothing. We’re going to enable 100% reuse of the DAO classes between FDS and Flex Remoting.

Here’s the plan. First, we’ll make sure that our change-tracking collection class accumulates the updates, deletes, and inserts done to the collection data. Second, we will teach the collection to pack all the accumulated changes as an array of ActionScript objects, and each of them will contain newVersion and previousVersion along with the create/delete/update state variant: public var state:int;

public var newVersion:Object = null;

public var previousVersion:Object = null;

Last, we’ll make sure that these objects get marshaled into Java objects implementing the flex.data.ChangeObject interface. In Listing 7.8 we indicate that each ChangeObject has to be mar-shaled to its counterpart ChangeObjectImpl in com.theriabook.remoting package4:

package

{

// This line gets required only in UpdatableCollection

[RemoteClass(alias=”com.theriabook.remoting.ChangeObjectImpl”)]

public class ChangeObject

{

public var state:int;

public var newVersion:Object = null;

public var previousVersion:Object = null;

public var changedPropertyNames:Array= null;

public static const UPDATE:int=2;

public static const DELETE:int=3;

public static const CREATE:int=1;

public function ChangeObject(state:int=0, newVersion:Object=null,

previousVersion:Object = null) {

How to Write Your Own Data Management ServicesCHAPTER 7

Page 275: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 267

this.state = state;

this.newVersion = newVersion;

this.previousVersion = previousVersion;

}

public function isCreate():Boolean {

return state==ChangeObject.CREATE;

}

public function isUpdate():Boolean {

return state==ChangeObject.UPDATE;

}

public function isDelete():Boolean {

return state==ChangeObject.DELETE;

}

}

}

Listing 7.8 ChangeObject.as

So let’s implement our plan. As once said, “The goals are clear, tasks are set. To the work, com-rades!”

Tracking Collection ChangesThe simplest way to make a ChangeTrackingCollection is by extending the DestinationAwareCol-lection. Let’s start with extra properties: an array of deleted items and a dictionary of modified ones. Whereas the former contains an array of original items, the latter is a dictionary of ChangeObjects containing (newVersion, previousVersion) pairs:

public var deleted:Array = new Array();

public var modified:Dictionary = new Dictionary();

In addition, we introduce deletedCount and modifiedCount, implemented as getter/setter pairs:

private var _deletedCount : int = 0;

public function get deletedCount():uint {

return _deletedCount;

}

public function set deletedCount(val:uint):void {

var oldValue :uint = _deletedCount ;

_deletedCount = val;

dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this,

“deletedCount”, oldValue, _deleted-

Count));

How to Write Your Own Data Management ServicesCHAPTER 7

Page 276: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

268 RIA WITH ADOBE FLEX AND JAVA

}

private var _modifiedCount : int = 0;

. . . .

Accordingly, we need to modify the onCollectionEvent() handler. Take the remove case, for in-stance. We have to react differently to original and newly inserted items. Deleting the newly in-serted items shouldn’t get reported to the server. Further, we have to be careful about what we ask the server to delete because an item might have been modified and then deleted. In this event it’s the original item that has to be deleted, not the newVersion of it. All along, we maintain the values for modifiedCount and deletedCount:

case “remove”:

for (var i:int = 0; i < event.items.length; i++) {

var item:Object = event.items[i];

var co:ChangeObject = ChangeObject(modified[item]);

var originalItem:Object=null;

if (co == null) { // item has not been modified

originalItem = item;

} else if (!co.isCreate()) { // original item has been modified

originalItem = co.previousVersion;

delete modified[item];

modifiedCount--;

} else { // item has been inserted and modified

delete modified[item];

modifiedCount--;

}

if (originalItem!=null) {

deleted.push(originalItem);

deletedCount = deleted.length;

};

}

break;

. . . .

}

The complete code of ChangeTrackingCollection is in Listing 7.9. Besides maintaining the delet-ed and modified info, our collection includes methods to check and modify the state of the item: isItemNew(), isItemModified(), and setItemNotModified(). Lastly, it includes resetState()methods that clear all knowledge about the deleted and modified items and undoItem() that reverts the item’s properties to their original values:

// ChangeTrackingCollection.as

package {

How to Write Your Own Data Management ServicesCHAPTER 7

Page 277: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 269

import flash.utils.Dictionary;

import mx.events.CollectionEvent;

import flash.events.Event;

import mx.events.PropertyChangeEvent;

import mx.utils.ObjectUtil;

import ChangeObject;

[Bindable(event=”propertyChange”)]

public class ChangeTrackingCollection extends DestinationAwareCollection {

public function ChangeTrackingCollection(source:Array=null){

super(source);

}

public var deleted:Array = new Array();

public var modified:Dictionary = new Dictionary();

private var _deletedCount : int = 0;

public function get deletedCount():uint {

return _deletedCount;

}

public function set deletedCount(val:uint):void {

var oldValue :uint = _deletedCount ;

_deletedCount = val;

dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this,

“deletedCount”, oldValue, _deletedCount));

}

private var _modifiedCount : int = 0;

public function get modifiedCount():uint {

return _modifiedCount;

}

public function set modifiedCount(val:uint ) : void{

var oldValue :uint = _modifiedCount ;

_modifiedCount = val;

dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this,

“modifiedCount”, oldValue, _modifiedCount));

}

private var trackChanges:Boolean = true;

How to Write Your Own Data Management ServicesCHAPTER 7

Page 278: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

270 RIA WITH ADOBE FLEX AND JAVA

override public function set source(s:Array):void {

super.source = s;

list.addEventListener( CollectionEvent.COLLECTION_CHANGE, onCollectionEvent);

}

private function onCollectionEvent(event:CollectionEvent) :void {

if (!trackChanges) return;

switch(event.kind) {

case “remove”:

for (var i:int = 0; i < event.items.length; i++) {

var item:Object = event.items[i];

var co:ChangeObject = ChangeObject(modified[item]);

var originalItem:Object=null;

if (co == null) {

// NotModified

originalItem = item;

} else if (!co.isCreate()) {

// Modified

originalItem = co.previousVersion;

delete modified[item];

modifiedCount--;

} else {

// NewModified

delete modified[item];

modifiedCount--;

}

if (originalItem!=null) {

deleted.push(originalItem);

deletedCount = deleted.length;

};

}

break;

case “add”:

for ( i = 0; i < event.items.length; i++) {

item = event.items[i];

modified[item] = new ChangeObject(ChangeObject.CREATE, item, null);

modifiedCount++;

}

break;

case “update”:

for (i = 0; i < event.items.length; i++) {

item = null;

var pce:PropertyChangeEvent = event.items[i] as PropertyChangeEvent;

How to Write Your Own Data Management ServicesCHAPTER 7

Page 279: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 271

if ( pce != null) {

item = pce.currentTarget;

}

var previousVersion:Object;

if (item != null) {

if(modified[item] == null) {

previousVersion = ObjectUtil.copy(item);

previousVersion[pce.property] = pce.oldValue;

modified[item] = new ChangeObject(ChangeObject.UPDATE, item,

previousVersion);

modifiedCount++;

}

co = ChangeObject(modified[item]);

if (co.changedPropertyNames == null) {

co.changedPropertyNames = [];

}

co.changedPropertyNames.push(pce.property); // TODO avoid duplicates

}

}

break;

}

}

public function isItemNew(item:Object):Boolean {

var co:ChangeObject = modified[item] as ChangeObject;

return (co!=null && co.isCreate());

}

public function isItemModified(item:Object):Boolean {

var co:ChangeObject = modified[item] as ChangeObject;

return (co!=null && !co.isCreate());

}

public function setItemNotModified(item:Object):void {

var co:ChangeObject = modified[item] as ChangeObject;

if (co!=null) {

delete modified[item];

modifiedCount--;

}

}

public function resetState():void {

deleted = new Array();

modified = new Dictionary();

modifiedCount = 0;

deletedCount = 0;

How to Write Your Own Data Management ServicesCHAPTER 7

Page 280: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

272 RIA WITH ADOBE FLEX AND JAVA

}

public function undoItem(item:Object):void {

var co:ChangeObject = modified[item] as ChangeObject;

if (co!=null && !co.isCreate()) {

trackChanges= false;

for each (var property:String in co.changedPropertyNames) {

item[property] = co.previousVersion[property];

}

trackChanges = true;

delete modified[item];

modifiedCount--;

}

}

}

}

Listing 7.9 ChangeTrackingCollection.as

Now let’s play with this collection. We’ll create a ChangeTrackingCollectionDemo demo application with four tabs: “Current,” “Deleted,” “Updated,” and “Created.” At any given moment, each of these tabs will reveal the state of the corresponding item set. In addition, the “Current” tab will contain the buttons “Remove,” “Add,” and “Undo” along with counters for the modified and deleted items:

<mx:TabNavigator id=”tab” change=”onChange(event)”>

<mx:Panel label=”Current”> . . . .

<mx:ControlBar>

<mx:Button label=”Remove” click=”collection.removeItemAt(dg.selectedIndex)

enabled=”{dg.selectedIndex != -1}”/>

<mx:Button label=”Add” click=”collection.addItemAt(new EmployeeDTO(), Math.

max(0,dg.selectedIndex+1)) “/>

<mx:Button label=”Undo”

click=”collection.undoItem(dg.selectedItem) “

enabled=”{collection.isItemModified(EmployeeDTO(dg.selectedItem))}”

/>

<mx:Text text=”Modified:{collection.modifiedCount}”/>

<mx:Text text=”Deleted:{collection.deletedCount}”/>

</mx:ControlBar>

</mx:Panel>

<mx:Canvas label=”Deleted”> . . . .

</mx:Canvas>

How to Write Your Own Data Management ServicesCHAPTER 7

Page 281: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 273

<mx:Canvas label=”Updated”> . . . .

</mx:Canvas>

</mx:TabNavigator>

A snapshot of ChangeTraskingCollectionDemo is illustrated in Figure 7.3. The snapshot was taken after modifying the original value of the “Emp Frame” in the first row from Matthew to Matteus. Notice that the first row is shown in a bold font since the application will declare a Label-based renderer for each column that turns fontWeight to bold, if the item is modified:

<mx:DataGrid id=”dg” dataProvider=”{collection}” editable=”true”>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”EMP_FNAME” headerText=”Emp Fname” >

<mx:itemRenderer><mx:Component>

<mx:Label

fontWeight=”{outerDocument.collection.isItemModified(data)?’bold’:’normal’}”/>

</mx:Component></mx:itemRenderer>

</mx:DataGridColumn>

<mx:DataGridColumn dataField=”EMP_LNAME” headerText=”Emp Lname” > . . . .

</mx:Array></mx:columns>

</mx:DataGrid>

Figure 7.3 A snapshot of ChangeTrackingCollectionDemo (current data)

The next snapshot (see Figure 7.4) was taken when the “Updated” tab was clicked. It shows the original value of the collection item. To be precise, our application will rebuild the dataProviders

How to Write Your Own Data Management ServicesCHAPTER 7

Page 282: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

274 RIA WITH ADOBE FLEX AND JAVA

for the Deleted/Updated/Created data grid when the relevant tab is selected:

private function onChange(event:Object):void {

var co:ChangeObject;

var item:Object;

switch (tab.selectedIndex) {

case 1:

dg_deleted.dataProvider =

collection.deleted;

break;

case 2:

var updated:Array = [];

for ( item in collection.modified) {

co = collection.modified[item];

if (co.isUpdate()) updated.push(

co.previousVersion);

}

dg_updated.dataProvider = updated;

break;

case 3:

var created:Array = [];

for ( item in collection.modified) {

co = collection.modified[item];

if (co.isCreate()) created.push(

co.newVersion);

}

dg_created.dataProvider = created;

break;

}

}

How to Write Your Own Data Management ServicesCHAPTER 7

Page 283: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 275

Figure 7.4 A snapshot of the ChangeTrackingCollectionDemo (original data)

Similarly, you can view the “Deleted” and “Created” items going to the corresponding tabs. The complete code for ChangeTrackingCollectionDemo is presented in Listing 7.10:

<?xml version=”1.0” encoding=”UTF-8”?>

<!-- ChangeTrackingCollectionDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

creationComplete=”onCreationComplete()”>

<mx:TabNavigator id=”tab” change=”onChange(event)”>

<mx:Panel label=”Current”>

<mx:DataGrid id=”dg” dataProvider=”{collection}” editable=”true”>

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”EMP_FNAME” headerText=”Emp Fname” >

<mx:itemRenderer><mx:Component>

<mx:Label

fontWeight=”{outerDocument.collection.isItemModified(data)?’bold’:’normal’}”/>

</mx:Component></mx:itemRenderer>

</mx:DataGridColumn>

<mx:DataGridColumn dataField=”EMP_LNAME” headerText=”Emp Lname” >

<mx:itemRenderer><mx:Component>

<mx:Label

fontWeight=”{outerDocument.collection.isItemModified(data)?’bold’:’normal’}”/>

</mx:Component></mx:itemRenderer>

How to Write Your Own Data Management ServicesCHAPTER 7

Page 284: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

276 RIA WITH ADOBE FLEX AND JAVA

</mx:DataGridColumn>

<mx:DataGridColumn dataField=”STREET” headerText=”Street” >

<mx:itemRenderer><mx:Component>

<mx:Label

fontWeight=”{outerDocument.collection.isItemModified(data)?’bold’:’normal

’}”/>

</mx:Component></mx:itemRenderer>

</mx:DataGridColumn>

<mx:DataGridColumn dataField=”CITY” headerText=”City” >

<mx:itemRenderer><mx:Component>

<mx:Label

fontWeight=”{outerDocument.collection.isItemModified(data)?’bold’:’normal

’}”/>

</mx:Component></mx:itemRenderer>

</mx:DataGridColumn>

<mx:DataGridColumn dataField=”STATE” headerText=”State” >

<mx:itemRenderer><mx:Component>

<mx:Label

fontWeight=”{outerDocument.collection.isItemModified(data)?’bold’:’normal

’}”/>

</mx:Component></mx:itemRenderer>

</mx:DataGridColumn>

</mx:Array></mx:columns>

</mx:DataGrid>

<mx:ControlBar>

<mx:Button label=”Remove” click=”collection.removeItemAt(dg.selectedIndex)”

enabled=”{dg.selectedIndex != -1}”/>

<mx:Button label=”Add” click=”collection.addItemAt(new EmployeeDTO(), Math.

max(0,dg.selectedIndex+1)) “/>

<mx:Button label=”Undo”

click=”collection.undoItem(dg.selectedItem) “

enabled=”{collection.isItemModified(EmployeeDTO(dg.selectedItem))}”

/>

<mx:Text text=”Modified:{collection.modifiedCount}”/>

<mx:Text text=”Deleted:{collection.deletedCount}”/>

</mx:ControlBar>

</mx:Panel>

<mx:Canvas label=”Deleted”>

<mx:DataGrid id=”dg_deleted” >

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”EMP_FNAME” headerText=”Emp Fname” />

How to Write Your Own Data Management ServicesCHAPTER 7

Page 285: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 277

<mx:DataGridColumn dataField=”EMP_LNAME” headerText=”Emp Lname” />

<mx:DataGridColumn dataField=”STREET” headerText=”Street” />

<mx:DataGridColumn dataField=”CITY” headerText=”City” />

<mx:DataGridColumn dataField=”STATE” headerText=”State” />

</mx:Array></mx:columns>

</mx:DataGrid>

</mx:Canvas>

<mx:Canvas label=”Updated”>

<mx:DataGrid id=”dg_updated” >

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”EMP_FNAME” headerText=”Emp Fname” />

<mx:DataGridColumn dataField=”EMP_LNAME” headerText=”Emp Lname” />

<mx:DataGridColumn dataField=”STREET” headerText=”Street” />

<mx:DataGridColumn dataField=”CITY” headerText=”City” />

<mx:DataGridColumn dataField=”STATE” headerText=”State” />

</mx:Array></mx:columns>

</mx:DataGrid>

</mx:Canvas>

<mx:Canvas label=”Created”>

<mx:DataGrid id=”dg_created” >

<mx:columns><mx:Array>

<mx:DataGridColumn dataField=”EMP_FNAME” headerText=”Emp Fname” />

<mx:DataGridColumn dataField=”EMP_LNAME” headerText=”Emp Lname” />

<mx:DataGridColumn dataField=”STREET” headerText=”Street” />

<mx:DataGridColumn dataField=”CITY” headerText=”City” />

<mx:DataGridColumn dataField=”STATE” headerText=”State” />

</mx:Array></mx:columns>

</mx:DataGrid>

</mx:Canvas>

</mx:TabNavigator>

<mx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

import ChangeTrackingCollection;

import com.theriabook.datasource.dto.EmployeeDTO;

[Bindable] public var collection:ChangeTrackingCollection;

private function onCreationComplete():void {

collection = new ChangeTrackingCollection();

collection.destination = “Employee”;

collection.method = “getEmployees”;

collection.fill(new Date());

}

How to Write Your Own Data Management ServicesCHAPTER 7

Page 286: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

278 RIA WITH ADOBE FLEX AND JAVA

private function onChange(event:Object):void {

var co:ChangeObject;

var item:Object;

switch (tab.selectedIndex) {

case 1:

dg_deleted.dataProvider = collection.deleted;

break;

case 2:

var updated:Array = [];

for ( item in collection.modified) {

co = collection.modified[item];

if (co.isUpdate()) updated.push( co.previousVersion);

}

dg_updated.dataProvider = updated;

break;

case 3:

var created:Array = [];

for ( item in collection.modified) {

co = collection.modified[item];

if (co.isCreate()) created.push( co.newVersion);

}

dg_created.dataProvider = created;

break;

}

}

]]>

</mx:Script>

</mx:Application>

Listing 7.10 ChangeTrackingCollectionDemo.mxml

But wait, what was the real purpose of tracking down the changes in the first place? Were we sup-posed to send them up to the server? Patience, dear reader, check the next section.

Making Collection UpdateableOnce we know how to track and accumulate changes, there’s very little left to send these changes to the server. In this section we’ll upgrade our collection to UpdatableCollection and illustrate its work with UpdatableCollectionDemo as shown in Figure 7.5:

How to Write Your Own Data Management ServicesCHAPTER 7

Page 287: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 279

Figure 7.5 A snapshot of UpdateableCollectionDemo

Let’s start with the collection class. We’ll extend the ChangeTrackingCollection and introduce an ad-ditional property – commitRequired, which will be implemented as a getter/setter pair so that any modifications to deletedCount and modifiedCount also “touches” the property commitRequired:

public class UpdatableCollection extends ChangeTrackingCollection {

private var _commitRequired:Boolean = false;

public function set commitRequired(val :Boolean) :void {

if (val!==_commitRequired) {

_commitRequired = val; dispatchEvent(mx.events.Prope

rtyChangeEvent.createUpdateEvent(this,

“commitRequired”, !_commitRequired, _commitRequired));

}

}

public function get commitRequired() :Boolean {

return _commitRequired;

}

public override function set deletedCount(val:uint):void {

super.deletedCount = val;

commitRequired = (modifiedCount>0 || deletedCount>0);

}

How to Write Your Own Data Management ServicesCHAPTER 7

Page 288: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

280 RIA WITH ADOBE FLEX AND JAVA

public override function set modifiedCount(val:uint ) : void{

super.modifiedCount = val;

commitRequired = (modifiedCount>0 || deletedCount>0);

}

. . . .

}

The most notable feature of the UpdatableCollection is its sync() method. It sends an array of changes to the server. Our implementation of sync() will assume success and so we’ll clear the state information in advance, right on the asynchronous send (invoke). However, to handle any potentially unpleasant surprises, we’ll preserve the same state information in the asynchronous token:

public function sync(): void {

var act:AsyncToken = invoke(method + “_sync”, [new

ArrayCollection(changes)]);

act.method = “sync”;

act.modified = modified;

act.deleted = deleted;

act.modifiedCount=modifiedCount;

resetState();

}

public function get changes():Array {

var args:Array = [];

for ( var i :int = 0; i < deleted.length; i++) {

args.push(

new ChangeObject(

ChangeObject.DELETE, null, deleted[i]

)

);

}

for ( var obj:Object in modified) {

args.push( modified[obj]);

}

return args;

}

Now, if things don’t work out, we’ll restore the status quo in the FAULT event handler:

override protected function ro_onFault(evt:FaultEvent):void {

CursorManager.removeBusyCursor();

if (evt.token.method == “sync”) {

modified = evt.token.modified;

How to Write Your Own Data Management ServicesCHAPTER 7

Page 289: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 281

modifiedCount = evt.token.modifiedCount;

deleted = evt.token.deleted;

}

dispatchEvent(evt);

if (alertOnFault) {

Alert.show(evt.fault.faultString, “Error on destination: “

+ evt.message.destination);

}

}

We are all set, short of separating a ResultEvent related to sync() from a ResultEvent originating in response to fill():

override protected function ro_onResult(evt:ResultEvent):void {

CursorManager.removeBusyCursor();

if (evt.token.method == “fill”) {

source = evt.result.source;

trace(destination + “::” + method + “() fill returned “ +

evt.result.source.length + “ records”);

resetState();

refresh();

} else {

trace( destination + “::” + method + “() completed”); }

dispatchEvent(evt);

}

Believe it or not, but this is all there is to the UpdatableCollection; the complete code is in Listing 7.11:

// UpdatableCollection.as

package {

import mx.collections.ArrayCollection;

import mx.controls.Alert;

import mx.events.*;

import mx.managers.CursorManager;

import mx.rpc.AsyncToken;

import mx.rpc.events.*;

import ChangeObject;

public class UpdatableCollection extends ChangeTrackingCollection {

// We need this to allow marshalling of DataSyncException into

// DataErrorMessage as part of FaultEvent

How to Write Your Own Data Management ServicesCHAPTER 7

Page 290: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

282 RIA WITH ADOBE FLEX AND JAVA

// Otherwise we will get something like

// type Coercion failed: cannot convert ... to mx.messaging.messages.IMessage

import mx.data.messages.DataErrorMessage;

private var linkage:DataErrorMessage;

public function UpdatableCollection(source:Array=null){

super(source);

}

private var _commitRequired:Boolean = false;

public function set commitRequired(val :Boolean) :void {

if (val!==_commitRequired) {

_commitRequired = val;

dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this,

“commitRequired”, !_commitRequired, _commitRequired));

}

}

public function get commitRequired() :Boolean {

return _commitRequired;

}

public override function set deletedCount(val:uint):void {

super.deletedCount = val;

commitRequired = (modifiedCount>0 || deletedCount>0);

}

public override function set modifiedCount(val:uint ) : void{

super.modifiedCount = val;

commitRequired = (modifiedCount>0 || deletedCount>0);

}

public function get changes():Array {

var args:Array = [];

for ( var i :int = 0; i < deleted.length; i++) {

args.push(

new ChangeObject(

ChangeObject.DELETE, null, deleted[i]

)

);

}

for ( var obj:Object in modified) {

args.push( modified[obj]);

}

return args;

}

How to Write Your Own Data Management ServicesCHAPTER 7

Page 291: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 283

public function sync(): void {

var act:AsyncToken = invoke(method + “_sync”, [new ArrayCollection(changes)]);

act.method = “sync”;

act.modified = modified;

act.deleted = deleted;

act.modifiedCount=modifiedCount;

resetState();

}

override protected function ro_onFault(evt:FaultEvent):void {

CursorManager.removeBusyCursor();

if (evt.token.method == “sync”) {

modified = evt.token.modified;

modifiedCount = evt.token.modifiedCount;

deleted = evt.token.deleted;

}

dispatchEvent(evt);

if (alertOnFault) {

Alert.show(evt.fault.faultString, “Error on destination: “ +

evt.message.destination);

}

}

override protected function ro_onResult(evt:ResultEvent):void {

CursorManager.removeBusyCursor();

if (evt.token.method == “fill”) {

source = evt.result.source;

trace(destination + “::” + method + “() fill returned “ +

evt.result.source.length + “ records”);

resetState();

refresh();

} else {

trace( destination + “::” + method + “() completed”);

}

dispatchEvent(evt);

}

}

}

Listing 7.11 UpdatableCollection.as

How to Write Your Own Data Management ServicesCHAPTER 7

Page 292: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

284 RIA WITH ADOBE FLEX AND JAVA

Now let’s spin our UpdatableCollection. The testing application, UpdatableCollectionDemo, is practically the same as the one we used to test ChangeTrackingCollection; we’ve just added a “Commit” button and changed the collection references:

<?xml version=”1.0” encoding=”UTF-8”?>

<!-- UpdatableCollectionDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

creationComplete=”onCreationComplete()”

>

<mx:TabNavigator id=”tab” change=”onChange(event)”>

<mx:Panel label=”Current”>

. . . . .

<mx:ControlBar>

<mx:Button label=”Remove” click=”collection.removeItemAt(dg.selectedIndex)”

enabled=”{dg.selectedIndex != -1}”/>

<mx:Button label=”Add” click=”collection.addItemAt(new EmployeeDTO(),

Math.max(0,dg.selectedIndex+1)) “/>

<mx:Button label=”Undo”

click=”collection.undoItem(dg.selectedItem) “

enabled=”{collection.isItemModified(EmployeeDTO(dg.selectedItem))}”

/>

<mx:Button label=”Commit”

click=”collection.sync()”

enabled=”{collection.commitRequired}”

/>

<mx:Text text=”Modified:{collection.modifiedCount}”/>

<mx:Text text=”Deleted:{collection.deletedCount}”/>

</mx:ControlBar>

</mx:Panel>

. . . . .

</mx:TabNavigator>

<mx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

import UpdatableCollection;

import com.theriabook.datasource.dto.EmployeeDTO;

[Bindable] public var collection:UpdatableCollection;

private function onCreationComplete():void {

collection = new UpdatableCollection();

collection.destination = “Employee”;

collection.method = “getEmployees”;

collection.fill(new Date());

How to Write Your Own Data Management ServicesCHAPTER 7

Page 293: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 285

}

]]>

</mx:Script>

</mx:Application>

Listing 7.12 UpdatableCollectionDemo.mxml

All in all we ‘ve taken you through the full cycle: we’ve built a self-sufficient collection that can feed itself with server-side data and that’s capable of synchronizing changes with the server5. As you probably realize by now, three layers of inheritance – DestinationAware/ChangeTracking/Up-dateableCollection – were required purely for didactic purposes. The source code accompanying this book contains the DataCollection in the com.theriabook.collections package that combines all functionality in one class.

Taking Care of Business: TransactionsNow that we’ve tamed DataCollection, let’s think about business applications. More often than not, synchronizing client data with the server can’t be confined to one database table or, in general, to one source of data. Take a simple case: customers place orders, each order can have several items with their own prices, quantities. etc.:

create table simple_order_item(

order_id char(32) not null,

item_id char(32) not null,

product_name varchar(32) not null,

quantity int(11) default ‘1’ not null)

The order, as a whole, has its own attributes, such as the address and order date:

create table simple_order(

order_id char(32) PRIMARY KEY not null,

customer_first_name varchar(32) not null,

customer_last_name varchar(32) not null,

order_date datetime default ‘0000-00-00 00:00:00’ not null,

address varchar(64) not null)

Once the order is placed, the customer can update it. The customer will expect that the order cre-ation as well as the order updates follow the transaction or “unit of work” paradigm: it can be com-pleted only entirely; partial updates won’t happen.

How can we implement transactions in RIA? One thing is certain: BEGIN and COMMIT (or ROLL-BACK) should happen on the server. If our Java DAO is invoked via a DataServices adapter, the BE-GIN has already happened. In a Flex Remoting scenario, we should handle transactions ourselves. Listing 7.13 represents the implementation of the DAO sync() method that would seamlessly work

How to Write Your Own Data Management ServicesCHAPTER 7

Page 294: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

286 RIA WITH ADOBE FLEX AND JAVA

for both DataServices and Remoting:

public final List getOrders_sync(List items)

{

logger.debug(“getOrders_sync(...)”);

UserTransaction userTransaction = null;

boolean localTransaction = false;

boolean commit=false;

try {

Context ctx = new InitialContext();

String jndiName = “java:comp/UserTransaction”;

userTransaction = (UserTransaction)ctx.lookup(jndiName);

if (userTransaction!=null) {

if (userTransaction.getStatus()==Status.STATUS_NO_TRANSACTION) {

localTransaction = true;

userTransaction.begin();

if (logger.isDebugEnabled()) logger.debug(“BEGIN userTransaction”);

} else {

if (logger.isDebugEnabled()) logger.debug(“START PARTICIPATING in external

transaction”);

}

getOrders_deleteItems(items);

getOrders_updateItems(items);

getOrders_insertItems(items);

commit = true;

} else {

logger.error(“Cannot start/take part in a JTA transaction”);

}

} catch(DataSyncException dse) {

dse.printStackTrace();

throw dse;

} catch(Throwable te) {

te.printStackTrace();

throw new DAOException(te.getMessage(), te);

} finally {

if (userTransaction!=null) {

try {

if (localTransaction) {

if (commit) {

userTransaction.commit();

if (logger.isDebugEnabled()) logger.debug(“COMMIT userTransaction”);

} else {

How to Write Your Own Data Management ServicesCHAPTER 7

Page 295: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 287

userTransaction.rollback();

if (logger.isDebugEnabled()) logger.debug(“ROLLBACK

userTransaction”);

}

} else {

if (!commit)

userTransaction.setRollbackOnly();

if (logger.isDebugEnabled()) logger.debug(“Marked ROLLBACKONLY for

external transaction”);

}

} catch (Exception e){

logger.error(“Could not rollback/mark transaction for rollback”, e);

}

}

}

return items;

}

Listing 7.13 A “universal” Java sync() method for FDS and Remoting

This snippet follows the standard Java Transaction API (JTA) to tentatively begin, commit. or roll back transactions. If a transaction has been started earlier, it marks it for rollback with setRollback-Only(); such a transaction can never be committed; it’ll throw an exception if someone attempts to do it. Please notice that we have broken the real sync into three parts, which will be explained a bit later:

getOrders_deleteItems(items);

getOrders_updateItems(items);

getOrders_insertItems(items);

Java Batch Gateway: Many Remote Calls in One ShotThe transaction handled by the sync() method works fine for only one table. In particular, the code in Listing 7.13 ensures that all deletes, updates, and inserts in the simple_order_item table are atomic. But how about transactions that span two tables: simple_orders and simple_order_items?

When we dealt with one DataCollection everything was simple: we remotely invoked the sync-Method and it took care of the transaction. In real life we have to deal with multiple DataCollections at a time. The issue then boils down to:

• Batching changes from multiple collections for a single remote call• Creating a generic Flex Remoting destination that would process the batches so that all the

changes get applied within the boundaries of the external JTA transaction

How to Write Your Own Data Management ServicesCHAPTER 7

Page 296: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

288 RIA WITH ADOBE FLEX AND JAVA

So let’s step back and look at the DataCollection sync method or at any remote method for that matter. They can be completely described by destination, method, and parameter values. The Java’s BatchMember class matches this triad:

package com.theriabook.remoting;

import java.util.*;

public class BatchMember {

public String destinationName;

public String methodName;

public List parameters;

}

Listing 7.14 Java BatchMember class

Setting aside the client’s task of packing matching ActionScript BatchMembers into an ActionScript array, let’s see how we could organize the sequential execution of the batch:

public List executeBatch(List batch) throws Throwable {

List result = new ArrayList();

Iterator iterator = batch.iterator();

while(iterator.hasNext()) {

BatchMember member = (BatchMember)iterator.next();

result.add(invoke( member.destinationName, member.methodName,

member.parameters));

}

return result;

}

The invoke()method also consists of three major parts, which use the Flex Data Services API. First we find the destination by name:

MessageBroker mb = MessageBroker.getMessageBroker(null);

RemotingService srv = (RemotingService)mb.getServiceByType(

“flex.messaging.services.RemotingService”

);

RemotingDestination remotingDestination = (RemotingDestination)srv.getDestinati

onByName(destinationName);

Then we get the object’s instance and calculate the method reference:

FactoryInstance factoryInstance = remotingDestination.getFactoryInstance();

How to Write Your Own Data Management ServicesCHAPTER 7

Page 297: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 289

Object instance = factoryInstance.lookup();

Class clazz = instance.getClass();

MethodMatcher methodMatcher = remotingDestination.getMethodMatcher();

Method method = methodMatcher.getMethod(clazz, methodName, parameters);

Finally, we invoke the method and, in the case of an exception, unwrap the cause of the real excep-tion from within the InvocationTargetException so that by the time the mx.rpc.events.FaultEvent comes to the ActionScript, it carries an exception as if it was a non-batched call:

try {

result = method.invoke(instance, parameters.toArray());

} catch ( InvocationTargetException e) {

logger.error(e, e.getCause());

throw e.getCause();

}finally {

factoryInstance.operationComplete(instance);

}

The complete code of the BatchGateway class is presented in Listing 7.15:

package com.theriabook.remoting;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.util.*;

import flex.data.DataServiceTransaction;

import flex.messaging.FactoryInstance;

import flex.messaging.util.*;

import flex.messaging.services.remoting.RemotingDestination;

import flex.messaging.MessageBroker;

import flex.messaging.services.RemotingService;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.transaction.*;

import org.apache.log4j.Logger;

import com.theriabook.daoflex.JDBCConnection;

public class BatchGateway {

public Object invoke(String destinationName, String methodName, List parameters )

throws IllegalAccessException, IllegalArgumentException, Throwable {

How to Write Your Own Data Management ServicesCHAPTER 7

Page 298: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

290 RIA WITH ADOBE FLEX AND JAVA

// This class throws up all exceptions, since this invoke

// sequentially happens inside the bigger invoke of the JavaAdapter

// which handles all of them anyway. However, we unwrap Invocation

// target to avoid double wrapping.

Object result = null;

MessageBroker mb = MessageBroker.getMessageBroker(null);

RemotingService srv = (RemotingService)mb.getServiceByType(“flex.messaging.services.

RemotingService”);

RemotingDestination remotingDestination = (RemotingDestination)srv.getDestinationBy

Name(destinationName);

FactoryInstance factoryInstance = remotingDestination.getFactoryInstance();

Object instance = factoryInstance.lookup();

Class clazz = instance.getClass();

MethodMatcher methodMatcher = remotingDestination.getMethodMatcher();

Method method = methodMatcher.getMethod(clazz, methodName, parameters);

try {

result = method.invoke(instance, parameters.toArray());

} catch ( InvocationTargetException e) {

logger.error(e, e.getCause());

throw e.getCause();

}finally {

factoryInstance.operationComplete(instance);

}

return result;

}

public List executeBatch(List batch) throws Throwable {

List result = new ArrayList();

Iterator iterator = batch.iterator();

while(iterator.hasNext()) {

BatchMember member = (BatchMember)iterator.next();

result.add(invoke( member.destinationName, member.methodName, member.param-

eters));

}

return result;

}

public List executeTransactionBatch(List batch) throws Throwable {

UserTransaction userTransaction = null;

List result = new ArrayList();

boolean commit=false;

try {

Context ctx = new InitialContext();

How to Write Your Own Data Management ServicesCHAPTER 7

Page 299: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 291

String jndiName = DataServiceTransaction.USER_TX_JNDI_NAME;//”java:comp/User-

Transaction”;

userTransaction = (UserTransaction)ctx.lookup(jndiName);

userTransaction.begin();

if (logger.isDebugEnabled()) logger.debug(“BEGIN userTransaction”);

result = executeBatch(batch);

commit = true;

return result;

} finally {

if (userTransaction!=null) {

if (commit) {

userTransaction.commit();

if (logger.isDebugEnabled()) logger.debug(“COMMIT userTransaction”);

} else {

userTransaction.rollback();

if (logger.isDebugEnabled()) logger.debug(“ROLLBACK userTransaction”);

}

}

}

}

static Logger logger;

static

{

logger = Logger.getLogger(BatchGateway.class);

}

}

Listing 7.15 The Java BatchGateway class

We’ve added executeTransactionBatch(), which has the same mission as executeBatch(); it’s sequen-tially calling all the listed remote methods and all this is done in a JTA transaction.

So Why bother with a non-transactional batch in the first place? Because a batch comes in very handy for retrieving. For example, if your screen presents the data from several tables, you don’t want your application to be in an inconsistent state (half the screen is populated, half is done, half is refreshed, etc).

To be useful, this BatchGateway class has to be declared as a remoting destination:

<?xml version=”1.0” encoding=”UTF-8”?>

<!-- WEB-INF/flex/remoting-config.xml -->

How to Write Your Own Data Management ServicesCHAPTER 7

Page 300: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

292 RIA WITH ADOBE FLEX AND JAVA

<service id=”remoting-service”

class=”flex.messaging.services.RemotingService”

messageTypes=”flex.messaging.messages.RemotingMessage”>

<adapters>

<adapter-definition id=”java-object” class=”flex.messaging.services.remoting.

adapters.JavaAdapter” default=”true”/>

</adapters>

<default-channels>

<channel ref=”my-amf”/>

</default-channels>

. . . .

<destination id=”batchGateway”>

<properties>

<source>com.theriabook.remoting.BatchGateway</source>

</properties>

</destination>

</service>

Listing 7.16 Remoting-config.xml with “batchGateway” destination

BatchMember: Order MattersWe have created our own BatchGateway so let’s see how to use it. To call executeTransactionBatch(), we need to prepare an array of ActionScript classes that will be marshaled into the Java List com.theriabook.remoting.BatchMember objects expected by BatchGateway. Here’s the definition of the matching ActionScript class:

package com.theriabook.remoting {

[RemoteClass(alias=”com.theriabook.remoting.BatchMember”)]

public class BatchMember {

public var destinationName:String;

public var methodName:String;

public var parameters:Array;

public function BatchMember(destinationName:String, methodName:String,

parameters:Array=null) {

this.destinationName = destinationName;

this.methodName = methodName;

this.parameters = parameters;

}

How to Write Your Own Data Management ServicesCHAPTER 7

Page 301: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 293

public function toString():String { return “com.theriabook.remoting.BatchMember {

“+” destinationName:”+destinationName+” methodName:”+methodName+”

parameters:”+parameters + “}” };

}

}

Listing 7.17 ActionScript BatchMember class

We are almost there: for each collection that needs to join to the batch, we can write something like:

var bm:BatchMember;

var batch:Array=[];

bm = new BatchMember(

orderCollection.destination,

orderCollection.syncMethod,

[orderCollection.changes]

);

batch.push(bm);

Then we could place a remote call to BatchGateway and process the callbacks. Mission accom-plished…or so it seems.

Here’s the problem, however. The simple_order and simple_order_item database tables represent a master/detail relationship. In referential integrity terms, simple_order_item could have had a foreign key on the column order_id from simple_order. In this scenario you won’t be able to insert an item with a non-existent foreign key value; your transaction would succeed only if you inserted orders first.

In the case of a delete operation, a foreign key definition could possibly prescribe a cascade-dele-tion of all dependent order items, when an order is being deleted. That’s something we could live with. But how about this: a foreign key could restrict the deletion of the order until the depen-dant order items exist. In other words, try to sync your collections in the wrong order and you’re doomed!

BatchServiceTo solve the problem identified above, we’ll create a BatchService class that once given a collection, adds three BatchMembers to the batch: one with deletes, one with inserts, and one with updates. In the case of more than one collection, our BatchService will consider its priorities and add deletes for all the collections in reverse order of priority, then add inserts and updates for all – in the opposite order.

Earlier we promised to explain why on the Java DAO side we’ve split the job of sync() into three calls of this sequence:

How to Write Your Own Data Management ServicesCHAPTER 7

Page 302: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

294 RIA WITH ADOBE FLEX AND JAVA

getOrders_deleteItems(items);

getOrders_updateItems(items);

getOrders_insertItems(items);

The reason is the need to expose these methods for remoting. As Anton Chekhov used to say6: “If in the first act you’ve hung a pistol on the wall, then in the following one it should be fired.”

We’ll see how pistols fire from inside our BatchService after introducing a BatchService’s API. Sup-pose we have two collections – orders and orderItems:

orders = new DataCollection();

orders.destination=”Order”;

orders.method=”getOrders”;

orderItems = new DataCollection();

orderItems.destination=”Order”;

orderItems.method=”getOrderItems”;

Once the collections are created, we register them with the BatchService specifying their priorities:

batchService = new BatchService();

batchService.addEventListener(FaultEvent.FAULT, onCommitFault);

batchService.registerCollection(orders,0);

batchService.registerCollection(orderItems,1);

When it’s time to synchronize – perhaps when the user clicks on the Save button – we ask the Batch-Service to make the batch and send it to the BatchGateway:

var batch:Array = batchService.batchRegisteredCollections();

batchService.sendBatch(batch);

That’s all there is to a BatchService API and we can turn our attention to the implementation, see Listing 7.18. The focal point here is the batchRegisteredCollections() method. This method sends accumulated collection changes three times,7 which, in case of a getOrdersDAO, translates to a sequential call via the BatchGateway:

getOrders_deleteItems(items)

getOrders_updateItems(items)

getOrders_insertItems(items)

The other two public methods – registerCollection() and sendBatch() – are rather simple. Please note how we re-dispatch ResultEvent and FaultEvent from the remote object to the instance of the BatchService so that the application can process it accordingly.

How to Write Your Own Data Management ServicesCHAPTER 7

Page 303: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 295

package com.theriabook.remoting

{

import com.theriabook.collections.DataCollection;

import com.theriabook.remoting.BatchMember;

import mx.rpc.events.*;

import mx.managers.CursorManager;

import mx.rpc.*;

import mx.rpc.remoting.mxml.RemoteObject;

import flash.events.EventDispatcher;

[Event(name=”result”, type=”mx.rpc.events.ResultEvent”)]

[Event(name=”fault”, type=”mx.rpc.events.FaultEvent”)]

public class BatchService extends EventDispatcher

{

public static const BATCH_GATEWAY_DESTINATION:String=”batchGateway”;

protected var ro:RemoteObject = null;

private var registeredCollections:Array=[];

public function registerCollection(collection:DataCollection, priority:int=0):void

{

registeredCollections.push({collection:collection, priority:priority});

}

public function batchRegisteredCollections():Array {

var batch:Array = [];

registeredCollections.sort(sortOnPriority);

var collection:DataCollection;

var member:BatchMember;

for (var i:int=registeredCollections.length-1; i>=0; i-- ) {

collection = registeredCollections[i].collection as DataCollection;

member = new BatchMember(

collection.destination,

collection.method + “_deleteItems”,

[collection.changes]

);

batch.push(member);

}

for (i=0; i< registeredCollections.length; i++ ) {

collection = registeredCollections[i].collection as DataCollection;

member = new BatchMember(

collection.destination,

collection.method + “_updateItems”,

How to Write Your Own Data Management ServicesCHAPTER 7

Page 304: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

296 RIA WITH ADOBE FLEX AND JAVA

[collection.changes]

);

batch.push(member);

}

for (i=0; i< registeredCollections.length; i++ ) {

collection = registeredCollections[i].collection as DataCollection;

member = new BatchMember(

collection.destination,

collection.method + “_insertItems”,

[collection.changes]

);

batch.push(member);

}

return batch;

}

public function sendBatch(batch:Array, transaction:Boolean=true): AsyncToken {

if( ro==null ) ro = createRemoteObject();

var operation:AbstractOperation =

ro.getOperation(transaction?”executeTransactionBatch”:”executeBatch”);

operation.arguments = [batch];

var act:AsyncToken = operation.send();

act.arguments = batch;

return act;

}

private function createRemoteObject():RemoteObject {

var ro:RemoteObject = new RemoteObject(BATCH_GATEWAY_DESTINATION);

ro.showBusyCursor = true;

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

return ro;

}

private function ro_onFault(event:FaultEvent):void {

CursorManager.removeBusyCursor();

dispatchEvent(event);

}

private function ro_onResult(evt:ResultEvent):void {

var collection:DataCollection;

for (var i:int=0; i< registeredCollections.length; i++ ) {

collection = registeredCollections[i].collection as DataCollection;

How to Write Your Own Data Management ServicesCHAPTER 7

Page 305: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 297

collection.resetState();

}

CursorManager.removeBusyCursor();

dispatchEvent(evt);

}

private function sortOnPriority(a:Object, b:Object):int {

var pa:int = a.priority;

var pb:int = b.priority;

if (pa > pb) {

return 1;

} else if (pa < pb) {

return -1;

} else {

//pa == pb

return 0;

}

}

}

}

Listing 7.18 BatchService.as

A Sample Application – An Order Entry The following sections describe a sample application – OrderEntryDemo – that allows transac-tional data entry into the tables simple_order and simple_order_item. The application is composed of two panels: OrdersPanel and OrderItemsPanel with controller class – OrderManager as shown in Listing 7.19:

<?xml version=”1.0” encoding=”UTF-8”?>

<!--OrderEntryDemo.mxml -->

<mx:Application

xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=”*”

>

<OrderManager id=”orderManager” />

<mx:ControlBar>

<mx:Button label=”Fill” click=”orderManager.fillOrders()” />

<mx:Button label=”Commit” click=”orderManager.commit()” enabled=”{orderManager.com

mitRequired}” />

</mx:ControlBar>

<mx:VDividedBox >

<OrdersPanel id=”master” />

How to Write Your Own Data Management ServicesCHAPTER 7

Page 306: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

298 RIA WITH ADOBE FLEX AND JAVA

<OrderItemsPanel id=”detail” width=”100%”/>

</mx:VDividedBox>

</mx:Application>

Listing 7.19 OrderEntryDemo.mxml

The snapshot of a running OrderEntryDemo is presented in Figure 7.6. A click on the Fill button populates the Orders panel and the selection of a particular order repopulates OrderItems. When-ever the data is modified on either panel, the Commit button gets enabled and you can click on it to save the data changes to simple_order and simple_order_item in one transaction:

Figure 7.6 A snapshot from the OrderEntryDemo application

OrderEntryDemo Pre-requisitesWe’re not going to burden the reader again with the details of preparing DAO, Java, and Action-Script DTO, etc. Suffice it to say that to get everything in one shot, we process the source code of

How to Write Your Own Data Management ServicesCHAPTER 7

Page 307: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 299

the Java class below with a DAOFlex generator:

package com.theriabook.datasource;

import java.util.List;

/**

* @daoflex:webservice

* pool=jdbc/theriabook

*/

public abstract class Order {

/**

* @daoflex:sql

* sql=::

select order_id, customer_first_name firstName,

customer_last_name lastName, order_date, address

from simple_order

::

* transferType=OrderDTO[]

* keyColumns=order_id

* updateTable=simple_order

*/

public abstract List getOrders();

/**

* @daoflex:sql

* sql=select * from simple_order_item WHERE ORDER_ID=:orderId

* transferType=OrderItemDTO[]

* updateTable=simple_order_item

* keyColumns=order_id,item_id,product_name

*/

public abstract List getOrderItems(String orderId);

}

Listing 7.20 The DAOFlex Java source file for an OrderEntryDemo application

As a result, all classes and JARs are properly deployed, and the only remaining thing to take care of is that our remoting-service.xml points to a generated destination-include file Order.xml:

<?xml version=”1.0” encoding=”UTF-8”?>

<service id=”remoting-service”

class=”flex.messaging.services.RemotingService”

How to Write Your Own Data Management ServicesCHAPTER 7

Page 308: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

300 RIA WITH ADOBE FLEX AND JAVA

messageTypes=”flex.messaging.messages.RemotingMessage”>

<adapters>

<adapter-definition id=”java-object” class=”flex.messaging.services.remoting.

adapters.JavaAdapter” default=”true”/>

</adapters>

<default-channels>

<channel ref=”my-amf”/>

</default-channels>

<destination id=”batchGateway”>

<properties>

<source>com.theriabook.remoting.BatchGateway</source>

</properties>

</destination>

<destination-include file-path=”daoflex/remoting-config/Order.xml” />

</service>

Listing 7.21 Remoting-config.xml with a manually included Order.xml

<?xml version=”1.0” encoding=”UTF-8”?>

<!—-Order.xml.

Generated by DaoFlex via remoting-config.xsl-->

<destination id=”Order”>

<properties>

<source>com.theriabook.datasource.OrderDAO</source>

</properties>

</destination>

Listing 7.22 Generated Order.xml with an Order destination

OrderEntryDemo: OrdersPanelNow let’s look at the details of the application code. The top panel of the application, OrdersPanel, binds its data grid to the collection, a private variable of the component:

<mx:DataGrid id=”dg” dataProvider=”{collection}” …/>

Importantly, this variable, upon execution of the onCreationComplete(), carries a reference to a bindable orders collection hosted by the OrderManager. In other words, the data grid of orders serves as a pure view of the orders collection from the OrderManager:

How to Write Your Own Data Management ServicesCHAPTER 7

Page 309: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 301

private function onCreationComplete() : void {

orderManager = Application.application.orderManager;

collection = orderManager.orders;

}

The buttons Add and Remove delegate their work to the orderManager in a clean MVC style:

private function onAdd(position:int):void {

orderManager.addOrder(position);

dg.selectedIndex = position;

}

private function onRemove(position:int):void {

orderManager.removeOrder(position);

}

Finally, whenever the user is changing the row selection, we communicate the selected orderId to the OrderManager:

<mx:DataGrid id=”dg” dataProvider=”{collection}”

change=”orderManager.orderId=dg.selectedItem.ORDER_ID”>

The complete code for the OrdersPanel is presented in Listing 7.23:

<?xml version=”1.0” encoding=”UTF-8”?>

<!-- OrdersPanel.mxml -->

<mx:Panel title=”Orders”

xmlns:mx=”http://www.adobe.com/2006/mxml”

creationComplete=”onCreationComplete()”>

<mx:DataGrid id=”dg” dataProvider=”{collection}” editable=”true” height=”100%”

change=”orderManager.orderId=dg.selectedItem.ORDER_ID”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”ORDER_ID” headerText=”Order Id” />

<mx:DataGridColumn dataField=”FIRSTNAME” headerText=”First Name” />

<mx:DataGridColumn dataField=”LASTNAME” headerText=”Last Name” />

<mx:DataGridColumn dataField=”ADDRESS” headerText=”Address” />

<mx:DataGridColumn dataField=”ORDER_DATE” headerText=”Order Date”

itemEditor=”mx.controls.DateField” editorDataField=”selectedDate”/>

</mx:Array>

</mx:columns>

</mx:DataGrid>

<mx:ControlBar width=”100%”>

<mx:Button label=”Remove” click=”onRemove(dg.selectedIndex)” enabled=”{dg.selectedI

How to Write Your Own Data Management ServicesCHAPTER 7

Page 310: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

302 RIA WITH ADOBE FLEX AND JAVA

ndex != -1}”/>

<mx:Button label=”Add” click=”onAdd(Math.max(0,dg.selectedIndex+1)); “/>

<mx:Label text=”Deleted: {collection.deletedCount}”/>

<mx:Label text=”Modified: {collection.modifiedCount}”/>

</mx:ControlBar>

<mx:Script>

<![CDATA[

import mx.core.Application;

import com.theriabook.collections.DataCollection;

[Bindable]

private var collection:DataCollection;

private var orderManager:OrderManager ;

private function onCreationComplete() : void {

orderManager = Application.application.orderManager;

collection = orderManager.orders;

}

private function onAdd(position:int):void {

orderManager.addOrder(position);

dg.selectedIndex = position;

}

private function onRemove(position:int):void {

orderManager.removeOrder(position);

}

]]>

</mx:Script>

</mx:Panel>

Listing 7.23 OrdersPanel.mxml

OrderEntryDemo: OrderItemsPanelThe bottom panel of the application, OrderItemsPanel, populates its data grid with data from a bindable orderItems collection, hosted by the OrderManager:

<mx:DataGrid id=”dg” dataProvider=”{collection}” editable=”true” height=”100%”

…./>

[Bindable]private var collection:DataCollection ;

[Bindable]private var orderManager:OrderManager ;

private function onCreationComplete() : void {

How to Write Your Own Data Management ServicesCHAPTER 7

Page 311: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 303

orderManager = Application.application.orderManager;

collection = orderManager.orderItems;

}

Similar to the OrdersPanel, the buttons Add and Remove delegate their work to the OrderMan-ager:

private function onAdd(position:int):void {

orderManager.addOrderItem(position);

dg.selectedIndex = position;

}

private function onRemove(position:int):void {

orderManager.removeOrderItem(position);

}

The complete code of the OrderItemsPanel is presented in Listing 7.24:

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

<!-- OrderItemsPanel.mxml -->

<mx:Panel title=”OrderItems”

xmlns:mx=”http://www.adobe.com/2006/mxml”

creationComplete=”onCreationComplete()”

>

<mx:DataGrid id=”dg” dataProvider=”{collection}” editable=”true” height=”100%”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”ORDER_ID” headerText=”Order Id” />

<mx:DataGridColumn dataField=”ITEM_ID” headerText=”Item Id” />

<mx:DataGridColumn dataField=”PRODUCT_NAME” headerText=”Product” />

<mx:DataGridColumn dataField=”QUANTITY” headerText=”Quantity” />

</mx:Array>

</mx:columns>

</mx:DataGrid>

<mx:ControlBar width=”100%”>

<mx:Button label=”Remove” click=”onRemove(dg.selectedIndex);” enabled=”{dg.selected

Index != -1}”/>

<mx:Button label=”Add” click=”onAdd(Math.max(0,dg.selectedIndex + 1)); “

enabled=”{orderManager.orderId!=null}”/>

<mx:Label text=”Deleted: {collection.deletedCount}”/>

<mx:Label text=”Modified: {collection.modifiedCount}”/>

</mx:ControlBar>

<mx:Script>

<![CDATA[

How to Write Your Own Data Management ServicesCHAPTER 7

Page 312: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

304 RIA WITH ADOBE FLEX AND JAVA

import mx.core.Application;

import com.theriabook.collections.DataCollection;

[Bindable]private var collection:DataCollection ;

[Bindable]private var orderManager:OrderManager ;

private function onCreationComplete() : void {

orderManager = Application.application.orderManager;

collection = orderManager.orderItems;

}

private function onAdd(position:int):void {

orderManager.addOrderItem(position);

dg.selectedIndex = position;

}

private function onRemove(position:int):void {

orderManager.removeOrderItem(position);

}

]]>

</mx:Script>

</mx:Panel>

Listing 7.24 OrderItemsPanel.mxml

OrderEntryDemo: OrderManagerA class OrderManager is also important. It encapsulates orders and orderItems collections along with the methods that manipulate these collections.

The most important things here are the methods that populate collections and communicate the changes to the server. Populating is rather trivial; we delegate it to the fill() method of the appropri-ate collection:

orders.fill();

orderItems.fill(orderId);

To send the changes to the server in one transactional batch, we use the BatchService:

batchService = new BatchService();

batchService.addEventListener(FaultEvent.FAULT, onCommitFault);

batchService.registerCollection(orders,0);

batchService.registerCollection(orderItems,1);

. . . .

var batch:Array = batchService.batchRegisteredCollections();

batchService.sendBatch(batch);

How to Write Your Own Data Management ServicesCHAPTER 7

Page 313: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 305

How do you determine that changes are pending on a DataCollection? You can use the bind-able property commitRequired. And what if you have several DataCollections? Please notice the technique we used to determine if anything needs to be saved on at least one of the DataCollec-tions:

[Bindable]public var commitRequired:Boolean;

. . .

orders.addEventListener(

PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChange

);

orderItems.addEventListener(

PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChange

);

. . .

public function onPropertyChange(event:PropertyChangeEvent):void {

if (event.property == “commitRequired”) {

commitRequired = (

orders.commitRequired || orderItems.commitRequired

);

}

}

We have covered the most interesting details of OrderManager. The complete code for the Order-Manager class is in Listing 7.25:

// OrderManager.as

package

{

import flash.events.EventDispatcher;

import mx.events.PropertyChangeEvent;

import mx.rpc.events.FaultEvent;

import mx.controls.Alert;

import com.theriabook.collections.DataCollection;

import com.theriabook.datasource.dto.OrderDTO;

import com.theriabook.datasource.dto.OrderItemDTO;

import com.theriabook.remoting.BatchService;

public class OrderManager

{

[Bindable]public var orders:DataCollection ;

[Bindable]public var orderItems:DataCollection ;

How to Write Your Own Data Management ServicesCHAPTER 7

Page 314: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

306 RIA WITH ADOBE FLEX AND JAVA

[Bindable]public var commitRequired:Boolean;

private var _orderId:String;

[Bindable]

public function set orderId(newOrderId:String):void {

_orderId = newOrderId;

fillOrderItems(newOrderId);

}

public function get orderId():String {

return _orderId;

}

public var batchService:BatchService;

public function OrderManager(){

orders = new DataCollection();

orders.destination=”Order”;

orders.method=”getOrders”;

orderItems = new DataCollection();

orderItems.destination=”Order”;

orderItems.method=”getOrderItems”;

batchService = new BatchService();

batchService.addEventListener(FaultEvent.FAULT, onCommitFault);

batchService.registerCollection(orders,0);

batchService.registerCollection(orderItems,1);

orders.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChange);

orderItems.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onProper-

tyChange);

}

public function onPropertyChange(event:PropertyChangeEvent):void {

if (event.property == “commitRequired”) {

commitRequired = (orders.commitRequired || orderItems.commitRequired);

}

}

public function fillOrders():void {

orders.fill();

}

public function fillOrderItems(orderId:String):void {

orderItems.fill(orderId);

}

public function commit():void {

var batch:Array = batchService.batchRegisteredCollections();

How to Write Your Own Data Management ServicesCHAPTER 7

Page 315: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 307

batchService.sendBatch(batch);

}

public function onCommitFault(event:Object):void {

Alert.show(event.fault.faultString , “Error”);

}

public function addOrder (position:int):OrderDTO {

var item:OrderDTO = new OrderDTO();

item.ORDER_DATE = new Date();

orders.addItemAt(item, position);

orderItems.removeAll();

orderItems.resetState(); // We do not want deletes, don’t we?

return item;

}

public function removeOrder(position:int):void {

orderItems.removeAll();

orders.removeItemAt(position);

commit();

}

public function addOrderItem (position:int):OrderItemDTO {

var item:OrderItemDTO = new OrderItemDTO();

item.ORDER_ID = orderId;

item.QUANTITY = 1;

orderItems.addItemAt(item, position);

return item;

}

public function removeOrderItem(position:int):void {

orderItems.removeItemAt(position);

}

}

}

Listing 7.25 OrderManager.as

ConclusionFlex Remoting, as this chapter illustrates, can be a simplified alternative to Flex Data Services. We didn’t attempt to provide all the functionality offered by FDS. For example, we didn’t address the data push and pagination that come free with FDS. And yet, there are distinct advantages to using Flex Remoting:

• Your server’s memory isn’t burdened with persisting identities for each subscribed collection. • You don’t depend on session replication or, for that matter, any clustering features.

How to Write Your Own Data Management ServicesCHAPTER 7

Page 316: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

308 RIA WITH ADOBE FLEX AND JAVA

• You have unlimited flexibility in managing your data from the ActionScript side.

Either mechanism, as we have shown in this and previous chapters, requires building a routine set of Java and ActionScript objects, a process that with an open source DAOFlex generator utility can be completly automated.

How to Write Your Own Data Management ServicesCHAPTER 7

Endnotes1. Flex Data Services terminology might look a bit confusing. At the architectural level, Flex Data Services

encompass Data Management Services, Remoting Services and Messaging Services. At the API level,

DataService object maps to a Data Management Service, which is only a part of the FDS. This chapter

discusses data management with remoting, which is also a part of FDS. Please be aware that elsewhere in

the chapter, when we refer to Data Services we mean a specific part of the Data Management Service.

2. We mean a request possibly pending for this remote object. Flex accumulates requests in batches; a batch

is sent to the Flex MessageBroker servlet after a cycle of script execution and drawing is complete.

3. To be precise, typing alone is not enough: you need to change the focus from the modified cell to activate

the lower-level event that, eventually, results in CollectionEvent.

4. We have omittedthe implementation details of com.theriabook.remoting.ChangeObjectImpl for brevity,

but you can find the code in the accompanying CD and download.

5. In all honesty, we can’t credit UpdatableCollection with being completely self-sufficient. At the end of the

day it takes two to tango, and without a server-side DAO class it wouldn’t happen.

6. A Russian doctor and short story author who became a world famous playwrite.

7 . You may suggest that we don’t send the entire list of changes each time. Indeed, the copy of BatchSer-

vice that comes with the book extracts deletes, updates, and inserts separately and sends only what is

required. We omitted these details here for clarity’s sake.

Page 317: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 311

Enhancing and Extending Flex Controls

8CHAPTER

Page 318: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

312 RIA WITH ADOBE FLEX AND JAVA

The Flex framework contains a pretty impressive Flex library of off-the-shelf controls, which can fit the bill for many rich Internet applications needs. Yet, it’s just the tip of the iceberg, because Flex lets you create, combine, and extend existing components with a simplicity and elegance hardly ever offered by other GUI development systems.

In this chapter we’re going to lead you step-by-step through the process of extending a standard ComboBox component, which is a combination of an edit field, button, and dropdown list. We’ll be customizing the API and adding some new functionality, making our ComboBox a bit handier than the standard one. In particular, we’ll create a ComboBox with a multi-column dropdown list, then we’ll create a “destination-aware” ComboBox populated from the server using an RPC call, and we’ll show you how to implement the autocomplete feature for editable controls.

ComboBox ChallengesA typical task, while working with a standard ComboBox, is to programmatically select a specific value. Suppose our ComboBox is populated with an array of states:

private var usStates:Array=[

{label:”New York”, data:”NY”},

{label:”Colorado”, data:”CO”},

{label:”Texas”, data:”TX”}

];

. . . . . . . .

<mx:ComboBox id=”cbx_states” dataProvider=”{states}”/>

To programmatically select Texas (in the visible portion of the ComboBox), you can write the following index-fetching loop, comparing val against the label of each element of the dataPro-vider:

var val:String;

Enhancing and Extending Flex ControlsCHAPTER 8

Enhancing and Extending Flex Controls

Page 319: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 313

val = ’Texas’ ;

for (var i: int = 0; i < cbx.dataProdider.length; i++) {

if ( val == cbx_states.dataProvider[i].label) {

cbx_states.selectedIndex = i;

break;

}

}

Alternatively, you could look up the data of the dataProvider’s elements:

var val:String;

val = ‘TX’ ;

for (var i: int = 0; i < cbx.dataProdider.length; i++) {

if ( val == cbx.dataProvider[i].data) {

cbx_states.selectedIndex = i;

break;

}

}

Either way these index-fetching loops will clutter the application code instead of the simple cbx_states.value=‘Texas.’

On top of that, index fetching via data is often unapplicable. Consider real-life ComboBox records coming from databases, messages, etc. We can’t “mandate” these data sources to contain a data field in the relevant record sets. And while the standard ComboBox provides a labelField property, allowing us to draw a label value from an arbitrary property, there’s no dataField property, which would provide a similar flexibility for data.

So far we’ve identified what we would like to improve in setting the value. Now let’s look at the op-posite operations. The standard ComboBox offers the properties selectedIndex and selectedItem. When a ComboBox is populated with strings, selectedItem returns the selected string (or null if nothing is selected). If it’s populated with objects, selectedItem references the selected object (or contains null):

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml creationComplete=”onCreationComp

lete()”>

private function onCreationComplete():void {

mx.control.Alert.show(cbx_states.selectedItem.label); // displays ‘New York’

cbx_states.selectedIndex=-1; //Removes initial selection

mx.control.Alert.show(cbx_states.selectedItem); // “displays” null

}

. . . . . . . . .

Enhancing and Extending Flex ControlsCHAPTER 8

Page 320: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

314 RIA WITH ADOBE FLEX AND JAVA

</mx:Application>

Listing 8.1 Using selectedItem and selectedIndex properties

But wait, there’s also a read-only value property: if a selected object has something in the data property, the value refers to data, otherwise value refers to the label:

mx.control.Alert.show(cbx_states.value); // displays ‘NY’

As you can see value does half the job: it gives us “read” access and shields us from selectedItem/ selectedIndex. What we miss is the other half and in the following sections we’ll turn value into a read-write property. That will forever absolve us from index-fetching loops to modify the Combo-Box selection.

We’ll also introduce the dataField property to allow any arbitrary property in place of the data, depending on a specific ComboBox use case.

Making the Value Property WriteableLet’s start with upgrading the value to a first-class writeable property. The simplest way to do this is by extending the original ComboBox so that the derived class provides a special setter for the value property. The setter attempts to find the item in the dataProvider’s collection with either data or la-bel property matching the new value of value. Once a match is found, it modifies the selectedIndex, which should cause the ComboBox to select the matching object:

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

<mx:ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml” >

<mx:Script>

<![CDATA[

public function set value(val:Object) : void {

if ( val != null ) {

for (var i : int = 0; i < dataProvider.length; i++) {

if ( val == dataProvider[i].data || val == dataProvider[i].label) {

selectedIndex = i;

return;

} } }

selectedIndex = -1;

}

]]>

</mx:Script>

</mx:ComboBox>

Listing 8.2 Making the value property writeable

Enhancing and Extending Flex ControlsCHAPTER 8

Page 321: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 315

If the ComboBox.mxml is located under the com/theriabook/controls, it’s test application can look like Listing 8.3.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” xmlns:lib=”com.the-

riabook.controls.*”>

<mx:ArrayCollection id=”comboData” >

<mx:Array>

<mx:Object label=”New York” data=”NY”/>

<mx:Object label=”Connecticut” data=”CT”/>

<mx:Object label=”Illinois” data=”IL”/>

</mx:Array>

</mx:ArrayCollection>

<mx:Label text=”Current bound value is &quot;{cbx_1.value}&quot;” />

<lib:ComboBox id=”cbx_1” value=”IL” width=”150” dataProvider=”{comboData}”/>

</mx:Application>

Listing 8.3 Using our new ComboBox

Run this application and you’ll see the ComboBox displaying the value New York when we’d expect Illinois. We forgot about the order in which objects’ properties get initialized. The value property in our case is initialized before the dataProvider. Then the initialization of dataProvider makes the first (default) item selected and the work done by our value setter is wasted. You can prove the point just by trading the places of the value and dataProvider in the application code above.

Should we rely on the order of the attributes in the MXML components? Apparently not. Especially when Flex offers an excellent mechanism to coordinate the updates to multiple properties of the control – the commitProperties() method.

Here’s how it works: whenever you need to modify a property, raise some indicator, store the value in the temporary variable, and call invalidateProperties() as in the following snippet:

private var candidateValue:Object;

private var valueDirty:Boolean = false;

public function set value(val:Object) : void {

candidateValue = val;

valueDirty = true;

invalidateProperties();

}

In response to invalidateProperties() Flex will schedule a call of commitProperties() for later execu-tion so that all property changes deferred in the above manner can be consolidated in a single place and in a pre-determined order:

Enhancing and Extending Flex ControlsCHAPTER 8

Page 322: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

316 RIA WITH ADOBE FLEX AND JAVA

override protected function commitProperties():void {

super.commitProperties();

if (dataProviderDirty) {

super.dataProvider = candidateDataProvider;

dataProviderDirty = false;

}

if (valueDirty) {

applyValue(candidateValue);

valueDirty = false;

}

}

Aside from co-ordinating updates to different properties, this coding pattern helps to avoid mul-tiple updates to the same property and, in general, allows setter methods to return faster, improv-ing the overall performance of the control. The entire code of our “value-aware” ComboBox is pre-sented in Listing 8.4:

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

<mx:ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml” >

<mx:Script>

<![CDATA[

private var candidateValue:Object;

private var valueDirty:Boolean = false;

private var candidateDataProvider:Object;

private var dataProviderDirty:Boolean = false;

private function applyValue(val:Object):void {

if ((val != null) && (dataProvider != null)) {

for (var i : int = 0; i < dataProvider.length; i++) {

if ( val == dataProvider[i].data || val == dataProvider[i].label) {

selectedIndex = i;

return;

} } }

selectedIndex = -1;

}

public function set value(val:Object) : void {

candidateValue = val;

valueDirty = true;

invalidateProperties();

Enhancing and Extending Flex ControlsCHAPTER 8

Page 323: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 317

}

override public function set dataProvider(value:Object):void {

candidateDataProvider = value;

dataProviderDirty = true;

invalidateProperties();

}

override protected function commitProperties():void {

super.commitProperties();

if (dataProviderDirty) {

super.dataProvider = candidateDataProvider;

dataProviderDirty = false;

}

if (valueDirty) {

applyValue(candidateValue);

valueDirty = false;

}

}

]]>

</mx:Script>

</mx:ComboBox>

Listing 8.4 The value-aware ComboBox

Now everything works as expected. The screenshot of the running application is presented in Fig-ure 8.1.

Figure 8.1 The “value-aware ComboBox in action

Enhancing and Extending Flex ControlsCHAPTER 8

Page 324: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

318 RIA WITH ADOBE FLEX AND JAVA

If you change the ComboBox selection, the top label, which initially contains a current bound val-ue, is “IL” and will change accordingly. No miracles here, a regular Flex data binding, one would say. Indeed, good things are easy to take for granted. Still, we haven’t provided any binding declarations or binding code in our ComboBox. So why does it work? It works because the original Flex defi-nition of the value getter ComboBox has already been marked with a metadata tag [“Bindable”], which makes the property bindable (you don’t have to have a setter to be bindable):

[Bindable(“change”)]

[Bindable(“valueCommitted”)]

But wait, you may say, these binding definitions indicate that data modifications bound to the value property get triggered in response to change or valueCommitted events. Yet our value set-ter doesn’t contain a single dispatchEvent call. What’s the catch? Events are dispatched inside the code that assigns a selectedIndex. This assignment results in invoking a selectedIndex setter, which ultimately dispatches the events.

Adding a dataField PropertyTwo important structural elements of an object populating the ComboBox are label and data. To map the label property to a field of such a business object, Flex ComboBox offers a read-write prop-erty called labelField (there’s another mapping mechanism, labelFunction, a callback that returns the string to be stored in the object’s label). However, Flex ComboBox doesn’t give you the same choice in the data property. We’ll fill this gap by introducing a new dataField property. In other words, we’d like to be able to write something like:

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml

xmlns:lib=”com.theriabook.controls.*”>

. . . . . . . .

<lib:ComboBox id=”cbx_1” width=”150” value=”123” labelField=”state_name”

dataField=”state_code” dataProvider=”{comboData}”/>

</mx:Application>

To do this, we’ll start by adding a public variable dataField to our ComboBox that by default will store the same value as in the data property:

public var dataField:String = “data”;

Next, we’ll modify the applyValue() to do the data comparison of the dataField and the labelField, instead of directly operating on the data and label properties:

private function applyValue(val:Object) : void {

if ((val != null) && (dataProvider != null)) {

for (var i : int = 0; i < dataProvider.length; i++) {

if (val==dataProvider[i][dataField] ||

Enhancing and Extending Flex ControlsCHAPTER 8

Page 325: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 319

val== dataProvider[i][labelField]) {

selectedIndex = i;

return;

}

}

}

selectedIndex = -1;

}

This time we also have to override the default value getter to recognize the newly introduced dataField similar to the setter:

override public function get value() : Object {

var item:Object = selectedItem;

if (item == null || typeof(item) != “object”) return item;

return item[dataField] ? item[dataField] : item.label;

}

When overriding system properties you should take care of the metadata as well. As long as we were only responsible for the setter, we could have relied on the metadata bindings coming through the standard getter in the ancestor class. Now that we’ve overridden the getter as well, we’re on our own. The following lines should be added in front of either the getter or setter for the value property:

[Bindable(“change”)]

[Bindable(“valueCommitted”)]

[Inspectable(defaultValue=”0”, category=”General”, verbose=”1”)]

The complete listing for the customized ComboBox with the new dataField property is presented in Listing 8.5:

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

<mx:ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml” >

<mx:Script>

<![CDATA[

public var dataField:String = “data”;

private var candidateValue:Object;

private var valueDirty:Boolean = false;

private var candidateDataProvider:Object;

private var dataProviderDirty:Boolean = false;

[Bindable(“change”)]

[Bindable(“valueCommit”)]

Enhancing and Extending Flex ControlsCHAPTER 8

Page 326: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

320 RIA WITH ADOBE FLEX AND JAVA

[Inspectable(defaultValue=”0”, category=”General”, verbose=”1”)]

override public function get value() : Object {

var item:Object = selectedItem;

if (item == null || typeof(item) != “object”) return item;

return item[dataField] ? item[dataField] : item.label;

}

private function applyValue(val:Object):void {

if ((val != null) && (dataProvider != null)) {

for (var i : int = 0; i < dataProvider.length; i++) {

if ( val == dataProvider[i][dataField] || val == dataProvider[i][labelField])

{

selectedIndex = i;

return;

} } }

selectedIndex = -1;

}

public function set value(val:Object) : void {

candidateValue = val;

valueDirty = true;

invalidateProperties();

}

override public function set dataProvider(value:Object):void {

candidateDataProvider = value;

dataProviderDirty = true;

invalidateProperties();

}

override protected function commitProperties():void {

super.commitProperties();

if (dataProviderDirty) {

super.dataProvider = candidateDataProvider;

dataProviderDirty = false;

}

if (valueDirty) {

applyValue(candidateValue);

valueDirty = false;

}

}

]]>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 327: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 321

</mx:Script>

</mx:ComboBox>

Listing 8.5 A ComboBox with the DataField value

The test application code is presented in Listing 8.6. Now you can map any business object’s prop-erties (regardless of what their names are) while declaring the new version of the ComboBox, which will give you extra flexibility.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:lib=”com.theriabook.

controls.*”>

<mx:ArrayCollection id=”comboData” >

<mx:Array>

<mx:Object state_name=”New York” state_code=”NY”/>

<mx:Object state_name=”Connecticut” state_code=”CT”/>

<mx:Object state_name=”Illinois” state_code=”IL”/>

</mx:Array>

</mx:ArrayCollection>

<mx:Label id=”lbl_1” text=”Current bound value is &quot;{cbx_1.value}&quot;” />

<lib:ComboBox id=”cbx_1” width=”150” value=”IL” labelField=”state_name”

dataField=”state_code” dataProvider=”{comboData}”/>

</mx:Application>

Listing 8.6 A test application for the ComboBox

ComboBox with a Multi-Column ListLet’s make our ComboBox a bit fancier by teaching the dropdown list portion of the ComboBox to carry multiple columns of data. It may please users and does not require any code changes to the ComboBox: the underlying mechanism, known as item rendering, is provided by Flex off-the-shelf.

Flex creators envisioned that most of the application-specific richness should come from appli-cation developers. In particular, while on the surface the standard ComboBox still resembles its HTML counterpart, an elegant Flex mechanism of custom item renderers lets us turn the boring single-column dropdown into a fancy multi-column one. We have to mention that it can also im-plement the multi-column ComboBox without resorting to an explicit item renderer. Please see MultiColumnComboBoxDropDownFactoryDemo.mxml in the samples that come with the book. This sample is based on the dropdownFactory property of the ComboBox:

Enhancing and Extending Flex ControlsCHAPTER 8

Page 328: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

322 RIA WITH ADOBE FLEX AND JAVA

Figure 8.2 A ComboBox with a multi-column dropdown

Flex item renderers go inline with the best traditions of Macintosh, OS/2, and Windows presenta-tion engines, where similar mechanisms were known as an “owner draw” painting. Specifically, item renderer lets us modify the look-and-feel of items in any list-based control, such as DataGrid, List, Tree, etc. The dropdown portion of the ComboBox is nothing but the List. Hence we’e going to build a renderer that will show four columns with controlled widths, separated with vertical lines, as shown in Figure 8.2.

First, though, we’ll modify the data used to populate the ComboBox. So far we’ve been working with a collection of U.S. states, each element of the collection being a name-code value pair. Now we’ll change the comboData collection into an array of four-field objects containing emp_id, fname, lname, and phone:

<mx:ArrayCollection id=”comboData” >

<mx:Array>

<mx:Object emp_id=”102” fname=”Yakov” lname=”Fain” phone=”7322342654”/>

<mx:Object emp_id=”104” fname=”Anatole” lname=”Tartakovsky”

phone=”5618325611”/>

<mx:Object emp_id=”106” fname=”Victor” lname=”Rasputnis”

phone=”7184017234”/>

</mx:Array>

</mx:ArrayCollection>

The data is ready, and we proceed to the renderer. One way of packaging the item renderer is to create a special ActionScript class or MXML component file. Alternatively, Flex provides a way to define a nested MXML component with an <mx:Component> tag. To simplify our example we’ll go with such an inline renderer; although we were looking for unlimited reusability we’d certainly select the dedicated file, aka drop-in renderer.

Below is the definition of our inline renderer. We start with the Component tag, which carries spe-

Enhancing and Extending Flex ControlsCHAPTER 8

Page 329: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 323

cific scoping arrangements. Most importantly, the property data in the scope of the inline renderer references the element of the dataProvider that corresponds to the relevant item of the list. (On par with data there’s another property – outerDocument – that references the same scope that our ComboBox is within).

The inline component includes an HBox containing four Labels, a separating VRule between each column, and a PhoneFormatter to format the phone number with a default phone format. We split the width of the dropdown list between our columns as 10%, 35%, 35%, and 20%. We’ll just set the height of the HBox to 20 pixels to accommodate the size of the default font. To avoid individual scrollbars in each cell of the list we turn off both the horizontal and vertical scroll policy styles:

<mx:Component

<mx:HBox horizontalScrollPolicy=”off” verticalScrollPolicy=”off” height=”20”>

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.emp_id}” width=”10%”/><mx:VRule />

<mx:Label text=”{data.fname}” width=”35%”/><mx:VRule />

<mx:Label text=”{data.lname}” width=”35%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.phone)}” width=”20%”/>

</mx:HBox>

</mx:Component>

For a standard ComboBox to actually inline an item renderer we’d have to write:

<mx:ComboBox id=”cbx_1” value=”102” width=”150” . . .>

<mx:itemRenderer>

<mx:Component

<mx:HBox

. . . . . . . .

</mx:HBox>

</mx:Component>

</mx:itemRenderer>

</mx:ComboBox>

However, our case is a bit more complicated because we reference the custom ComboBox through a different namespace prefixed with lib. Accordingly, to avoid the compiler error “Could not resolve <mx:itemRenderer> to component implementation,” we’ll prefix the itemRender with lib as well:

<lib:ComboBox id=”cbx_1” value=”102” width=”150” . . .>

<lib:itemRenderer>

<mx:Component

<mx:HBox

. . . . . . . .

</mx:HBox>

</mx:Component>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 330: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

324 RIA WITH ADOBE FLEX AND JAVA

</lib:itemRenderer>

</lib:ComboBox>

Let’s set the width of the dropdown list to 350 pixels to accommodate the total width of the four columns:

<lib:ComboBox id=”cbx_1” value=”102” width=”150” dropdownWidth=”350”

dataProvider=”{comboData}” labelField=”lname” dataField=”emp_id”>

MXML creators gave us a handy dropdownWidth property. There is also the ability to decorate a dropdown list with CSS styles. To avoid gaps between two adjacent vertical VRule lines we set the CSS vertical padding to zero:

<mx:Style>

.noVerticalPadding{padding-top:0;padding-bottom:0}

</mx:Style>

<lib:ComboBox id=”cbx_1” value=”102” width=”150”

dropdownWidth=”350” dropDownStyleName=”noVerticalPadding”

dataProvider=”{comboData}” labelField=”lname” dataField=”emp_id”>

The complete code of the sample application is presented in Listing 8.7:

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:lib=”com.theriabook.

controls.*”>

<mx:ArrayCollection id=”comboData” >

<mx:Array>

<mx:Object emp_id=”102” fname=”Yakov” lname=”Fain” phone=”7322342654”/>

<mx:Object emp_id=”104” fname=”Anatole” lname=”Tartakovsky” phone=”5618325611”/>

<mx:Object emp_id=”106” fname=”Victor” lname=”Rasputnis” phone=”7184017234”/>

</mx:Array>

</mx:ArrayCollection>

<mx:Style>

.noVerticalPadding{padding-top:0;padding-bottom:0}

</mx:Style>

<lib:ComboBox id=”cbx_1” value=”102” width=”150”

dropdownWidth=”350” dropDownStyleName=”noVerticalPadding”

dataProvider=”{comboData}” labelField=”lname” dataField=”emp_id”>

<lib:itemRenderer>

<mx:Component>

<mx:HBox height=”20” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” >

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.emp_id}” width=”10%”/><mx:VRule />

<mx:Label text=”{data.fname}” width=”35%”/><mx:VRule />

Enhancing and Extending Flex ControlsCHAPTER 8

Page 331: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 325

<mx:Label text=”{data.lname}” width=”35%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.phone)}” width=”20%”/>

</mx:HBox>

</mx:Component>

</lib:itemRenderer>

</lib:ComboBox>

</mx:Application>

Listing 8.7 A sample application with a multi-column dropdown

Populating a ComboBox with Server-Side DataSo far we’ve been populating our ComboBox controls with the static data. Let’s change the pattern and load the ComboBox with data coming from a server-side database. The purpose of such an exercise in the context of this chapter is simply to have all the artifacts handy for the subsequent sections in which we’ll improve our custom ComboBox further. And the end of the section, we’ll build an application that will look like Figure 8.3.

Figure 8.3 A ComboBox populated from the database

We’ll be using the RemoteObject tag to communicate with the server-side Java classes (we went through this excersise in the Stock Portfolio example in Chapter 5). The RemoteObject enables asynchronous calls to a Java class as long as this class can be found by the Web application. Here’s the snippet of the MXML application where we declare the service: an instance of the RemoteOb-ject. The implementation of the Java method getEmployees is shown in Listing 8.8.

<mx:RemoteObject id=”service”

destination=”com_theriabook_composition_EmployeeDAO”

result=”onServiceDataReady(event)” showBusyCursor=”true”

Enhancing and Extending Flex ControlsCHAPTER 8

Page 332: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

326 RIA WITH ADOBE FLEX AND JAVA

fault=”onServiceFault(event)”>

<mx:method name=”getEmployees” />

</mx:RemoteObject>

The name of the server destination looks like the fully qualified Java class name, where the dots are replaced by underscores. Actually, you shouldn’t use this naming style in your projects since it discloses the internal structure of your server-side code. We use such a naming convention in this book just to help you understand what Java’s counterpart MXML uses.

You don’t have to enumerate remote functions via the method MXML tags, although these tags let you reconfigure properties such as result, fault, and showBusyCursor on a per-method basis. We’ll get to these properties a bit later.

A RemoteObject facilitates calls to the Java class mapped through a configurable server-side des-tination. Assuming the default setup, all remoting destinations should be listed as children of the services node of the remoting-config.xml, located inside the WEB-INF/flex folder of our Web appli-cation. Here’s the relevant fragment of the remoting-config.xml that maps our logical destination to the physical Java class:

<destination id=”com_theriabook_composition_EmployeeDAO”>

<properties>

<source>com.theriabook.composition.EmployeeDAO</source>

</properties>

</destination>

Listing 8.8 shows the com.theriabook.composition.EmployeeDAO.java, the class that our service remotes to. It uses two utility classes (the source code is available for download): JDBCConnec-tion, which isolates us from connecting to the datasource and the unchecked DAOException. This sample assumes that you’ve configured the JDBC data source called jdbc/theriabook on the Java EE application server.

package com.theriabook.composition;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import com.theriabook.composition.to.EmployeeDTO;

import com.theriabook.daoflex.DAOException;

import com.theriabook.daoflex.DateTimeConversion;

import com.theriabook.daoflex.JDBCConnection;

Enhancing and Extending Flex ControlsCHAPTER 8

Page 333: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 327

public final class EmployeeDAO

{

public EmployeeDAO()

{

}

public final EmployeeDTO[] getEmployees(){

String sql = “select * from EMPLOYEE”;

EmployeeDTO[] result = null;

ArrayList list = new ArrayList();

PreparedStatement stmt = null;

ResultSet rs = null;

Connection conn = null;

try {

conn = JDBCConnection.getConnection(“jdbc/theriabook”);

stmt = conn.prepareStatement(sql);

rs = stmt.executeQuery();

while( rs.next() ){

EmployeeDTO to = new EmployeeDTO();

to.EMP_ID = (rs.getInt(1));

to.EMP_FNAME = (rs.getString(3));

to.EMP_LNAME = (rs.getString(4));

//. . . . .

to.SEX = (rs.getString(20));

list.add(to);

}

result = new EmployeeDTO[list.size()];

result = (EmployeeDTO[])list.toArray(result);

return result;

}catch (SQLException se){

throw new DAOException(se);

} catch(Throwable te){

throw new DAOException(“Internal error”, te);

} finally {

try {rs.close(); rs = null;} catch (Exception e){…}

try {stmt.close(); stmt = null;} catch (Exception e){…}

JDBCConnection.releaseConnection(conn);

}

}

}

Listing 8.8 The server-side class EmployeeDAO.java

Enhancing and Extending Flex ControlsCHAPTER 8

Page 334: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

328 RIA WITH ADOBE FLEX AND JAVA

Java programmers may not like the fact that we named the non-final variables in capital letters, but this is done to simplify the mapping between these variables and the database column names used later in this book.

The method getEmployees() returns an array of Java objects shown in Listing 8.9.

package com.theriabook.composition.dto;

public class EmployeeDTO

{

public int EMP_ID;

public String EMP_FNAME;

public String EMP_LNAME;

//. . . . . . . .

public String SEX;

public EmployeeDTO()

{

}

}

Listing 8.9 A Java class EmployeeDTO

Here’s an interesting situation: a RemoteObject can’t deliver Java objects to the Flash Player, so Flex will marshall Java EmployeeDTO objects into the matching ActionScript objects. The best practice – both performance- and usability-wise – is to have ActionScript packages and classes match their Java counterparts. Otherwise, the incoming data will be un-marshalled into anonymous Objects. (Flex documentation describes this under the topic Type-to-Type Mapping.)

In our case, we have defined the ActionScript class com.theriabook.composition.dto.EmployeeDTO as shown in Listing 8.10.

But declaring the ActionScript class matching its Java counterpart property by property is only half the deal. The second half of successful Java/ActionScript marshalling is to register the class with the marshalling mechanism. The Flex ActionScript compiler facilitates this registration with a Re-moteClass metadata directive:

package com.theriabook.composition.dto

{

[RemoteClass(alias=”com.theriabook.composition.dto.EmployeeDTO”)]

public dynamic class EmployeeDTO

{

public var EMP_ID : Number;

Enhancing and Extending Flex ControlsCHAPTER 8

Page 335: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 329

public var EMP_FNAME : String;

public var EMP_LNAME : String;

. . . . . . . . . .

public var SEX : String;

}

}

Listing 8.10 The ActionScript class com.theriabook.composition.dto.EmployeeDTO

Flex does lookup among the registered classes when Java objects come from the server. The class name of the Java object is used as a key to find the registered ActionScript class to deserialize to. Conversely, when data is sent to the server, Flex carries the information of the Java class – alias – to create proper objects on the server side.

In the MXML declaration of the RemoteObject service, we have to provide event handlers for the result, which is dispatched when the method returns successfully, and for the fault, which is dis-patched when something goes wrong. As a reminder, both events are asynchronous: <mx:RemoteObject id=”service” destination=”com_theriabook_composition_Employ-

eeDAO”

result=”onServiceDataReady(event)” showBusyCursor=”true”

concurrency=”last”

fault=”onServiceFault(event)”>

<mx:method name=”getEmployees” />

</mx:RemoteObject>

To process result we add the function called onServiceDataReady. This function makes use of the C#-style typecasting introduced in ActionsScript 3 – A as B. The ActionScript compiler wouldn’t let us get away with the following line because the type on the left isn’t compatible with the type on the right:

var employees:Array = event.result; // Compiler error (incompatible types),

On the other hand, had we done

var employees:Array = new Array(event.result);

or

var employees:Array = Array(event.result);

we’d end up converting whatever comes through event.result into an array. The outcome of such a conversion would be a brand new array with one element containing whatever came with the event.result. The as casting to the rescue!1 Now we request an explicit casting instead of conversion.

Enhancing and Extending Flex ControlsCHAPTER 8

Page 336: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

330 RIA WITH ADOBE FLEX AND JAVA

public function onServiceDataReady(event:ResultEvent):void {

// We have to use “as” here, otherwise array conversion

// will take precedence over casting to array, leaving

// us with 1-element array wrapped around our result

var employees:Array = event.result as Array;

employeeCollection = new ArrayCollection(employees);

}

Listing 8.11 The function onServiceDataReady

Finally, somewhere in our application we have to invoke getEmployees(). Let’s do it right before the application’s screen becomes visible, that’s during the processing of the application’s creationCom-plete event:

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml creationComplete=”onCrea

tionComplete()”>

<mx:Script>

<![CDATA[

public function onCreationComplete():void {

service.getEmployees();

}

. . . . .

The complete listing of the client application is represented in Listing 8.12.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:lib=”com.theriabook.

controls.*”

creationComplete=”onCreationComplete()”>

<mx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

import mx.rpc.events.*;

import mx.managers.CursorManager;

import mx.controls.Alert.*;

[Bindable]

public var employeeCollection:ArrayCollection;

public function onCreationComplete():void {

service.getEmployees();

}

Enhancing and Extending Flex ControlsCHAPTER 8

Page 337: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 331

public function onServiceDataReady(event:ResultEvent):void {

// We have to use “as” cast style to avoid result2array conversion,

// which would leave us with 1-element array wrapped around the

// result

var employees:Array = event.result as Array;

employeeCollection = new ArrayCollection(employees);

}

private function onServiceFault(event:FaultEvent):void {

// Remove busy cursor or it will spin forever (:

CursorManager.removeBusyCursor();

// Error processing ...

Alert.show(“Failed retrieving data: “+event.message, “[ComboBox]” + id);

}

]]>

</mx:Script>

<mx:Label text=”Current bound value is &quot;{cbx_1.value}&quot;” />

<lib:ComboBox id=”cbx_1” value=”102”

width=”150” dataProvider=”{employeeCollection}”

labelField=”EMP_LNAME” dataField=”EMP_ID” dropdownWidth=”350” dropDownStyleName

=”noVerticalRadding” >

<lib:itemRenderer>

<mx:Component >

<mx:HBox height=”20” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” >

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.EMP_ID}” width=”10%”/><mx:VRule />

<mx:Label text=”{data.EMP_FNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{data.EMP_LNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.PHONE)}” width=”20%”/>

</mx:HBox>

</mx:Component>

</lib:itemRenderer>

</lib:ComboBox>

<mx:RemoteObject id=”service” destination=”com.theriabook.composition.EmployeeDAO”

showBusyCursor=”true” concurrency=”last”

result=”onServiceDataReady(event)” fault=”onServiceFault(event)”>

<mx:method name=”getEmployees” />

</mx:RemoteObject>

</mx:Application>

Listing 8.12 The code that populates the Combobox from a database

Enhancing and Extending Flex ControlsCHAPTER 8

Page 338: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

332 RIA WITH ADOBE FLEX AND JAVA

Hurray! Our application is running. Remote Java classes are sending us data on demand. Don’t you feel good? Well, we don’t want to ruin the party, but the Java-to-ActionScript marshalling didn’t work as expected. Not that the marshalling didn’t happen at all, but despite all of our meticulous efforts, EmployeeDTO Java classes were converted into anonymous ActionScript Objects.

Here’s what happened.

When we discussed marshaling Java EmployeeDTO into its EmployeeDTO.as counterpart, we made an assumption that class EmployeeDTO.as is available during runtime.2 The truth is, while compil-ing and linking our application into the SWF file, Flex had no reason to believe that this class has to be included in the SWF, because there’s no direct use of a variable of this class in the entire ap-plication, including implementing the ComboBox. We’ll cover class loading in more detail later in the book, but for now, let’s just fool the Flex compiler by having a variable declaration somewhere in the application:

var linkageDummy: com.theriabook.composition.dto.EmployeeDTO;

That will do the job and the marshalling will indeed consider the Employee DTO.as class.

Encapsulating a Remote Object Inside the ComboBoxNow that we’ve populated the ComboBox via the RemoteObject, recall the technique introduced in Chapter 7, where we have made our collections “destination-aware.” The rationale there was to eliminate the boring routine of creating RemoteObject and programming standard onResult() and onFault() handlers. We achieved our goal by encapsulating this functionality directly into the col-lection object.

Such collections are self-sufficient, they don’t need external controllers and, at the end of the day, this technique allows you to avoid writing and maintaining mundane code that’s not needed in the first place.

So is the story with ComboBoxes, because as far as application partitioning goes, there’s a com-pelling argument to encapsulate instantiation and interoperation with the RemoteObject inside the visual component, making it a destination-aware one. In this section we’ll demonstrate a technique for embedding the RemoteObject directly into the ComboBox, which will make our “Employees ComboBox” fully autonomous and reusable. The following snippet illustrates the suggested usage pattern of the ComboBox we’re going to make.

<lib:ComboBox id=”cbx_1”

width=”150” dropdownWidth=”350”

labelField=”EMP_LNAME” dataField=”EMP_ID”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployees” creationComplete=”cbx_1.fill()”

/>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 339: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 333

This design approach sets application business logic free from low-level details like mundane re-moting.

Now, you might be worried about our adherence to sacred architectural pillars like separation of presentation from data, MVC, etc. Let us assure you, MVC is fully preserved, the only change being that the mix of M, V, and C has been delegated from the application to the component level.

We’ll start by adding two more properties to our ComboBox control – destination and method. We’ll declare them public so they can be accessed from the outside, and, in particular, at the MXML level:

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

<mx:ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml” >

<mx:Script>

<![CDATA[

. . . . . . . . .

public var destination:String=null;

public var method:String = null;

]]>

</mx:Script>

</mx:ComboBox>

We’ll also embed a reference to a RemoteObject instance – ro – and declare a fill() method that cre-ates (if not created earlier) a real instance of the RemoteObject:

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

<mx:ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml” >

<mx:Script>

<![CDATA[

. . . . . . . . .

public var destination:String=null;

public var method:String = null;

protected var ro:RemoteObject = null;

public function fill(... args): void {

if( ro==null ) {

ro = new RemoteObject(destination);

ro.showBusyCursor = true;

}

. . . . .

}

]]>

</mx:Script>

</mx:ComboBox>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 340: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

334 RIA WITH ADOBE FLEX AND JAVA

Proceeding with the fill() method, we set the concurrency of the RemoteObject to last (similar to what we did in Chapter 5, and we’ll cover more on this later in this chapter) and associate the event listener functions with the result and fault events.

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

Now, suppose ro has been created. How can we call our dynamic method? In a typical scenario, the following two lines would do the job (the method apply() is equivalent to Java reflection’s invoke():

var functionObject:Function = ro[method];

functionObject.apply(ro, args);

However, our ro is an instance of RemoteObject and so it has the method getOperation(), which accepts the name of the operation as its argument and returns an instance of mx.rpc.AbstractOper-ation. Conveniently, the AbstractOperation’s send() method assumes the value of the arguments property as the parameters:

var operation:AbstractOperation = ro.getOperation(method);

operation.arguments = args;

operation.send();

(Check the Flex MXML and ActionScript Language Reference to get more familiar with mx.rpc.AbstractOperation.)

Hence, our implementation of fill() will look like Listing 8.13.

public function fill(... args): void {

if( ro==null ) {

if( destination==null || destination.length==0 )

throw new Error(“No destination specified”);

if( method==null || method.length==0 )

throw new Error(“No retrieveMethod specified”);

ro = new RemoteObject(destination);

ro.showBusyCursor = true;

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

}

// In a simple scenario,to dynamically invoke a method on ro:

//var functionObject:Function = ro[method];

Enhancing and Extending Flex ControlsCHAPTER 8

Page 341: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 335

//functionObject.apply(ro, args);

var operation:AbstractOperation = ro.getOperation(method);

operation.arguments = args;

operation.send();

}

Listing 8.13 Implementation of the method fill()

Another method called ro_onResult() updates the ComboBox’s dataProvider with a brand new re-sult set when it’s ready:

private function ro_onResult(evt:ResultEvent):void {

dataProvider = evt.result;

}

And this is the sketchy ro_onFault(): private function ro_onFault(evt:FaultEvent):void {

CursorManager.removeBusyCursor();

// Do your own error processing with event.message

}

We must emphasize that we deliberately dropped the MXML-based RemoteObject in favor of a programmatic instantiation of one. We’ve done it for performance reasons: with MXML, you pay a penalty for creating the RemoteObject upfront, during the creation of the ComboBox. But in some applications programmatic creation of the RemoteObject may be deferred or may not be needed at all.

Having all this code in place, we can further modify the definition of the ComboBox to the follow-ing:

<lib:ComboBox id=”cbx_1” width=”150” dropdownWidth=”350”

labelFunction=”fullName” dataField=”EMP_ID”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployees” creationComplete=”cbx_1.fill()”

>

To sanitize the application code, let’s remove the references to RemoteObject from our sample ap-plication. In Listing 8.14, the data population is simply handled by calling cbx_1.fill() from within the ComboBox’s event handler creationComplete.

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

Enhancing and Extending Flex ControlsCHAPTER 8

Page 342: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

336 RIA WITH ADOBE FLEX AND JAVA

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:lib=”com.theriabook.

controls.*”>

<mx:Style>

.noVerticalPadding{padding-top:0;padding-bottom:0}

</mx:Style>

<mx:Script>

import com.theriabook.composition.dto.*;

private var linkage:EmployeeDTO;

</mx:Script>

<lib:ComboBox id=”cbx_1” width=”150” value=”1039” labelField=”EMP_LNAME”

dataField=”EMP_ID”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployees” creationComplete=”cbx_1.fill()”

dropdownWidth=”350” dropDownStyleName=”noVerticalPadding”

>

<lib:itemRenderer>

<mx:Component >

<mx:HBox height=”20” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” >

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.EMP_ID}” width=”10%”/><mx:VRule />

<mx:Label text=”{data.EMP_FNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{data.EMP_LNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.PHONE)}” width=”20%”/>

</mx:HBox>

</mx:Component>

</lib:itemRenderer>

</lib:ComboBox>

</mx:Application>

Listing 8.14. A sample application using a destination-aware ComboBox

The complete source of our destination-aware ComboBox is presented in Listing 8.15.

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

<mx:ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml” >

<mx:Script>

<![CDATA[

public var dataField:String = “data”;

private var candidateValue:Object;

private var valueDirty:Boolean = false;

private var candidateDataProvider:Object;

private var dataProviderDirty:Boolean = false;

Enhancing and Extending Flex ControlsCHAPTER 8

Page 343: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 337

[Bindable(“change”)]

[Bindable(“valueCommit”)]

[Inspectable(defaultValue=”0”, category=”General”, verbose=”1”)]

override public function get value() : Object {

var item:Object = selectedItem;

if (item == null || typeof(item) != “object”) return item;

return item[dataField] ? item[dataField] : item.label;

}

private function applyValue(val:Object):void {

if ((val != null) && (dataProvider != null)) {

for (var i : int = 0; i < dataProvider.length; i++) {

if ( val == dataProvider[i][dataField] || val == dataProvider[i][labelField])

{

selectedIndex = i;

return;

} } }

selectedIndex = -1;

}

public function set value(val:Object) : void {

candidateValue = val;

valueDirty = true;

invalidateProperties();

}

override public function set dataProvider(value:Object):void {

candidateDataProvider = value;

dataProviderDirty = true;

invalidateProperties();

}

override protected function commitProperties():void {

super.commitProperties();

if (dataProviderDirty) {

super.dataProvider = candidateDataProvider;

dataProviderDirty = false;

}

if (valueDirty) {

applyValue(candidateValue);

valueDirty = false;

}

}

Enhancing and Extending Flex ControlsCHAPTER 8

Page 344: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

338 RIA WITH ADOBE FLEX AND JAVA

import mx.rpc.remoting.mxml.RemoteObject;

import mx.rpc.AbstractOperation;

import mx.controls.Alert;

import mx.rpc.events.ResultEvent;

import mx.rpc.events.FaultEvent;

import mx.managers.CursorManager;

import mx.collections.ArrayCollection;

public var destination:String=null, method : String = null;

public var autoFill : Boolean = true;

protected var ro:RemoteObject = null;

public function fill(... args): void {

if( ro==null ) {

if( destination==null || destination.length==0 )

throw new Error(“No destination specified”);

if( method==null || method.length==0 )

throw new Error(“No retrieveMethod specified”);

ro = new RemoteObject(destination);

ro.showBusyCursor = true;

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

}

var operation:AbstractOperation = ro.getOperation(method);

operation.arguments = args;

operation.send();

}

private function ro_onFault(evt:FaultEvent):void {

CursorManager.removeBusyCursor();

Alert.show(“Failed retrieving data: “+evt.message, “[DestinationAwareComboBox]”

+ id);

}

private function ro_onResult(evt:ResultEvent):void {

if (evt.result.length != 0)

dataProvider = evt.result;

}

]]>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 345: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 339

</mx:Script>

</mx:ComboBox>

Listing 8.15 A destination-aware ComboBox

Adding Autocomplete Support to TextInput Moving on with improving the functionality of the ComboBox, in this section the autocomplete feature is on our radar screen. According to Wikipedia, autocomplete involves the program predict-ing a word or phrase that the user wants to type in without the user actually typing it in completely. In our case, we’re going to offer predictions in response to the user’s keystrokes in the editable portion of the ComboBox. Since our ComboBox is now destination-aware, we won’t stop short of searching for matches against the remote data stores (during the autocomplete process). In other words, once the user types a character, we’ll be retrieving all the matching records and highlight-ing the predicted part so that the user can type the next character over it. Visually it will look like Figure 8.4.

Figure 8.4 A ComboBox with autocomplete support

The autocomplete requirements can be adjusted according to the needs of a particular business application. Take, for instance, a customer account search. You may want to defer a search until at least the first three characters of the account are entered, etc.

Before we venture to improve the ComboBox, or to be more accurate, a TextInput child of the Combo-Box, we’ll pilot autocompletion functionality on a plain TextInput control. Similarly, before we get to search the remote database records, let’s practice searching against some local ArrayCollection.We’re going to create an application containing two controls: TextInput inp_1 and List list_1. We’ll use a vertical layout so that our TextInput is positioned above the List:

Enhancing and Extending Flex ControlsCHAPTER 8

Page 346: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

340 RIA WITH ADOBE FLEX AND JAVA

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”>

<mx:TextInput id=”inp_1” />

<mx:List id=”list_1” width=”180”>

<mx:dataProvider>

<mx:Array>

<mx:String>Apple</mx:String>

<mx:String>Application</mx:String>

<mx:String>Sign</mx:String>

<mx:String>Signature</mx:String>

<mx:String>Singleton</mx:String>

</mx:Array>

</mx:dataProvider>

</mx:List>

</mx:Application>

Listing 8.16 Code for a client with TextInput and List controls

Here’s our goal: whenever a user modifies the inp_1 field, a matching value from the list_1, if any, jumps into inp_1. And when this happens, the predicted part of that value should be highlighted. Thereafter the first user keystroke will replace the selection and the whole process starts all over.

Figure 8.5 shows a screenshot of the pilot application we’re going to develop.

Figure 8.5 Simple AutoComplete with TextInput and List controls

Let’s get the ball rolling by intercepting all interactive changes to a TextInput via its change event:

<mx:TextInput id=”inp_1” change=”autocomplete()”/>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 347: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 341

The autocomplete() function can scan the objects in the dataProvider until it finds a string that starts with the inp_1.text. If so, the TextOnput field inp_1 should be updated with the string and the remaining portion of the string should be highlighted:

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

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml . . .>

<mx:Script>

<![CDATA[

private function autocomplete():void {

var typedText:String = inp_1.text.toLowerCase();

var typedLenth :int = typedText.length;

for (var i : int = 0; i < list_1.dataProvider.length; i++) {

if ( typedText == list_1.dataProvider[i].substr(0, typedLenth).toLowerCase()) {

inp_1.text = list_1.dataProvider[i];

inp_1.setSelection (typedLenth, inp_1.text.length); // won’t work!!!

break;

}

}

}

. . . . .

</mx:Application>

Listing 8.17 The first (broken) version of the autocomplete implementation

The only drawback to this approach is that, since autocomplete() is a handler of change event, the selection we apply to inp_1 will get dismissed by default processing on the return from autocom-plete(). The delayed rendering causes that. One working solution to alleviate this situation is to defer the text selection through the setTimeout() function. As a reminder, the first parameter of the setTimeout() is a reference to a function object to be called, the second parameter is the timeout when you want the call to happen, while the rest are, well, the rest-style parameters of the function. In our case, the first parameter is the nested function select(): setTimeout(

function select(fromChar:int, toChar:int):void {

inp_1.setSelection(fromChar, toChar);

}, 50, typedLenth, inp_1.text.length );

The function autocomplete() will look the following way:

private function autocomplete():void {

var typedText:String = inp_1.text.toLowerCase();

var typedLenth :int = typedText.length;

for (var i : int = 0; i < list_1.dataProvider.length; i++) {

if ( typedText == list_1.dataProvider[i].substr(0, typedLenth).toLowerCase()) {

Enhancing and Extending Flex ControlsCHAPTER 8

Page 348: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

342 RIA WITH ADOBE FLEX AND JAVA

inp_1.text = list_1.dataProvider[i];

setTimeout(

function select(fromChar:int, toChar:int):void {

inp_1.setSelection(fromChar, toChar);

},50, typedLenth, inp_1.text.length);

break;

}

}

}

Listing 8.18 The second (fixed) version of the autocomplete function

Finally, we’ll take care of the BackSpace button. The user may be annoyed if, after hitting the Back-Space button, the autocomplete restores the value instead of letting her type a different ending. Let’s add the processing to the keyDown event on inp_1 so that we’ll know if the last key was the BackSpace. The full listing of our example is presented in Listing 8.19.

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

<!-SimpleAutoCompleteDemo.mxml-->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” layout=”vertical”>

<mx:Script>

<![CDATA[

private var lastText:String = null;

private var lastKeyBackspace:Boolean = false;

private function onKeyDown(evt:KeyboardEvent):void {

if(evt.charCode == flash.ui.Keyboard.BACKSPACE)

lastKeyBackspace = true;

}

private function autocomplete(text:String):void {

var typedText:String = text.toLowerCase();

var typedLenth :int = text.length;

if (lastKeyBackspace) {

lastKeyBackspace = false;

return;

}

for (var i : int = 0; i < list_1.dataProvider.length; i++) {

if ( typedText == list_1.dataProvider[i].substr(0, typedLenth).toLowerCase() ) {

inp_1.text = list_1.dataProvider[i];

setTimeout(

function select(fromChar:int, toChar:int):void {

inp_1.setSelection(fromChar, toChar);

Enhancing and Extending Flex ControlsCHAPTER 8

Page 349: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 343

},

50, typedLenth, inp_1.text.length

);

break;

}

}

}

]]>

</mx:Script>

<mx:TextInput id=”inp_1” change=”autocomplete(inp_1.text)” keyDown=”onKeyDown(event

)”/>

<mx:List id=”list_1” width=”180”>

<mx:dataProvider>

<mx:Array>

<mx:String>Apple</mx:String>

<mx:String>Application</mx:String>

<mx:String>Sign</mx:String>

<mx:String>Signature</mx:String>

<mx:String>Singleton</mx:String>

</mx:Array>

</mx:dataProvider>

</mx:List>

</mx:Application>

Listing 8.19 Complete listing of the SimpleAutoCompleteDemo.mxml

Integrating DataBase Search and ComboBox with AutocompleteWe’re getting closer to integrating autocomplete directly into our ComboBox control. One re-maining piece is the remote database search. We’ll have to make certain changes on the Java side so that the Java class mapped to the destination attribute can fetch the employee data-base record, based on the provided SQL statement in the format similar to “SELECT … WHERE emp_lname like ?”. As soon as the Java side produces the result set, we’ll get back to the TextInput child of the ComboBox and modify the processing of the change event. That will complete our autocomplete escapade.

Let’s start with Java. Earlier in this chapter we implemented the method getEmployees() in the Em-ployeeDAO class (see Listing 8.8). Now we will add one more method getEmployeesByName() as shown in Listing 8.20:

package com.theriabook.composition;

import . . .

public final class EmployeeDAO

Enhancing and Extending Flex ControlsCHAPTER 8

Page 350: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

344 RIA WITH ADOBE FLEX AND JAVA

{

public EmployeeDAO()

{

}

public final EmployeeDTO[] getEmployees()

{

. . . .

}

public final EmployeeDTO[] getEmployeesByName(String namePart)

{

String sql = “select * from EMPLOYEE where emp_lname like ?”;

EmployeeDTO[] result = null;

ArrayList list = new ArrayList();

PreparedStatement stmt = null;

ResultSet rs = null;

Connection conn = null;

try

{

conn = JDBCConnection.getConnection(“jdbc/theriabook”);

stmt = conn.prepareStatement(sql);

stmt.setString(1, namePart);

rs = stmt.executeQuery();

while( rs.next() ){

EmployeeDTO to = new EmployeeDTO();

to.EMP_ID = (rs.getInt(1));

to.EMP_FNAME = (rs.getString(3));

to.EMP_LNAME = (rs.getString(4));

. . . . .

to.SEX = (rs.getString(20));

list.add(to);

}

result = new EmployeeDTO[list.size()];

result = (EmployeeDTO[])list.toArray(result);

return result;

catch (SQLException se){

throw new DAOException(se);

} catch(Throwable te){

throw new DAOException(“Internal error”, te);

} finally {

try {rs.close(); rs = null;} catch (Exception e){…}

try {stmt.close(); stmt = null;} catch (Exception e){…}

JDBCConnection.releaseConnection(conn);

}

Enhancing and Extending Flex ControlsCHAPTER 8

Page 351: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 345

}

}

Listing 8.20. Retrieving employees by name in Java

We’re done with the Java part. Now, let’s find out how the standard ComboBox control is processing change events for its TextInput child component and modify this routine.

We’ll do something like Sherlock Holmes did at the end of each solved case. He’d explain to Dr. Watson how he solved the mystery.

Flex 2.0 comes with source code and we found the class ComboBox.as and its ancestor – Combo-Base.as – inside the folder frameworks\source\mx\controls. The easiest way to find the location of the event-handling code is to search for “addEventListener” in these two files. Here’s what we’ve found in ComboBase.as:

. . . . . .

public var textInput:TextInput;

public var downArrowButton:Button;

protected function textInput_changeHandler(event:Event):void

. . . . . .

override protected function createChildren():void

{

super.createChildren();

// Create the border first, in the back.

. . . . . .

// Next, create the downArrowButton before creating the textInput,

// because it can be as large as the entire control.

if (!downArrowButton) {

downArrowButton = new Button();

. . . . . .

addChild(downArrowButton);

downArrowButton.addEventListener(

FlexEvent.BUTTON_DOWN, downArrowButton_buttonDownHandler

);

}

// Create the textInput on top.

if (!textInput)

{

textInput = new TextInput();

textInput.editable = _editable;

. . . . . . .

addChild(textInput);

textInput.addEventListener(Event.CHANGE, textInput_changeHandler);

Enhancing and Extending Flex ControlsCHAPTER 8

Page 352: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

346 RIA WITH ADOBE FLEX AND JAVA

. . . . . .

}

}

Listing 8.21 A fragment of ComboBase.as

Do you know what real happiness is for a component developer? Are you ready to learn? Okay, we’ll share it with you. The pinnacle of happiness for a component developer is finding out that the function he wants to enhance wasn’t declared private in the ancestor class. Yes! The textIn-put_changeHandler was declared protected. So in our ComboBox, we could simply override the handler in the following manner:

override protected function textInput_changeHandler(event:Event):void

{

super.textInput_changeHandler(event);

if (editable && destination != null)

fill( text + “%”); // async call, we will call autoComplete

// when we get result

else

autoComplete(text);

}

“Elementary, my dear Watson!” In fact, the line doesn’t appear in the Conan Doyle books, only in Sherlock Holmes movies.

Please note that the only argument of fill() is the text + “%” expression, where text is the ComboB-ox’s property that contains, well, the text, as the user typed it in the editable portion of the Combo-Box. As per the implementation of getEmployeesByName(String partName), its argument is used to prepare the query select * from EMPLOYEE where emp_lname like ?. This query will bring us all the data that start with the specified text, if any.

Note the if-statement in our textInput_changeHandler(). If a ComboBox turns out to be un-editable or doesn’t have a source of remote data, i.e., a destination, we call the autocomplete() and use whatever is in the dropdown List child of the ComboBox. If the remote destination was defined, we’ll invoke fill() but we can’t call the autocomplete() right away, because the content of the List (aka dataProvider) can be updated only on completion of the RemoteOb-ject call, which in Flex is always asynchronous. Now the fact that the method ro_onResult() is in the scope of the ComboBox control becomes very handy. Here’s the modified version of ro_onResult():

private function ro_onResult(event:ResultEvent):void {

var txt:String = text; // Keep the text as in old dataProvider

if (event.result.length != 0)

dataProvider = event.result;

Enhancing and Extending Flex ControlsCHAPTER 8

Page 353: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 347

autoComplete(txt); // Restore and complete original text

}

Now we can implement the autocomplete() similar to our pilot example in the previous section. There’s one important distinction though. In our pilot example the dataProvider was a collection of strings. Now that we’re dealing with a collection of objects, several scenarios are equally possible:

a. Objects contain label propertyb. labelField is declared on the ComboBox in MXMLc. labelFunction is declared on the ComboBoxd. Combinations of the above

How do we get the true label? Or, to put it differently, how do we know what should be considered a label by the ComboBox? Praise the Flex creators again, because there’s a convenient public method itemToLabel() that covers all the scenarios. Check with the Flex documentation (see Adobe Flex 2 ActionScript and MXML Language Reference) about the exact order of preferences implemented by itemToLabel(). Meanwhile we’re going to use it for our benefit here:

private function autoComplete(txt:String){

var typeLength:int = txt.length;

var lText:String = txt.toLowerCase();

for (var i : int = 0; i < dataProvider.length; i++) {

var label:String = itemToLabel(dataProvider[i]);

if ( label.substr(0, typeLength).toLowerCase()==lText) {

textInput.text = label;

setTimeout(

function select(fromChar:int, toChar:int) {

textInput.setSelection(fromChar, toChar);

},

50, typedLength, textInput.text.length

);

break;

}

}

}

But wait, the RPC calls that are hitting the database are expensive, aren’t they? Suppose a user types really fast. Shouldn’t we skip the RPC between two fast keystrokes? One way to minimize the num-ber of RPCs is to wait and issue a call after, say, 300 milliseconds via a setTimeout():

var timer:uint = 0;

override public function textInput_changeHandler(event:Event):void

{

super.textInput_changeHandler(event);

Enhancing and Extending Flex ControlsCHAPTER 8

Page 354: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

348 RIA WITH ADOBE FLEX AND JAVA

if (editable && destination != null) {

killTimeout(timer);

timer = setTimeout(function() { fill( text + “%”);}, 300);

} else

autoComplete(text);

}

But if an RPC is expensive why add an extra 300 milliseconds on top of an already slow operation? Shouldn’t we just issue the RPC requests as soon as possible? Quite a dilemma, isn’t it? The answer depends on the network configuration and the threading limitation of your servers.

Another important point is in setting the concurrency property of the Remote Object method to last. Its default value is multiple, and it allows as many invocations on the server side as there are incoming requests. But the value last asserts that any new RPC request will cancel the ones that are in progress or haven’t been started. This is exactly what we need because we don’t need to process outdated requests. And since our implementation of fill() method is using last, it will guarantee that the outdated result set will be disregarded. You may need to tune the timeout based on the network and threading capacity of Java EE server.

Here’s the sample application to test our ComboBox. Note that in this application we’ve turned the ComboBox editable property to true:

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

<!AutoCompleteComboBoxDemo.mxml-->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=”com.theriabook.controls.*” >

<mx:Style>

.noVerticalPadding{padding-top:0;padding-bottom:0}

</mx:Style>

<mx:Script>

import com.theriabook.composition.dto.*;

private var linkage:EmployeeDTO;

</mx:Script>

<lib:ComboBox id=”cbx_1”

width=”150”

labelField=”EMP_LNAME” dataField=”EMP_ID”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployeesByName”

editable=”true”

dropdownWidth=”350” dropDownStyleName=”noVerticalPadding”

>

<lib:itemRenderer>

<mx:Component >

<mx:HBox height=”20” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” >

Enhancing and Extending Flex ControlsCHAPTER 8

Page 355: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 349

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.EMP_ID}” width=”10%”/><mx:VRule />

<mx:Label text=”{data.EMP_FNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{data.EMP_LNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.PHONE)}” width=”20%”/>

</mx:HBox>

</mx:Component>

</lib:itemRenderer>

</lib:ComboBox>

</mx:Application>

Listing 8.22 A sample application to test the destination-aware ComboBox

The complete code of our destination-aware ComboBox with autocomplete and dataField support is presented below:

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

<mx:ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml” >

<mx:Script>

<![CDATA[

public var dataField:String = “data”;

private var candidateValue:Object;

private var valueDirty:Boolean = false;

private var candidateDataProvider:Object;

private var dataProviderDirty:Boolean = false;

[Bindable(“change”)]

[Bindable(“valueCommit”)]

[Inspectable(defaultValue=”0”, category=”General”, verbose=”1”)]

override public function get value() : Object {

var item:Object = selectedItem;

if (item == null || typeof(item) != “object”) return item;

return item[dataField] ? item[dataField] : item.label;

}

private function applyValue(val:Object):void {

if ((val != null) && (dataProvider != null)) {

for (var i : int = 0; i < dataProvider.length; i++) {

if ( val == dataProvider[i][dataField] || val == dataProvider[i][labelField])

{

selectedIndex = i;

Enhancing and Extending Flex ControlsCHAPTER 8

Page 356: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

350 RIA WITH ADOBE FLEX AND JAVA

return;

} } }

selectedIndex = -1;

}

public function set value(val:Object) : void {

candidateValue = val;

valueDirty = true;

invalidateProperties();

}

override public function set dataProvider(value:Object):void {

candidateDataProvider = value;

dataProviderDirty = true;

invalidateProperties();

}

override protected function commitProperties():void {

super.commitProperties();

if (dataProviderDirty) {

super.dataProvider = candidateDataProvider;

dataProviderDirty = false;

}

if (valueDirty) {

applyValue(candidateValue);

valueDirty = false;

}

}

private var lastKeyBackspace:Boolean = false;

private function textInput_onKeyDown(evt:KeyboardEvent):void {

if(evt.charCode == flash.ui.Keyboard.BACKSPACE)

lastKeyBackspace = true;

}

private function textInput_onChange(event:Event):void

{

if (lastKeyBackspace) {

lastKeyBackspace = false;

return;

}

if (editable && destination != null)

fill( text + “%”);

else

Enhancing and Extending Flex ControlsCHAPTER 8

Page 357: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 351

autoComplete(text);

}

override protected function createChildren():void

{

super.createChildren();

textInput.addEventListener(“keyDown”, textInput_onKeyDown );

}

override public function textInput_changeHandler(event:Event):void

{

super.textInput_changeHandler(event);

if (lastKeyBackspace) {

lastKeyBackspace = false;

return;

}

if (editable && destination != null)

fill( text + “%”);

else

autoComplete(text);

}

private function autoComplete(txt:String):void{

var textLength:int = txt.length;

for (var i : int = 0; i < dataProvider.length; i++) {

var label:String = itemToLabel(dataProvider[i]);

if ( label.substr(0, textLength).toLowerCase() == txt.toLowerCase() ) {

textInput.text = label;

setTimeout(

function select(fromChar:int, toChar:int):void {

textInput.setSelection(fromChar, toChar);

},

50, textLength, textInput.text.length);

break;

} }

}

import mx.controls.Alert;

import mx.rpc.remoting.mxml.RemoteObject;

import mx.rpc.events.ResultEvent;

import mx.rpc.events.FaultEvent;

Enhancing and Extending Flex ControlsCHAPTER 8

Page 358: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

352 RIA WITH ADOBE FLEX AND JAVA

public var destination:String=null, method : String = null;

protected var ro:RemoteObject = null;

public function fill(... args): void {

if( ro==null ) {

if( destination==null || destination.length==0 )

throw new Error(“No destination specified”);

if( method==null || method.length==0 )

throw new Error(“No retrieveMethod specified”);

ro = new RemoteObject(destination);

ro.destination = destination;

ro.showBusyCursor = true;

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

}

var operation:AbstractOperation = ro.getOperation(method);

operation.arguments = args;

operation.send();

}

private function ro_onFault(evt:FaultEvent):void {

mx.controls.Alert.show(“Failed retrieving data: “+evt.message, “[AutoCompleteCombo-

Box]” + id);

}

private function ro_onResult(evt:ResultEvent):void {

var txt:String = text;

if (evt.result.length != 0)

dataProvider = evt.result;

autoComplete(txt);

}

]]>

</mx:Script>

</mx:ComboBox>

Listing 8.23 A destination-aware ComboBox

Enhancing and Extending Flex ControlsCHAPTER 8

Page 359: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 353

Separating Business Resources from Generic Component Code So far we’ve been busy extending the standard ComboBox component and all along our sample applications have contained a single instance of the ComboBox populated with employee records. But how do we avoid the temptation of a cut-and-paste solution from screen-to-screen if we need this component in two different screens? We’ll face a similar question when a screen needs two instances of the same component to display different data.

We’re going to offer two solutions here. The first solution will be to build a business-specific Em-ployeeComboBox as an extension of the ComboBox with extra properties containing a remote des-tination and method. The second solution will be to encapsulate these extra properties in a sepa-rate resource class and have our generic ComboBox point to resource instances when the design choice is referred to as an “is” versus “has” pattern.

Listing 8.24 is a reprint of the AutoCompleteComboBoxDemo, Listing 8.22, with some lines printed in bold.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=”com.theriabook.controls.*” >

<mx:Style>

.noVerticalPadding{padding-top:0;padding-bottom:0}

</mx:Style>

<mx:Script>

import com.theriabook.composition.dto.*;

private var linkage:EmployeeDTO;

</mx:Script>

<lib:ComboBox id=”cbx_1”

width=”150”

labelField=”EMP_LNAME” dataField=”EMP_ID”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployeesByName”

editable=”true”

dropdownWidth=”350” dropDownStyleName=”noVerticalPadding”

>

<lib:itemRenderer>

<mx:Component >

<mx:HBox height=”20” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” >

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.EMP_ID}” width=”10%”/><mx:VRule />

<mx:Label text=”{data.EMP_FNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{data.EMP_LNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.PHONE)}” width=”20%”/>

</mx:HBox>

</mx:Component>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 360: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

354 RIA WITH ADOBE FLEX AND JAVA

</lib:itemRenderer>

</lib:ComboBox>

</mx:Application>

Listing 8.24 A sample application with highlighted tags

To illustrate the first solution we’ll encapsulate all the lines, except the bold ones, inside the new ActionScript class EmployeeComboBox. For the second solution, we’ll encapsulate these lines in-side the EmployeeComboBoxResource and modify our custom ComboBox from Listing 8.24 to rec-ognize such resource classes.

Under the first scenario, our application is going to get simplified to this one:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=”com.theriabook.controls.*” >

<lib:EmployeeComboBox id=”cbx_1” width=”150” />

</mx:Application>

Alternatively, under the second scenario, our application will look like:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=”com.theriabook.controls.*” >

<mx:Script>

import com.theriabook.resources.*;

private var linkage:EmployeeComboBoxResource;

</mx:Script>

<lib:ComboBox id=”cbx_1” width=”150”

resource=”com.theriabook.resources.EmployeeComboBoxResource”

/>

</mx:Application>

Building a Business-Specific ComboBoxLet’s look at the file com.theriabook.controls.EmployeeComboBox.mxml – the component that ex-tends our custom ComboBox. It’s got the predefined settings labelField, dataField, destination, and method – all four tightly integrated with each other:

<lib:ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=”com.theriabook.controls.*”

labelField=”EMP_LNAME” dataField=”EMP_ID”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployeesByName”

Enhancing and Extending Flex ControlsCHAPTER 8

Page 361: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 355

. . . . .

</lib:ComboBox>

The itemRenderer of the EmployeeComboBox is the one-to-one copy of the inline implementation we had before:

ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=”com.theriabook.controls.*”

. . . . . .

>

<itemRenderer>

<mx:Component >

<mx:HBox height=”20” horizontalScrollPolicy=”off” verticalScrollPolicy=”off”

>

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.EMP_ID}” width=”10%”/><mx:VRule />

<mx:Label text=”{data.EMP_FNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{data.EMP_LNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.PHONE)}” width=”20%”/>

</mx:HBox>

</mx:Component>

</itemRenderer>

. . . . . . .

</ComboBox>

The style trick, which helped us create a vertical grid with VRules, has moved inside EmployeeCom-boBox and so has the declaration of a linkage variable that we used to force EmployeeDTO to be included in the generated SWF:

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

<ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=”com.theriabook.controls.*”

. . . .

dropDownStyleName=”noVerticalPadding”

>

. . . . . .

<mx:Style>

.noVerticalPadding{padding-top:0;padding-bottom:0}

</mx:Style>

<mx:Script>

<![CDATA[

import com.theriabook.composition.dto.*;

private var linkage:EmployeeDTO;

]]>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 362: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

356 RIA WITH ADOBE FLEX AND JAVA

</mx:Script>

</CompleteComboBox>

The full listing of EmployeeComboBox is presented below:

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

<ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=”com.theriabook.controls.*”

labelField=”EMP_LNAME” dataField=”EMP_ID”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployeesByName”

editable=”true”

dropdownWidth=”350” dropDownStyleName=”noVerticalPadding”

>

<itemRenderer>

<mx:Component >

<mx:HBox height=”20” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” >

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.EMP_ID}” width=”10%”/><mx:VRule />

<mx:Label text=”{data.EMP_FNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{data.EMP_LNAME}” width=”35%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.PHONE)}” width=”20%”/>

</mx:HBox>

</mx:Component>

</itemRenderer>

<mx:Style>

.noVerticalPadding{padding-top:0;padding-bottom:0}

</mx:Style>

<mx:Script>

<![CDATA[

import com.theriabook.composition.dto.*;

private var linkage:EmployeeDTO;

]]>

</mx:Script>

</ComboBox>

Listing 8.25 The EmployeeComboBox

This illustrates the resource separation technique based on the control extension (“is”). The second solution (“has”), based on the dedicated resource classes, is covered in a following section.

Building and Using the Resource ClassesThe resource-based approach is essential for large projects with a lot of objects that are reused by

Enhancing and Extending Flex ControlsCHAPTER 8

Page 363: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 357

different modules. It helps isolate developers working on screen prototyping from the implementa-tion details of the database communications. It simplifies solutions that are based on dynamically changing the component’s functionality and presentation during the runtime. And it paves the way for building systems that allow on-demand loading of business-driven metadata repositories.

Let’s start with the definition of the base class for all the ComboBox resources. This class will be extended by EmployeeComboBoxResource and other ComboBox resource classes alike:

package com.theriabook.resources {

import mx.core;

public class ComboBoxResource {

public var dataField : String = null;

public var destination:String=null;

public var dropdownWidth : int = 0;

public var editable:Boolean = false;

public var itemRenderer:IFactory = null;

public var labelFunction : Function = null;

public var method : String = null;

public var resourceProps : Array = [

“dataField”,”destination”,”dropdownWidth”,

“editable”,”itemRenderer”,”labelFunction”,”method”

];

//Code to be shared by all instances of the resource

. . . . . .

}

}

Listing 8.26 The first cut of the ComboBoxResource.as

As you can see from Listing 8.26, the EmployeeComboBoxResource relies on the compiler mapping the MXML attributes dataField, destination, method, editable, and dropdownWidth to the match-ing public variables of the underlying class ComboBoxResource:

<?xml version=”1.0” encoding=”utf-8”?><!—EmployeeComboBoxResource.mxml‡

<ComboBoxResource xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=”com.theriabook.resources.*”

dataField=”EMP_ID”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployeesByName”

editable=”true”

dropdownWidth=”350”

>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 364: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

358 RIA WITH ADOBE FLEX AND JAVA

. . . . . . .

</ComboBoxResource>

Similarly, the value of the itemRenderer variable in the EmployeeComboBoxResource will be set to the class factory of the inline component based on the HBox:

<?xml version=”1.0” encoding=”utf-8”?><!—EmployeeComboBoxResource.mxml ‡

<ComboBoxResource xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=”com.theriabook.resources.*”

. . . . . . .

>

<itemRenderer>

<mx:Component>

<mx:HBox height=”20”

horizontalScrollPolicy=”off” verticalScrollPolicy=”off” >

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.EMP_LNAME}” width=”40%”/><mx:VRule/>

<mx:Label text=”{data.EMP_FNAME}” width=”40%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.PHONE)}” width=”20%”/>

</mx:HBox>

</mx:Component>

</itemRenderer>

</ComboBoxResource>

We’re going to do some more work with both ComboBoxResource and EmployeeComboBoxRe-source classes a bit later. For now, let’s put the resource classes to work as they are. Let’s add a new property – resource with setter and getter – to our custom ComboBox. The setter, given the resource class name, creates an instance of this object and copies its properties into this object of the ComboBox. For brevity’s sake, we’ll show only the part of the ComboBox listing relevant to the resource: <?xml version=”1.0” encoding=”utf-8”?>

<mx:ComboBox xmlns:mx=”http://www.adobe.com/2006/mxml” >

. . . . . .

import resources.ComboBoxResource;

private var _resource:String = null;

public function set resource(resourceName:String) : void {

var clazz:Class = flash.utils.getDefinitionByName(resourceName);

var instance: * = new clazz();

_resource = resourceName;

var props:Array = instance.resourceProps;

for (var i:int = 0; i < props.length;i++){

Enhancing and Extending Flex ControlsCHAPTER 8

Page 365: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 359

if (instance[props[i]] != null) {

this[props[i]] = instance[props[i]];

}

}

// The style “.comboBoxResourceDropDownStyle” is getting created on

// constructor of the ComboBoxResource class, if not created already.

// Here we just apply it

this.setStyle(“dropDownStyleName”, “comboBoxResourceDropDownStyle”);

}

public function get resource():String {

return _resource;

}

</mx:ComboBox>

Now we can run the application using the new resource property.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=”com.theriabook.controls.*” >

<mx:Script>

import com.theriabook.resources.*;

private var linkage:EmployeeComboBoxResource;

</mx:Script>

<ComboBox id=”cbx_1” width=”150”

resource=”com.theriabook.resources.EmployeeComboBoxResource”

/>

</mx:Application>

To complete the ComboBoxResource we’ll emulate the vertical grid with our VRules by creating and using a style that eliminates vertical padding. In the above code snippet, we’ve referred to this style as the comboBoxResourceDropDownStyle. Although it’s possible to declare this style through a document-wide stylesheet or via the default stylesheet used by the compiler, we’re going to cre-ate and initialize this style in the constructor of the ComboBoxResource. We’ll be checking for the existence of the style in the global StyleManager, since the style could have already been created by another call to the same constructor (if not created through the global or default stylesheet):

package com.theriabook.resources {

import mx.core.IFactory;

import mx.core.UIComponent;

import mx.styles.CSSStyleDeclaration;

import mx.styles.StyleManager;

Enhancing and Extending Flex ControlsCHAPTER 8

Page 366: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

360 RIA WITH ADOBE FLEX AND JAVA

public class ComboBoxResource {

public var dataField : String = null;

public var destination:String=null;

public var dropdownWidth : int = 0;

public var editable:Boolean = false;

public var itemRenderer:IFactory = null;

public var labelFunction : Function = null;

public var method : String = null;

public var resourceProps : Array = [

“dataField”,”destination”,”dropdownWidth”,

“editable”,”itemRenderer”,”labelFunction”,”method”

];

public function ComboBoxResource() {

var sd:CSSStyleDeclaration =

StyleManager.getStyleDeclaration(“.comboBoxResourceDropDownStyle “);

if (!sd) {

sd = new CSSStyleDeclaration();

StyleManager.setStyleDeclaration(

“.comboBoxResourceDropDownStyle “, sd, false

);

sd.setStyle(“paddingBottom”, 0);

sd.setStyle(“paddingTop”, 0);

}

}

}

}

And finally, let’s make one more modification to the EmployeeComboBoxResource class. We’ll make it carry functions, in particular – the implementation of the labelFunction, which was included in the class on purpose. The full listing of the EmployeeComboBox is presented below:

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

<ComboBoxResource xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=”com.theriabook.resources.*”

dataField=”EMP_ID” labelFunction=”fullName”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployeesByName”

editable=”true”

dropdownWidth=”350”

>

<itemRenderer>

<mx:Component>

Enhancing and Extending Flex ControlsCHAPTER 8

Page 367: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 361

<mx:HBox height=”20”

horizontalScrollPolicy=”off” verticalScrollPolicy=”off” >

<mx:PhoneFormatter id=”phoneFormatter”/>

<mx:Label text=”{data.EMP_LNAME}” width=”40%”/><mx:VRule/>

<mx:Label text=”{data.EMP_FNAME}” width=”40%”/><mx:VRule />

<mx:Label text=”{phoneFormatter.format(data.PHONE)}” width=”20%”/>

</mx:HBox>

</mx:Component>

</itemRenderer>

<mx:Script>

<![CDATA[

import com.theriabook.composition.dto.*;

private var linkage:EmployeeDTO;

private function fullName(data:Object):String {

return data.EMP_LNAME + “,” + data.EMP_FNAME;

}

]]>

</mx:Script>

</ComboBoxResource>

Listing 8.27 EmployeeComboBox control

This change will affect all the screens using this resource without needing to modify each and every use case. Unlike the direct classes, like EmployeeComboBox, the resources don’t need to be linked with the modules that use them, making development by a team of programmers much easier. And it shows that the resources are full-blown classes, letting you mix data, presentation, and script.

This concludes the illustration of the resource-classes technique. Arguably, this is a much cleaner way to separate code from metadata: you modify the resource property on the ComboBox to a CustomerComboBoxResource of your own and – voila! – you can search customers instead of em-ployees using the same code.

In industrial-scale systems spanning a set of Flex projects, it makes sense to maintain the resources as well as the reusable components in shared runtime libraries. We’ll cover the topic of creating, loading, and using such libraries in Chapter 10.

SummaryIn this chapter we went through several practical examples of customizing a Flex component, namely ComboBox.3 Such customization has several benefits:

• Your productivity increases as you create components that can be reused in multiple projects. • Commonly used components can help you divide your application into distinct modules.• The functionality of Flex controls can be substantially enhanced.

Enhancing and Extending Flex ControlsCHAPTER 8

Page 368: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

362 RIA WITH ADOBE FLEX AND JAVA

• The new breed of destination-aware components can in some cases literally provide drag-and-drop programming of Web applications. You drop a component on the screen that not only knows on which remote server its data resides, but also arrives on your Web browser pre-populated with data.

The Flex component model lets you extend visual and non-visual components as you see fit.

Enhancing and Extending Flex ControlsCHAPTER 8

Endnotes1. A major difference between the two ways of casting is not highlighted here, but is worth mentioning

anyway: if an object you are trying to cast a traditional way is not matching the target type, you will get a

runtime exception; under the same scenario as casting will return null.

2. Strictly speaking Flash Player operates with class definitions that get loaded into application domains;

here we are discussing the presence of the definition of EmployeeDTO.as in the main application domain.

3. Please be aware that as of Chapter 8 we have not made sure that the ComboBox will work well as a ren-

derer in the List-based controls. The complete version would require overriding the setter for the data

property. The adequate ComboBox code can be found inside /TheRiaBook/code/flex/theriabook folder of

the DVD that comes with the book.

Page 369: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 365

Trees with Dynamic Data Population

9CHAPTER

Page 370: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

366 RIA WITH ADOBE FLEX AND JAVA

In Chapter 3, we introduced the Flex Tree control in a very simplistic scenario. It’s time to switch gears and slowly push the accelerator pedal to the floor.

We will cover the following techniques on the Flex Tree:• A dynamic on-demand population of tree branches from a remote data source rather than

from a static XML or hierarchy of arrays.• Encapsulation of data-acquisition logic similar to our DataCollection and destination-aware

ComboBox from Chapters 7 and 8.• Custom rendering of the tree so checkboxes can be added to the nodes. During this we’ll take

a detailed look at drop-in item renderers as opposed to the inline item renderers that we introduced in Chapter 8.

Basics of Tree ControlTree control is a data-driven control derived from the List control. It lets hierarchical data be pre-sented as a structure of the branch and leaf nodes, which comes in handy when you have to display a hierarchy of some items that have parent-child relations. A good example of a multi-level hier-archy is a computer file system that can be represented as a tree structure of folders and files. A two-level hierarchy would be the American states and their cities.

What comes to mind when you have to represent a hierarchical data structure, but forgot every-thing you learned in your college Data Structures class? XML, of course. Let’s build a simple tree based on a static XML file of U.S. states and cities.

<?xml version=”1.0”?>

<!-- PlainXmlTreeDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”>

<mx:Tree id=”tree1” labelField=”@label” showRoot=”false”

width=”160”>

<mx:dataProvider>

<mx:XML format=”e4x”>

Trees with Dynamic Data Population CHAPTER 9

Trees with Dynamic Data Population

Page 371: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 367

<states>

<state label=”California” data=”CA” isBranch=”true”/>

<state label=”Pennsylvania” data=”PA”>

<city label=”Philadelphia” population=”1479339” />

</state>

<state label=”Arizona” data=”AZ” isBranch=”true”/>

<state label=”Texas” data=”TX” />

<state label=”Illinois” data=”IL” />

<state label=»New York» data=»NY» />

</states>

</mx:XML>

</mx:dataProvider>

</mx:Tree>

</mx:Application>

Listing 9.1 PlainXmlTreeDemo.mxml

The Tree control gets its data from a hierarchical data provider. Since the dataProvider property of the control is implemented as a setter function, there’s a lot of flexibility in what we can assign. In particular, instead of the E4X XML object (above) we could have assigned a string with a valid XML text, XMLList, XMLListCollection, or any object that could be interpreted as a hierarchical data provider according to the rules of the ITreeDataDescriptor interface.

In Listing 9.1, only the Pennsylvania <state> node has a real child – the <city> of Philadelphia. But if you look at Figure 9.1, you’ll notice that California and Arizona are also folder nodes. We’ve achieved this effect by using the node attribute isBranch=”true.” Similarly, if for any reason you don’t want to process a particular branch of the tree, explicitly set isBranch=“false” and the child nodes will be ignored.

Each node in our XML contains an attribute “label” that is supposed to be displayed as a value of the tree node. Had it been an array of objects, each containing a label property, Tree controls could readily act on that. However, we’re dealing with XML and because of that we have to explicitly specify via the labelField property of the Tree control that our labels are stored as attributes:

labelField=”@label”

Why? The attribute “label” and the child node <label> are equally good choices (and that’s just for starters because we could have had a child node <name> with the attribute “label”, etc.). How on earth does the Tree control know the location of the label? It doesn’t, until we intervene.

Another specific of XML data sources is that they always have a root node. If you don’t want to dis-play the root node, just set the showRoot property to false.

Trees with Dynamic Data Population CHAPTER 9

Page 372: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

368 RIA WITH ADOBE FLEX AND JAVA

Figure 9.1 A plain XML-based TreeView component

Our next example will use an Array as a data provider. Note that we don’t need to set a showRoot=”false” here. We also don’t have to explicitly declare a labelField= “label” because the label attribute is assumed by default.

In the next example we’ll use nested arrays of ActionScript objects to populate the tree. A default implementation of ITreeDataDescriptor interface – DefaultDataDescriptor – interprets the children property as a repository of the branch nodes. With MXML we can lay out sub-node arrays with an mx:children tag:

<?xml version=”1.0”?>

<!-- PlainArrayTreeDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”>

<mx:Tree id=”tree1” width=”160”>

<mx:dataProvider>

<mx:Array>

<mx:Object label=”California” data=”CA” >

<mx:children><mx:Array/></mx:children>

</mx:Object>

<mx:Object label=”Arizona” data=”AZ” >

<mx:children><mx:Array/></mx:children>

</mx:Object>

<mx:Object label=”Pennsylvania” data=”PA”>

Trees with Dynamic Data Population CHAPTER 9

Page 373: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 369

<mx:children>

<mx:Object label=”Philadelphia” population=”1479339” />

</mx:children>

</mx:Object>

<mx:Object label=”Texas” data=”TX” />

<mx:Object label=”Illinois” data=”IL” />

<mx:Object label=»New York» data=»NY» />

</mx:Array>

</mx:dataProvider>

</mx:Tree>

</mx:Application>

Listing 9.2 PlainArrayTreeDemo.mxml

Warning: When you use arrays as data providers, you can’t use the property isBranch.

The Role of dataDescriptor A Tree control delegates all parsing, adding and removing nodes to a specific controller object. This controller object is registered via the dataDescriptor property of the Tree control and, by default, refers to an internally created instance of the DefaultDataDescriptor that is a default implementa-tion of the ITreeDataDescriptor interface. Those relevant to our chapter methods of the ITreeD-ataDescriptor interface are presented in Table 9.1.

addChildAt(item:Object, child:Object, index:int, model:Object = null):Boolean

Adds a child node to a node at the specified index.

getChildren(item:Object, model:Object = null):ICollection-View

Provides access to a node’s chil-dren, returning a collection view of the children if they exist.

isBranch(item:Object, model:Object = null):Boolean

Tests a node for termination.

removeChildAt(item:Object, child:Object, index:int, model:Object = null):Boolean

Removes a child node to a node at the specified index.

Table 9.1

While these APIs are definitely fine for shaping your Tree control to any form, they’re synchronous methods. As long as data is already on the client side, these methods let you customize the process of assembling the tree. But the ability to act as a retriever for the server data is entirely missing here.

Trees with Dynamic Data Population CHAPTER 9

Page 374: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

370 RIA WITH ADOBE FLEX AND JAVA

Moving to the Real Asynchronous WorldIn a standard Flex Tree component a getChildren() callback is called synchronously; it waits until this method’s code completes. On the other hand, when you request the remote data, be it with the RemoteObject, WebService, DataService, or messaging, your program gets the data asynchro-nously. This means that ITreeDataDescriptor is helpless when you have to dynamically compose your tree from remote data sources.

Abandoning ITreeDataDescriptor, we’ll illustrate the approach to dynamic population of the tree based on using the itemOpen event of the Tree control.

Let’s create a tree populated by a pseudo-remote data feed. To emulate the asynchronous nature of data retrieval we’ll use the setTimeout() function. We’ll also use plain vanilla arrays for data and focus on handling the asynchronous data returns.

The itemOpen event is dispatched when a corresponding branch has been expanded. While pro-cessing this event, we’ll only initiate data retrieval. We can’t expect more from an asynchronous call, can we? However, while initiating data retrieval we should also make a provision that lets us identify the response when (and if) it comes back. The program from Listing 9.3 dynamically ap-pends the nodes by reacting to the event itemOpen.

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

<!--CompositeAsyncLocalDataTreeDemo.mxml-->

<mx:Application

xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=»*»

xmlns:lib=»com.theriabook.controls.*»

layout=»vertical»

creationComplete=»onCreationComplete()»>

<mx:Script>

<![CDATA[

import mx.collections.*;

import mx.events.*;

import mx.managers.CursorManager;

private var bigStates:Array=[

{label:»California», data:»CA», children:[]},

{label:»Pennsylvania», data:»PA», children:[]},

{label:»Arizona», data:»AZ», children:[]},

{label:»Texas», data:»TX», children:[]},

{label:»Illinois», data:»IL», children:[]},

{label:»New York», data:»NY», children:[]},

];

private var stateCities:Object={

Trees with Dynamic Data Population CHAPTER 9

Page 375: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 371

AZ: [ {label:»Phoenix», population:1388416} ],

CA: [ {label:»Los Angeles», population:3819951},

{label:»San Diego», population:1266753} ],

TX: [ {label:»Dallas», population:1208318},

{label:»Houston», population:2009690},

{label:»San Antonio», population:1214725}],

PA: [ {label:»Philadelphia», population:1479339} ],

NY: [ {label:»New York», population:8085742} ],

IL: [ {label:»Chicago», population:2869121} ]

};

public function onCreationComplete():void {

tree.addEventListener(«itemOpen», tree_onItemOpen);

//Emulate asynch call to RPC object

CursorManager.setBusyCursor();

var RESPONSE_TIME:uint = 2000;

setTimeout(onRootChildrenReady, RESPONSE_TIME, bigStates);

}

public function tree_onItemOpen(event:TreeEvent):void {

var item:Object = event.item;

if (item.isPopulated == undefined) {

item.isPopulated = true;

CursorManager.setBusyCursor();

tree.dataDescriptor.addChildAt(

item, {label:»...»}, 0, tree.dataProvider

);

//Emulate the asynch call to RPC object

var RESPONSE_TIME:uint = 2000;

setTimeout(onChildrenReady, RESPONSE_TIME, item, stateCities[item.data]);

}

}

public function onChildrenReady(item:Object, children:Array):void {

var list:IList =

IList(tree.dataDescriptor.getChildren(item,tree.dataProvider))

var ellipsisItem:Object = list.getItemAt(0);

tree.dataDescriptor.removeChildAt(

item, ellipsisItem, 0, tree.dataProvider

);

for (var i:int=0; i<children.length; i++) {

tree.dataDescriptor.addChildAt(

Trees with Dynamic Data Population CHAPTER 9

Page 376: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

372 RIA WITH ADOBE FLEX AND JAVA

item, children[i], item.children.length, tree.dataProvider

);

}

tree.expandItem(item,false);

tree.expandItem(item,true,true);

CursorManager.removeBusyCursor();

}

public function onRootChildrenReady(children:Array):void {

tree.dataProvider = new ArrayCollection(children);

CursorManager.removeBusyCursor();

}

]]>

</mx:Script>

<mx:Tree id=»tree» width=»50%» />

</mx:Application>

Listing 9.3 CompositeAsyncLocalDataTreeDemo.mxml

During the application start, the CompositeAsyncLocalDataTreeDemo registers a listener to the tree’s itemOpen event and then emulates the RPC call to populate the children of the root node:

public function onCreationComplete():void {

tree.addEventListener(“itemOpen”, tree_onItemOpen);

//Emulate asynch call to RPC object

CursorManager.setBusyCursor();

var RESPONSE_TIME:uint = 2000;

setTimeout(onRootChildrenReady, RESPONSE_TIME, bigStates);

}

The code snippet above says, “Two seconds from now please call the function onRootChildren-Ready(), passing it the argument array bigStates.”

Our function onRootChildrenReady() simply assigns the array of the states to the tree’s dataPro-vider. Since we’ve started the busy cursor during the onCreationComplete(), on the data “return” we need to take it out:

public function onRootChildrenReady(children:Array):void

// Imagine that the data came back from RPC

tree.dataProvider = new ArrayCollection(children);

CursorManager.removeBusyCursor();

}

Now that we’ve taken care of the root level, let’s look at the children. The expansion of a tree item

Trees with Dynamic Data Population CHAPTER 9

Page 377: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 373

will be intercepted by our event listener – tree_OnItemOpen shown below. Please note that “item” is an instance of the Object, i.e., the dynamic class. The property item.isPopulated is a dynamic property that’s set just once to indicate that the node has been populated.

Prior to sending a request for children we create a visual node that contains the label “…” (ellip-sisItem) to indicate the progress to the user. It will be removed when the data comes back and the folder is filled.

public function tree_onItemOpen(event:TreeEvent):void {

var item:Object = event.item;

if (item.isPopulated == undefined) {

item.isPopulated = true;

CursorManager.setBusyCursor();

tree.dataDescriptor.addChildAt(

item, {label:”...”}, 0, tree.dataProvider

);

//Emulate asynch call to RPC object

var RESPONSE_TIME:uint = 2000;

setTimeout(onChildrenReady, RESPONSE_TIME, item, stateCities[item.data]);

}

}

As soon as the first child arrives, we have to remove the ellipsis and repopulate the branch by add-ing the real children. We cast the children of the item to IList, which gives us an agnostic way of navigating through the collection, be it a collection based on XMLList or Array. Once ellipsisItem is removed, we add all children iteratively. In both item removal and addition, we delegate to the tree’s dataDescriptor. To ensure the immediate visual update of the tree branch, we collapse and then expand the item again:

public function onChildrenReady(item:Object, children:Array):void {

var list:IList =

IList(tree.dataDescriptor.getChildren(item,tree.dataProvider))

var ellipsisItem:Object = list.getItemAt(0);

tree.dataDescriptor.removeChildAt(

item, ellipsisItem, 0, tree.dataProvider

);

for (var i:int=0; i<children.length; i++) {

tree.dataDescriptor.addChildAt(

item, children[i], item.children.length, tree.dataProvider );

}

tree.expandItem(item,false);

Trees with Dynamic Data Population CHAPTER 9

Page 378: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

374 RIA WITH ADOBE FLEX AND JAVA

tree.expandItem(item,true,true); CursorManager.removeBusyCursor();

}

No More Fake Remoting!In this section we’ll start using real remote data. Let’s switch to our regular database department/employee sample. The table structure of the database tables is listed in Appendix 3. Our goal is to write the application that’s presented in Figure 9.2.

Figure 9.2 Departments and employees

To relate the process to Java EE standard procedures in the following sections, we’ll present a hand-ful of patterns used in the book for one and only one purpose: to justify our naming convention.

Design Patterns in Our LifeWe’ve done quite a few code snippets so far. Let’s present a snippet of a different sort, one from the play by French author Jean Baptiste Molière, The Bourgeois Gentlemen, written more then 300 years ago. Here M. Jourdain wants his philosophy master to write a letter for him. Does he want it in verse or prose? What’s prose? Whatever isn’t verse:

Trees with Dynamic Data Population CHAPTER 9

Page 379: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 375

Philosopher: Did you want to write…poetry? M. Jordain: No, definitely not poetry. Philosopher: Right, so a piece of prose then. M. Jordain: Ugh, no! Can’t stand prose either! Philosopher: I’m afraid it will have to be one or the other. M. Jordain: Why? Philosopher: Because, Monsieur, there are regrettably only two forms in which you may express yourself: poetry and prose. M. Jordain: What? Nothing else? Philosopher: No, Monsieur. All that is not poetry is prose and all that is not prose, sadly, is po etry. M. Jordain: What about the stuff we talk all day? What do you call that? Philosopher: Prose. M. Jordain: What? You mean to tell me that when I say, “Nicole, bring me my slippers and nightcap,” that’s prose? Philosopher: Yes, Monsieur. M. Jordain: Snap me! Here I am speaking prose for over 40 years without ever realizing it! I am very grateful to you for teaching me that!

This joke has obviously been circulating for a long time now. But please note the following: M. Jor-dain takes pride in his sudden intimacy with prose, but he’s not sure whether it’s a universal ability or whether he’s special.

By now you have probably guessed our take on the subject. Time and again the authors of this book have been pleasantly surprised to find how often we speak… well, use one or the other pattern without realizing it.

Data Transfer ObjectSometimes this design pattern is also called a Data Transfer Object or Value Object. The Data Trans-fer Object is a wrapper class that holds multiple discrete values that are passed from one application tier to another. In our case, we called the data class describing the department – DepartmentDTO:

public class DepartmentDTO {

public int DEPT_ID;

public String DEPT_NAME;

public int DEPT_HEAD_ID;

}

Listing 9.4 DepartmentDTO.java

You can read more on the DTO pattern at http://www.corej2eepatterns.com/Patterns2ndEd/TransferObject.htm.

Trees with Dynamic Data Population CHAPTER 9

Page 380: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

376 RIA WITH ADOBE FLEX AND JAVA

Data Access Object The Data Access Object (DAO) pattern promotes the separation of the client and physical data source implementation. DAO acts as a middleman between the client and the data source (not necessarily a database). In our scenario, we call the class that takes full responsibility for retrieving from the database DepartmentDAO:

public final class DepartmentDAO {

public final DepartmentDTO[] getDepartments(){

String sql = “select * from DEPARTMENT”;

DepartmentDTO[] result = null;

ArrayList list = new ArrayList();

PreparedStatement stmt = null;

ResultSet rs = null;

Connection conn = null;

try {

conn = JDBCConnection.getConnection(“jdbc/theriabook”);

stmt = conn.prepareStatement(sql);

rs = stmt.executeQuery();

while( rs.next() ) {

DepartmentDTO to = new DepartmentDTO();

to.DEPT_ID = (rs.getInt(“DEPT_ID”));

to.DEPT_NAME = (rs.getString(“DEPT_NAME”));

to.DEPT_HEAD_ID = (rs.getInt(“DEPT_HEAD_ID”));

list.add(to);

}

result = new DepartmentDTO[list.size()];

result = (DepartmentDTO[])list.toArray(result);

return result;

} catch (SQLException se){…}

} catch(Throwable te){…}

} finally {// Close the JDBC objects here}

}

}

Listing 9.5 The code fragment of DepartmentDAO.java

Complete code for DepartmentDAO.java is on the DVD that is included with the book.

You can find the formal description of the DAO design pattern at http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html.

Trees with Dynamic Data Population CHAPTER 9

Page 381: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 377

Asynchronous Completion TokenThe Asynchronous Completion Token (ACT) design pattern solves identity problems inherent in the processing of callbacks of asynchronous operations. To illustrate the usefulness of the ACT pat-tern, let’s assume the user will click on one node of the Tree control, and then within a split second on another one. If the nodes are populated by asynchronous calls through the RemoteObject and the concurrency mode of the call is set to multiple, the results can come in any order. How then can we tell the results of one request from another? To do this, each asynchronous operation or, to be more specific, HTTP request that carries such an operation is associated with a unique object – Asynchronous Completion Token.

ACT is created before the HTTP request is sent to the server (you remember that all RPC requests go through HTTP, don’t you?). Importantly, a “stub” of the ACT travels to the server. Both the cli-ent and server continue their processing, and when the server sends a response, bad or good, it includes the ACT “stub” in the response. Then, the client uses the ACT stub to identify the original ACT object and all the properties associated with it react accordingly. For example, Listing 9.7 has the following lines:

var token:AsyncToken = remoteObject.getChildren(item);

token.item = item;

To understand these lines, think of how people mail a certified letter at the post office. First you get a receipt #12345 (var token) confirming that you’ve asked for a particular postal service (getChil-dren). You can scribble any useful information on this receipt, such as a reference to an item. The fact that you have a receipt doesn’t mean that the letter has been delivered to the recipient. Two days later, confirmation #12345 comes in the mail, so you can make a note that this particular letter has been delivered and do whatever you need with the item reference, which has been thoughtfully scribbled on your receipt. Here’s an example of how an ACT token can be used at the completion of the operation:

private function remoteObject_onResult(event:ResultEvent):void {

var token:AsyncToken = event.token;

var item:Object = event.token.item;

var children:Array = event.result as Array;

onChildrenReady(item,children);

}

For more details on the ACT pattern, please read http://www.cs.wustl.edu/~schmidt/PDF/ACT.pdf.

Assembler Design PatternThe Classic Transfer Object Assembler constructs a composite Data Transfer Object that represents the data from different data sources. The DTO holds all the data for the model(s) and is delivered to the client in one method call (http://java.sun.com/blueprints/corej2eepatterns/Patterns/Trans-ferObjectAssembler.html).

Trees with Dynamic Data Population CHAPTER 9

Page 382: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

378 RIA WITH ADOBE FLEX AND JAVA

The Flex Developer’s Guide in the section “Data Management Service Destinations” says it differ-ently: “An assembler class is a Java class that interacts indirectly or directly with a data resource. A common design pattern is for assembler to call a Data Access Object (DAO) that calls a data resource.”

Not that this deviation is accidental – it represents a major paradigm shift toward the assembly of DTOs in the client. Accordingly, the server-side Assembler most of the time (but not necessarily always) acts as a pure façade.

Façade Design PatternFaçade helps to hide the internals of the server-side processing from the client. In our scenario, the client’s code doesn’t have to know that we have separate DAOs for departments, employees, and whatnot. Our TreeViewAssembler has the overloaded method getChildren(): depending on the class of the parameter this method will return the children for each type of tree node.

This façade will return an array of appropriate DTOs, either departments or employees:

public class TreeViewAssembler {

public DepartmentDTO[] getChildren() {

return new DepartmentDAO().getDepartments();

}

public EmployeeDTO[] getChildren(DepartmentDTO parentNode) {

return new EmployeeDAO().getEmployeesByDepartment(“” +

parentNode.DEPT_ID);

}

}

Listing 9.6 TreeViewAssembler.java

To make this class accessible from the client, we’ll add the following destination in the flex-remot-ing-service.xml:

<?xml version=”1.0” encoding=”UTF-8”?>

<service id=”remoting-service” . . .>

. . . .

<destination id=”treeAssembler”>

<properties>

<source>com.theriabook.assembler.TreeViewAssembler</source>

</properties>

</destination>

</service >

Trees with Dynamic Data Population CHAPTER 9

Page 383: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 379

For more information on the façade pattern, please visit the Web page http://en.wikipedia.org/wiki/Facade_pattern.

Working with Employees and Departments In this section we’ll present an application shown in Figure 9.4. The client/server communication will utilize DAO, Façade, Assembler, DTO, and ACT patterns.

The code of the application – CompositeRemoteDataTreeDemo.mxml – is shown in Listing 9.7. Please note how we make use of the Java overloading of the method getChildren() in our tree-Assembler. We populated the top level of the tree by calling the no-argument getChildren() that returns DepartmentDTO[], yet populating the departments within tree_onItemOpen() was done with getChildren(item), which returns the EmployeeDTO[] array.

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

<!-- CompositeRemoteDataTreeDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”vertical” creationComplete=”onCreationComplete()”>

<mx:Script>

<![CDATA[

import mx.controls.Alert;

import mx.collections.*;

import mx.events.*;

import mx.rpc.*;

import mx.rpc.events.*;

import mx.managers.CursorManager;

private function onCreationComplete():void {

tree.addEventListener(«itemOpen», tree_onItemOpen);

tree.dataProvider=[{label: “...”}];

var token:AsyncToken = remoteObject.getChildren();

token.item = null;

}

private function tree_onItemOpen(event:TreeEvent):void {

var item:Object = event.item;

if (item.isPopulated == undefined) {

item.isPopulated = true;

CursorManager.setBusyCursor();

addEllipsis(item);

var token:AsyncToken = remoteObject.getChildren(item);

token.item = item;

}

}

Trees with Dynamic Data Population CHAPTER 9

Page 384: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

380 RIA WITH ADOBE FLEX AND JAVA

private function remoteObject_onResult(event:ResultEvent):void {

CursorManager.removeBusyCursor();

var token:AsyncToken = event.token;

var item:Object = event.token.item;

var children:Array = event.result as Array;

onChildrenReady(item,children);

}

private function onChildrenReady(item:Object, children:Array):void {

removeEllipsis(item);

for (var i:int=0; i<children.length; i++) {

tree.dataDescriptor.addChildAt(

item, children[i],

item?item.children.length:tree.dataProvider.length, tree.dataProvider

);

}

tree.expandItem(item,false);

tree.expandItem(item,true,true);

CursorManager.removeBusyCursor();

}

private function addEllipsis(item:Object):void {

tree.dataDescriptor.addChildAt(

item, {label:”...”}, 0, tree.dataProvider

);

}

private function removeEllipsis(item:Object):void {

var list:IList;

if (item!=null)

list = IList(tree.dataDescriptor.getChildren(item,tree.dataProvider));

else

list = IList(tree.dataProvider);

var ellipsisItem:Object = list.getItemAt(0);

tree.dataDescriptor.removeChildAt(

item, ellipsisItem, 0, tree.dataProvider

);

}

private function remoteObject_onFault(event:FaultEvent):void {

Trees with Dynamic Data Population CHAPTER 9

Page 385: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 381

CursorManager.removeBusyCursor();

var token:AsyncToken = event.token;

var item:Object = event.token.item;

removeEllipsis(item);

// Do your own error processing ...

Alert.show(“Failed retrieving data: “+event.fault.description,

“[ CompositeRemoteDataTreeDemo]” + id);

}

// Make sure classes are linked into SWF

import com.theriabook.composition.dto.*;

private var linkage:Object={l1:DepartmentDTO, l2:EmployeeDTO};

]]>

</mx:Script>

<mx:Tree id=”tree” width=”50%” height=”100%”/>

<mx:RemoteObject id=”remoteObject” destination=”treeAssembler”

result=”remoteObject_onResult(event)” showBusyCursor=”true” concurrency=”multiple”

fault=”remoteObject_onFault(event)”>

<mx:method name=”getChildren” />

</mx:RemoteObject>

</mx:Application>

Listing 9.7 CompositeRemoteDataTreeDemo.mxml

The Destination-Aware TreeEarlier in the book we introduced destination-aware DataCollection and ComboBox. In this sec-tion we’ll apply similar techniques to encapsulate the data acquisition logic inside the custom Tree control. The goal is to replace the rather long CompositeRemoteDataTreeDemo.mxml with the simplified application shown in Listing 9.8.

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

<!-- CompositeDestinationAwareTreeDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=”com.theriabook.controls.*” layout=”vertical”

initialize=”onCreationComplete()”>

<mx:Script>

<![CDATA[

public function onCreationComplete():void {

tree.fill(null);

Trees with Dynamic Data Population CHAPTER 9

Page 386: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

382 RIA WITH ADOBE FLEX AND JAVA

}

// Force to link all required objects into SWF

import com.theriabook.composition.dto.*;

internal var linkage:Object={t1:DepartmentDTO, t2:EmployeeDTO};

]]>

</mx:Script>

<lib:Tree id=”tree” width=”50%” height=”100%”

destination=”treeAssembler” >

</lib:Tree>

</mx:Application>

Listing 9.8 MXML application with the destination-aware Tree

This code looks more elegant than the one in Listing 9.7, doesn’t it? This is encapsulation in ac-tion.

In particular, we will move the creationComplete() code from the application in Listing 9.7 into the custom Tree control’s creationComplete(). This, in turn, will enable us to keep the onItemOpen() event listener as a private method of our Tree.

Also, similar to the DataCollection and destination-aware ComboBox, we will create a fill() method, which executes the remote call against the externally provided destination. To simplify the code, we stick to the name getChildren(). (In comparison, in Chapter 8 we showed how to parameterize both destination and method.)

Naturally, we will localize the instantiation of the RemoteObject along with onResult() and on-Fault() callbacks. Notice, however, that now we will change the concurrency mode to multiple, so that the fast clicking user could expand more than one item at a time. The last remaining move is to encapsulate onChildrenReady() and that will complete our job:

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

<!-- [CompositeDestinationAware]Tree.mxml -->

<mx:Tree xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*”

creationComplete=”onCreationComplete()”>

<mx:Script>

<![CDATA[

import mx.collections.*;

import mx.controls.Alert;

import mx.events.TreeEvent;

import mx.managers.CursorManager;

import mx.rpc.*;

import mx.rpc.events.*;

Trees with Dynamic Data Population CHAPTER 9

Page 387: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 383

import mx.rpc.remoting.mxml.RemoteObject;

public function onCreationComplete():void {

addEventListener(«itemOpen», onItemOpen);

}

private function onItemOpen(event:TreeEvent):void {

var item:Object = event.item;

if (item.isPopulated == undefined) {

item.isPopulated = true;

CursorManager.setBusyCursor();

addEllipsis(item);

fill(item);

}

}

public var destination:String=null;

protected var ro:RemoteObject = null;

public function fill(item:Object):Object {

if (dataProvider==null) {

dataProvider = [{label: «...»}];

}

if( ro==null ) ro = createRemoteObject();

var token:AsyncToken;

if (item==null)

// We help Flex RPC gateway with specific -

// getChildren() - signature for root level

token = AsyncToken(ro.getChildren());

else

token = AsyncToken(ro.getChildren(item));

token.item = item;

return token;

}

protected function createRemoteObject():RemoteObject {

var ro:RemoteObject;

if( destination==null || destination.length==0 )

throw new Error(«No destination specified»);

ro = new RemoteObject(destination);

ro.concurrency = «multiple”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

Trees with Dynamic Data Population CHAPTER 9

Page 388: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

384 RIA WITH ADOBE FLEX AND JAVA

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

return ro;

}

private function ro_onResult(event:ResultEvent):void {

CursorManager.removeBusyCursor();

var token:AsyncToken = event.token;

var item:Object = event.token.item;

var children:Array = event.result as Array;

onChildrenReady(item,children);

}

private function onChildrenReady(item:Object, children:Array):void {

removeEllipsis(item);

for (var i:int=0; i<children.length; i++) {

dataDescriptor.addChildAt(

item, children[i],

item?item.children.length:dataProvider.length, dataProvider

);

}

expandItem(item,false);

expandItem(item,true,true);

CursorManager.removeBusyCursor();

}

private function addEllipsis(item:Object):void {

dataDescriptor.addChildAt(

item, {label:”...”}, 0, dataProvider

);

}

private function removeEllipsis(item:Object):void {

var list:IList;

if (item!=null)

list = IList(dataDescriptor.getChildren(item,dataProvider));

else

list = IList(dataProvider);

var ellipsisItem:Object = list.getItemAt(0);

if ((ellipsisItem) && (ellipsisItem.label==”...”)) {

dataDescriptor.removeChildAt(

item, ellipsisItem, 0, dataProvider

);

}

Trees with Dynamic Data Population CHAPTER 9

Page 389: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 385

}

private function ro_onFault(event:FaultEvent):void {

CursorManager.removeBusyCursor();

var token:AsyncToken = event.token;

var item:Object = event.token.item;

removeEllipsis(item);

// Do your own error processing ...

Alert.show(“Failed retrieving data: “+event.fault.description,

“[CompositeDestinationAwareTree]” + id);

}

]]>

</mx:Script>

</mx:Tree>

Listing 9.9 CompositeDestinationAwareTree.mxml

You may ask, what’s the big deal? The total number of lines of code stays almost the same. There’s an important difference though. We’ve created a reusable Tree component that can be included in any application that needs composite hierarchical data and the code to do this will be almost identical to Listing 9.8. All you need to do to adapt it to different databases/tables is to change the code in the treeAssembler and DAOs behind it. The Tree we created is data-agnostic. The only as-sumption it makes is that you’ll provide a POJO assembler with overloaded getChildren() methods that return whatever your business hierarchy is about. This solution supports as many levels of hierarchy as the treeAssembler cares to provide.

Adding Checkboxes to a TreeUp till now, we’d been building a data-agnostic Tree control that features a dynamic population from a remote data source on the server. The second half of this chapter will be dedicated exclu-sively to the client. More specifically, we’ll gradually build “checkboxed” Tree, i.e., a Tree with check-boxes in each item as shown in Figure 9.5.

As a reminder, List-based controls (Tree, List, DataGrid, et al) delegate the display of individu-al data items using so-called item renderers. We’ve touched on this topic in Chapter 5, where we made the DataGrid column appear as a clickable LinkButton and in Chapter 8 where we emulated a “dropdown DataGrid” inside the ComboBox List part. In both cases we’ve been placing our own item renderers instead of default ones. Similarly, we will be replacing the default item renderer of the Tree control.

Trees with Dynamic Data Population CHAPTER 9

Page 390: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

386 RIA WITH ADOBE FLEX AND JAVA

Figure 9.3 Checkboxed Tree

Listing 9.10 contains the MXML code of the application that will make use of our custom item renderer (once we complete it toward the end of the chapter) to produce such a checkboxed tree:

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

<!-- CheckedTreeDemo.mxml -->

<mx:Application

xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=»com.theriabook.controls.*»

layout=»vertical»

initialize=»onCreationComplete()»>

<mx:Script>

<![CDATA[

public function onCreationComplete():void {

tree.fill();

}

// Force Flex to link all required objects into the SWF

Trees with Dynamic Data Population CHAPTER 9

Page 391: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 387

import com.theriabook.composition.dto.*;

internal var linkage:Object={t1:DepartmentDTO, t2:EmployeeDTO};

]]>

</mx:Script>

<lib:Tree id=»tree» width=»50%» height=»100%» destination=»treeAssembler»

itemRenderer=»com.theriabook.controls.CheckedTreeItemRenderer»>

</lib:Tree>

</mx:Application>

Listing 9.10 CheckedTreeDemo.mxml

This code is pretty compact for the obvious reason: all functionality is encapsulated in the CheckedTreeItemRenderer.as. Now we’ll start building this item renderer.

Customizing the TreeItemRendererThere are two types of item renderers:

• drop-in renderer is a class implementing several mandatory interfaces, IDropInListItemRen-derer in particular. An explicit class of the drop-in renderer can be specified as the value of the itemRenderer property of a list-derived control (you can specify a class name or an existing instance of the class). Creating a drop-in renderer may take a bit more time, but it will give you a reusable component

• Inline is a render implemented using <mx:Component> and <mx:itemRenderer> tags to define the renderer component inside the MXML of the hosting List control. Inline renderers are good for ad hoc/prototyping purposes, but they clutter code, especially if you have more then one in the same MXML. Also, once you need a correction in your renderer, you’d have to replicate the change in all instances.

The Flex Tree control employs a default drop-in renderer called TreeItemRenderer. This renderer draws the text associated with each item in the tree, an optional icon, and – for branch nodes – a default triangle disclosure icon (by default, the triangle) that the user can click on to expand or col-lapse the branch.

If you peek at the source code of this renderer – mx.controls.treeClasses.TreeItemRenderer.as – you’ll see all interfaces that it implements:

public class TreeItemRenderer extends UIComponent

implements IDataRenderer, IDropInListItemRenderer, IlistItemRenderer {

public var disclosure:IFlexDisplayObject;

public var icon:IFlexDisplayObject;

Trees with Dynamic Data Population CHAPTER 9

Page 392: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

388 RIA WITH ADOBE FLEX AND JAVA

public var label:UITextField;

}

In our case, when we’re adding a checkbox to each item of the Tree, extending TreeItemRenderer seems quite natural. Accordingly, we’ll be building our custom CheckedTreeItemRenderer as a de-scendant of TreeItemRenderer.

According to the Flex manual, all children of the custom components must be created in the cre-ateChildren() method. TreeItemRenderer follows the rules and so will we. In our first version of the custom renderer – CheckedTreeItemRenderer1 – we’ll override the createChildren() method. That will let us create a default set of children by delegating to super.createChildren() and then add an extra CheckBox child. Please note how we follow the Flex component creation practice of passing the component’s style definition to its children:

package com.theriabook.controls {

import mx.controls.CheckBox;

import mx.controls.treeClasses.*;

public class CheckedTreeItemRenderer1 extends TreeItemRenderer {

public var checkBox:CheckBox;

override protected function createChildren():void

{

super.createChildren();

if (!checkBox) {

checkBox = new CheckBox();

checkBox.styleName = this;

addChild(checkBox);

}

}

}

}

Listing 9.11 The first version of the CheckedTreeItemRenderer

This first version of the custom renderer produces the screen shown in Figure 9.6. We have omitted the listing of the CheckedTreeDemo1 application since it is the exact replica of CheckedTreeDemo with CheckedTreeItemRenderer1 instead of CheckedTreeItemRenderer:

Trees with Dynamic Data Population CHAPTER 9

Page 393: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 389

Figure 9.4 The first version of the checkboxed tree

The empty squares on the left side are the empty checkboxes added by the renderer. Clearly the checkboxes aren’t participating in the layout and have landed at the leftmost position of each List item.

Following the manual, positioning (as well as sizing) the component children has to be done via the updateDisplayList() method, which is called before the Flex component appears on the screen. Flex schedules a call to the updateDisplayList() in response to one or more calls to another method – invalidateDisplayList(). At any rate, the very execution of the updateDisplayList() happens during the next rendering event. The implementation of the addChild() method also automatically results in invalidateDisplayList() and so causes updateDisplayList().

In other words, we can address positioning issues by overriding updateDisplayList(), which is guar-anteed to happen due to our addChild() in particular. The second version of the renderer is shown in Listing 9.12.

//CheckedTreeItemRenderer2.as

package com.theriabook.controls {

import mx.controls.treeClasses.*;

import mx.controls.*;

public class CheckedTreeItemRenderer2 extends TreeItemRenderer {

public var checkBox:CheckBox;

Trees with Dynamic Data Population CHAPTER 9

Page 394: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

390 RIA WITH ADOBE FLEX AND JAVA

override protected function createChildren():void

{

super.createChildren();

if (!checkBox) {

checkBox = new CheckBox();

checkBox.styleName = this;

addChild(checkBox);

}

}

override protected function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

if (checkBox) {

checkBox.x = (icon)?icon.x:label.x;

checkBox.setActualSize(checkBox.measuredWidth, checkBox.measuredHeight);

if (icon) {

icon.x = icon.x + checkBox.measuredWidth - 6;

}

label.x = label.x + checkBox.measuredWidth - 6;

}

}

} //CheckedTreeItemRenderer2

}

Listing 9.12 The second version of the CheckTreeItemRenderer

We want to put our checkbox in front of the icon. However, if the tree item chooses not to display the icon, we want the checkbox in front of the label. Let’s position the checkbox:

checkBox.x = (icon)?icon.x:label.x;

Then we’ll set the checkbox size to its measured size (every control has one):

checkBox.setActualSize(checkBox.measuredWidth, checkBox.measuredHeight);

Now let’s move the icon and the label to the right for the width of the checkbox:

if (icon) {

icon.x = icon.x + checkBox.measuredWidth ;

}

Trees with Dynamic Data Population CHAPTER 9

Page 395: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 391

label.x = label.x + checkBox.measuredWidth;

This second version of CheckedTreeItemRenderer produces the visual in Figure 9.5. The empty boxes have moved aside, but now there’s a gap between the checkbox and the icon:

Figure 9.5 The second version of the checkboxed tree with gaps

A minor correction adjusting the width will remove the gaps:

override protected function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

if (checkBox) {

checkBox.x = (icon)?icon.x:label.x;

checkBox.setActualSize(checkBox.measuredWidth,

checkBox.measuredHeight);

if (icon) {

icon.x = icon.x + checkBox.measuredWidth - 6;

}

label.x = label.x + checkBox.measuredWidth - 6;

}

}

This hard-coded number six doesn’t looks too good. But the Flex CheckBox class extends the class

Trees with Dynamic Data Population CHAPTER 9

Page 396: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

392 RIA WITH ADOBE FLEX AND JAVA

Button and when we wrote this chapter the class mx.controls.Button.as had the following:

package mx.controls

{

public class Button extends UIComponent

implements IDataRenderer, IDropInListItemRenderer,

IFocusable, IlistItemRenderer {

override protected function measure():void

{

super.measure();

// Pad with additional spacing, but only if we have a label.

if (label && label.length != 0)

w += extraSpacing;

else

w += 6; //This is our hardcoded value

measuredMinWidth = measuredWidth = w;

measuredMinHeight = measuredHeight = h;

}

}

}

We’re sure this will be fixed in the next version of Flex, so we are merely compensating for the w+=6.

The last missing detail is sizing. That’s done with the measure() method, which sets the compo-nent’s default size and, optionally, suggests the component’s minimum size. This method is called as a result of one or more invalidateSize() calls happening before the screen’s next refresh. In par-ticular, method addChild() calls invalidateSize() and so results in measure().

Our implementation of measure() is pretty straightforward; we’ll add the width of the checkbox to the total width of the rendered item:

override protected function measure():void

{

super.measure();

if (checkBox) {

measuredWidth = measuredWidth +

checkBox.measuredWidth -6;

}

}

Trees with Dynamic Data Population CHAPTER 9

Page 397: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 393

Figure 9.6 The second version of the checkboxed tree

The Data Binding of CheckboxesDo you want to see a kaleidoscope of checkboxes? Do this: check one checkbox then resize your screen so the tree has a vertical scrollbar and watch how during the scrolling the checked items pop up randomly. The reason is simple: we didn’t take care of the proper display of the data model val-ues, and we also forgot about changing the data model values when the user clicks on a checkbox.

Let’s take care of the user input first. We’ll register the listener to the “click” event immediately after the checkbox is added to the renderer:

override protected function createChildren():void {

super.createChildren();

if (!checkBox) {

checkBox = new CheckBox();

checkBox.styleName = this;

addChild(checkBox);

checkBox.addEventListener(MouseEvent.CLICK,

checkBoxClickHandler);

}

}

Trees with Dynamic Data Population CHAPTER 9

Page 398: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

394 RIA WITH ADOBE FLEX AND JAVA

The listener – checkBoxClickHandler() – has to reflect the changes to the checkbox’s selected prop-erty in the checked property of the appropriate dataProvider’s item. How do we get to the item? Good question. An instance of drop-in item renderers has a property listData. (A pair of getter and setter methods for listData property is mandated by the IDropInListItemRenderer interface.) In case of Tree item renderers, reference to the dataProvider’s item can be found after casting listData to TreeListData, as shown below:

private function checkBoxClickHandler(event:MouseEvent):void{

var tld:TreeListData = TreeListData(listData);

var item:Object = tld.item;

item.checked = checkBox.selected;

}

Beware: this technique works only as long as items of the tree’s dataProvider have property checked or are outright dynamic classes. Otherwise, an assignment to item.checked will cause a runtime exception.

With the user input handled, let’s look at the proper display of the item state. The natural place to handle it is in the setter of the listData. We’ll set the selected property of the checkBox to true when the underlying item.checked is true. In the remaining cases – item.checked is false and item.checked is undefined – we set checkBox’s selected to false:

public override function set listData(value:BaseListData):void {

var checked:Boolean = false;

if (value!=null) {

var item:Object = TreeListData(value).item;

if (item.checked == true) {

// can be undefined as well

checked = true;

}

if (checkbox) checkBox.selected = checked;

}

super.listData = value;

}

}

Here’s the complete listing of the custom renderer for the checkboxed tree:

package com.theriabook.controls {

import mx.controls.treeClasses.*;

import mx.controls.*;

import flash.events.*;

import mx.controls.listClasses.*

Trees with Dynamic Data Population CHAPTER 9

Page 399: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 395

public class CheckedTreeItemRenderer extends TreeItemRenderer {

public var checkBox:CheckBox;

override protected function createChildren():void

{

super.createChildren();

if (!checkBox) {

checkBox = new CheckBox();

checkBox.styleName = this;

addChild(checkBox);

checkBox.addEventListener(MouseEvent.CLICK, checkBoxClickHandler);

}

}

override protected function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

if (checkBox) {

checkBox.x = (icon)?icon.x:label.x;

checkBox.setActualSize(checkBox.measuredWidth,

checkBox.measuredHeight);

if (icon) {

icon.x = icon.x + checkBox.measuredWidth -6;

}

label.x = label.x + checkBox.measuredWidth -6;

}

}

override protected function measure():void

{

super.measure();

if (checkBox) {

measuredWidth = measuredWidth +

checkBox.measuredWidth -6;

}

}

private function checkBoxClickHandler(event:MouseEvent):void{

var tld:TreeListData = TreeListData(listData);

var item:Object = tld.item;

item.checked = checkBox.selected;

}

Trees with Dynamic Data Population CHAPTER 9

Page 400: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

396 RIA WITH ADOBE FLEX AND JAVA

public override function set listData(value:BaseListData):void {

var checked:Boolean = false;

if (value!=null) {

var item:Object = TreeListData(value).item;

if (item.checked == true) {

// the item can be true, false or undefined

checked = true;

}

if (checkBox) checkBox.selected = checked;

}

super.listData = value;

}

}

}

Listing 9.13 The final version of CheckedTreeItemRenderer

You can test our item renderer with the demo application CheckedTreeDemo presented earlier in Listing 9.10 and Figure 9.3. The kaleidoscope of checkboxes is gone; positioning, sizing, and check-box values are all taken care of.

SummaryIn this chapter we showed you how to asynchronously populate Tree controls from remote sources, where hierarchy of the data is not pre-built, but rather dynamic and, possibly, composite. The cus-tom Tree control that we built can be applied for a use case with any number of levels by chang-ing the server-side treeAssembler and DAO classes. We also demonstrated the complete process of extending a standard item renderer for the Flex Tree control. All in all, we showed how standard Flex framework controls can be enhanced to provide developers with simple yet powerful compo-nents.

Trees with Dynamic Data Population CHAPTER 9

Page 401: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 399

Working with Large Applications

10CHAPTER

Page 402: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

400 RIA WITH ADOBE FLEX AND JAVA

In this chapter we’ll cover how to set up large applications intended for Web or, more broadly speaking, distributed deployment. As an example let’s consider an enterprise application that consists of hun-dreds of screens, reports, forms, and dashboards. Accordingly, about a dozen engineers specializing in GUIs, frameworks, data layers, and business domains are working on this application in parallel.

Large monolithic applications, the way Flex builds application SWFs by default, have several prob-lems: they take a lot of time to build on each change, they do not support the isolation of two teams working on two independent “portlets”, they do not allow the incremental delivery of patches to the systems, and they do not support the independent delivery of libraries of reusable components without rebuilding the entire application.

We’ll show how to accommodate these requirements emphasizing the productivity of team devel-opment and deployment flexibility. But first let’s review the deployment scenarios from the busi-ness point-of-view in detail.

Deployment ScenariosThroughout this chapter we’ll use the term patches, which are the fixes made to an application be-tween releases. Add-ons are the parts of the application that are typically added over time. Paches as well as add-ons blend seamlessly into the hosting application, both visually and programmati-cally.

In some cases the application build may not even “know” about specific add-ons since they’re different for each user type. For example, an enterprise application can reveal more screens or more functionality within these screens to internal users. In this case we talk about portlet-style add-ons.

Plug-ins are independent applications that don’t share the look-and-feel of the main application. No intensive interaction between the main application and plug-ins is expected.

Working with Large Applications CHAPTER 10

Working with Large Applications

Page 403: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 401

Application Domains 101As much as we don’t like to duplicate the work done by the Adobe Flex documentation team, we have to cover the subject of Application Domains since it’s essential to this chapter. So, here are the facts.

The Flash Player loads class definitions from SWF files into the instances of the flash.system.Appli-cationDomain class – aka application domains. Application domains are organized in a hierarchy with system domain containing all application domains. If a class is already loaded into a parent domain, loading a different definition into the child domain will have no effect. If this sounds fa-miliar, you may have worked with Java ClassLoaders.

Don’t mistake application domains for security domains. The latter are relevant for issues like sub-loading an SWF file from a different server and are outside the scope of this chapter.

Let’s move on. There are three classes in the Flex library that facilitate SWF loading: flash.display.Loader, mx.controls.SWFLoader, and mx.modules.ModuleLoader. With each of them you get two choices of where you want to load the classes: a new (child) domain or an existing ap-plication domain. ModuleLoader has an applicationDomain property. If you set it to new ApplicationDomain(ApplicationDomain.currentDomain), new classes will be loaded into a child of the currentDomain (the domain where the main application runs); to enforce loading in the same do-main you should set applicationDomain’s value to ApplicationDomain.currentDomain. In each case with Loader and SWFLoader, you control the target domain by similarly assigning the object’s loaderContext.applicationDomain.

We have mentioned the overshadowing of children’s class definitions by the parent ones. Accord-ingly, when you bring existing Flex subsystems (perhaps even written in a different version of Flex) under a common application umbrella, it makes sense to resort to a separate application domain, i.e., a child of the system domain. On the other end of the specter, if you need to dynamically load resources like DataGrid definitions, you will be better off with the same domain where the main application is running.

Runtime Shared Libraries 101Flex documentation defines Runtime Shared Libraries (RSL) as “a library of components.” We would like to start with the clarification that RSL is not a file but a pattern of using an SWF file from within another SWF file.

Specifically, when you compile with RSLs, Flex generates code to preload the relevant SWFs during the application’s bootstrap. To be exact, definitions contained in the SWF are loaded directly into the applicationDomain of the hosting application.

Now how does the application’s bootstrap know which SWF files are to be pre-loaded?

Here is an answer. Let’s assume that:

Working with Large Applications CHAPTER 10

Page 404: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

402 RIA WITH ADOBE FLEX AND JAVA

a. You made the file FlexLibrary.SWC (running the compc compiler explicitly or just by rebuild-ing the Flex Builder’s Library Project).

b. You’ve created the file FlexApplication.mxml, which refers to components from FlexLibrary.SWC.

c. While compiling FlexApplication.mxml you instructed the mxmlc compiler that FlexLibrary.SWC contains an image of an SWF to be pre-loaded during the bootstrap (this will be explained later in this chapter).

Then, the corresponding ActionScript file generated by the mxmlc compiler will have the code fragment shown in Listing 10.1. You’ll find this and other files in the generated folder of your appli-cation project once you set the compiler’s option to keep-generated-actionscript=true:

public class _FlexApplication_mx_managers_SystemManager extends mx.managers.SystemManage

r implements IFlexModuleFactory {

public function _FlexApplication_mx_managers_SystemManager() {

super();

}

override public function info():Object {

return {

“currentDomain”: ApplicationDomain.currentDomain,

“layout” : “absolute”,

“mainClassName” : “FlexApplication”,

“mixins” : [“_FlexApplication_FlexInit”, ......]

,

“rsls” : [{url: “FlexLibrary.swf”, size: -1}]

};

}

}

}

Listing 10.1 The fragment of the generated SystemManager class for FlexApplication.mxml

Listing 10.1 contains the code of the SystemManager class of the FlexApplication. Flex SystemManager is the first display class created within an application and the parent of all other displayable objects. SystemManager also creates the mx.preloaders.Preloader that loads SWF files.

Please note that FlexLibrary.swf in and of itself is not an RSL. As we said above, RSL is a usage pat-tern rather than a file. What makes FlexLibrary.swf part of this pattern is the intent to pre-load it during the application startup communicated by us to the mxmlc compiler.

Also note the line:

“currentDomain”: ApplicationDomain.currentDomain,

Working with Large Applications CHAPTER 10

Page 405: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 403

This is why class definitions from the FlexLibrary.swf are loaded into the same domain where the definition of the application classes belong. Accordingly we find the RSL technique especially use-ful for delivering various patches, which should be loaded prior to any other class definitions. RSLs are also the best way to package component libraries and resources.

SWFs and SWCs: What’s Under the HoodHow do our SWC files relate to SWFs? An SWC is an archive file, much like ZIP or JAR, and contains library.swf and catalog.xml files. The latter describes the hierarchy of dependencies found the for-mer; library.swf may, but it won’t nessesarily become FlexLibrary.swf (depending on the selected Link Type described below).

Figure 10.1 The content of the FlexLibrary.SWC file

When we compile FlexApplication.mxml containing references to FlexLibrary.SWC in the library search path, there are three link types to choose from:

• External: The catalog.xml in the FlexLibrary.swc will be used to resolve references; however the definitions contained in library.swf won’t be included in the body of the FlexApplication.swf. The External link type assumes that by the time FlexApplication needs to create instances of classes from the library.swf part the definitions for these classes will be accessible from the currentDomain.

• RSL: The catalog.xml in the FlexLibrary.swc will be used to resolve references; the defini-tions contained in library.swf won’t be included in the body of the FlexApplication.swf. So far sounds like External, right? Here’s the difference: all definitions originally contained in the library.swf part will be upfront-loaded into the main applicationDomain during application startup.

• Merge-in: Definitions contained in library.swf get embedded directly into FlexApplication.swf, but wait ... only those that are explicitly referenced by the application code. This is a default option for statically linked applications and guarantees that the definitions of all

Working with Large Applications CHAPTER 10

Page 406: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

404 RIA WITH ADOBE FLEX AND JAVA

referenced classes as well as the classes they depend on are loaded into the main application-Domain.

A Merge-in scenario is often called static linking, while External and RSL are cases of dynamic link-ing.

Suppose we went with dynamic linking via RSL. As illustrated in Listing 10.1, this means pre-load-ing the FlexLibrary.swf. Here’s the question: where do we get this FlexLibrary.swf from? Under one scenario we can let Flex Builder extract and rename the library.swf from the FlexLibrary.swc. In Flex Builder (project Properties >Flex Build Path> Library Path) this option is called Auto extract. Alter-natively, we could have declined auto-extracting and unzipped the SWF from the SWC ourselves. As we’ll show later, there’s yet another way of explicitly controlling the upfront build of FlexLibrary.swf.

We’ll illustrate these cases in the next section.

Making the FlexLibrary.swcLet’s make an SWC in Flex Builder by creating a new Flex Library Project. The only component we’re going to add to this SWC is the CustomPanel from Listing 10.2. The Customer Panel enumerates the instances of itself and imprints the number of the instance as part of its title, using the variable instanceNumber that we’ve declared bindable:

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

<!-- CustomPanel.mxml -->

<mx:Panel xmlns:mx=”http://www.adobe.com/2006/mxml” title=”’Custom’ Panel

#{instanceNumber}” width=”300” height=”150” creationComplete=”instanceNumber=++count;”

>

<mx:Script>

public static var count:int;

[Bindable]

private var instanceNumber:int;

</mx:Script>

</mx:Panel>

Listing 10.2 Custom Panel.mxml

To ensure that our CustomPanel is accounted for (in both library.swf and catalog.xml) we have to verify that it’s included in the Flex Library Build Path. Please be aware that every time you add or rename files in your Library Project, the corresponding checkbox in Flex Builder gets cleared.

Working with Large Applications CHAPTER 10

Page 407: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 405

Figure 10.2 Checked state of the CustomPanel in FlexLibrary.swc

After we click OK, Flex Builder will invoke the compc compiler to create the FlexLibrary.swc in the output bin folder1.

Making a FlexApplication ApplicationNow let’s make the application in a separate Flex Builder project. Nothing fancy, we’ll just make a static reference to the CustomPanel:

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

<!-- FlexApplication.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” xmlns=”*”>

<CustomPanel />

</mx:Application>

Listing 10.3 FlexApplication.mxml

Then we’ll link our library (see Figure 10.4), compile, and run the application. The output window will look like this:

Working with Large Applications CHAPTER 10

Page 408: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

406 RIA WITH ADOBE FLEX AND JAVA

Figure 10.3 A Custom Panel

Figure 10.4 illustrates the details of adding our recently created library FlexLibrary.swc to the proj-ect’s Library Build Path. The default link type is to merge-in the SWC’s content (to change the link type, if needed, select the Link Type line and press the button Edit on the screen below):

Figure 10.4 Linking FlexLibary.swc to FlexApplication – “Merged into code”

Working with Large Applications CHAPTER 10

Page 409: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 407

Merging the contents of SWC results in an optimized, non-overlapping size of the monolithic ap-plication. As shown in Figure 10.5, the size of such a self-sufficient FlexApplication.swf is 123KB.

Figure 10.5 The FlexApplication deployment folder after merging-in FlexLibrary.swc

Now let’s change the Link type to RSL. We’ll accept the default value of Auto extract=true:

Figure 10.6 Linking FlexLibary.swc to FlexApplication – “RSL”

Working with Large Applications CHAPTER 10

Page 410: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

408 RIA WITH ADOBE FLEX AND JAVA

As a result of the Auto extract, the file FlexLibrary.swf will appear adjacent to the FlexApplication.swf. The size of the FlexLibrary.swf will be 236K, nearly as much as the entire FlexLibrary.swc, but the size of the FlexApplication.swf will decrease to 40K:

Figure 10.7 The file view of the FlexApplication deployment folder after auto-extracting the FlexLibrary.swf

Now you can see why the default Link Type in Flex is Merge into code. This ensures the smallest size of the resulting monolithic SWF that carries only the code that was deemed relevant during the compilation process.

Naturally, the total of 236K + 40K takes two times longer to download then downloading of the statically linked 123K. However, once you have a family of three applications to offer the same user, all approximately the same size and all reusing the same FlexLibrary.swf, it becomes (236 + 3*40) versus 3*123 and you break even.

The size/download considerations are less relevant on the fast connections and, perhaps, are not relevant at all in scenarios where you can count on the browser cache to keep SWF files loaded for the next run of the application. A typical example where the time of the initial download is not an issue would be a corporate environment, but then again, some administrators set policies to wipe out the browser’s cache on user logoff.

Static versus Dynamic Linking: Development Perspective Let’s change the subject from download time to a developer’s productivity, a factor far more impor-tant for enterprise applications.

Let’s look at enterprise applications. Driven by user requirements, these applcations tend to change

Working with Large Applications CHAPTER 10

Page 411: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 409

frequently, growing in functionality and, accordingly, size, with phased delivery to the users. As an ap-plication gets to tens of megabytes, the time required to build the monolithic SWF becomes a notice-able setback to developer productivity. The problem gets magnified in a team development where it translates into many man-hours wasted every day.

And, regardless of size, let’s look at the use case of portlet-style add-ons. These are modules that are simply impossible to reference statically in advance. In fact, they are not even supposed to be pre-loaded at all: unlike RSLs they get loaded on demand (more on that later in the chapter).

All in all, we need to be able break the application into a set of modules that can be built indepen-dently and linked dynamically at runtime.

So, You Say Dynamic Linking?By now the reader may say, “OK, I got the message, I’ll go with RSLs to modularize my development with dynamic linking.” Not so fast. Like ancient Achilles, these mighty RSLs have a small weak spot: their well-being depends on static linkage from the main application.

Oh, but doesn’t that ruin the hope of dynamic linking? The answer is no, and the explanation is just around the corner in the next section.

First, however, let’s create an application to expose the problem. We’ll build a FlexApplication2 appli-cation. Unlike our previous example, it won’t contain static references to CustomPanel. Instead, Flex-Application2 will create instances of a CustomPanel (or any other object for that matter) dynamically, given a name of the class definition as it’s done in the function createComponent() of Listing 10.4:

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

<!-- FlexApplication2-->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” xmlns=”*”>

<!--CustomPanel /-->

<mx:Button label=”CreatePanel” click=”createComponent(‘CustomPanel’)”/>

<mx:Script>

<![CDATA[

private var displayObject:DisplayObject;

private function createComponent(componentName:String) : void {

var clazz : Class = getDefinitionByName(componentName) as Class;

displayObject = DisplayObject(new clazz() );

this.addChild(displayObject);

}

]]>

</mx:Script>

</mx:Application>

Listing 10.4 FlexApplication2.mxml

Working with Large Applications CHAPTER 10

Page 412: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

410 RIA WITH ADOBE FLEX AND JAVA

If you run the application and click the “Create Panel” button, the code terminates abnormally as shown in Figure 10.8.

Figure 10.8 An error related to a lack of initializing the library

The reason for this error is that many times mxmlc complements classes with additional initial-ization code at the SystemManager level. But if the relevant classes are completely shielded from mxmlc it’s absolved from taking care of them. Let’s explain this in detail. Please have another look at the generated SystemManager corresponding to FlexApplication from Listing 10.3.

package {

import mx.managers.SystemManager;

import flash.utils.*;

import flash.system.ApplicationDomain;

import mx.core.IFlexModuleFactory;

public class _FlexApplication_mx_managers_SystemManager extends mx.managers.SystemManage

r implements IFlexModuleFactory {

public function _FlexApplication_mx_managers_SystemManager() {

super();

}

override public function info():Object {

return {

“currentDomain”: ApplicationDomain.currentDomain,

“layout” : “vertical”,

“mainClassName” : “FlexApplication”,

“mixins” : [“_FlexApplication_FlexInit”,

“_activeTabStyleStyle”, …

“_ControlBarStyle”, “_PanelStyle”, “_CustomPanelWatcherSetupUtil”

]

,

Working with Large Applications CHAPTER 10

Page 413: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 411

“rsls” : [{url: “FlexLibrary.swf”, size: -1}]

};

}

} //_FlexApplication_mx_managers_SystemManager

}

Listing 10.5 The Generated SystemManager class for FlexApplication.mxml

If we compare this SystemManager with the one generated for our latest example – FlexApplica-tion2 – we’ll see that in the latter case the mixins array is short of the three values: “_ControlBar-Style,” “_PanelStyle,” and “_CustomPanelWatcherSetupUtil.”

The classes referenced in the mixins array take part in the initialization sequence of the application upon the initial load. In particular, CustomPanelWatcherSetupUtil is the class that facilitates the binding for the variable instanceNumber of CustomPanel. In the case of FlexApplication2, this part of the initialization “gets forgotten.”

SystemManager files _<ApplicationName>_mx_managers_System.Manager.as are not the only ones affected. The next interesting set of files to look after are <ApplicationName>_FlexInit.as. For example, in Chapter 8 we used to annotate the EmployeeDTO class with the metadata keyword RemoteClass:

[RemoteClass(alias=”com.theriabook.composition.dto.EmployeeDTO”)]

Below is the relevant part from a generated <ApplicationName>FlexInit class:

package {

[Mixin]

public class _SimpleAutoCompleteWithDynamicDataDemo_FlexInit

{

. . . .

public static function init(fbs:IFlexModuleFactory):void

{

. . . .

flash.net.registerClassAlias(

“com.theriabook.composition.dto.EmployeeDTO”,

com.theriabook.composition.dto.EmployeeDTO

);

. . . .

}

} // FlexInit

} // package

And again, the registration snippet

Working with Large Applications CHAPTER 10

Page 414: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

412 RIA WITH ADOBE FLEX AND JAVA

flash.net.registerClassAlias(

“com.theriabook.composition.dto.EmployeeDTO”,

com.theriabook.composition.dto.EmployeeDTO

);

Now imagine the scenario in which your RSL defines EmployeeDTO and the classes in RSL antici-pate using it, but the application does not explicitly mention EmployeeDTO anywhere. What hap-pens? The above initialization code does not get generated at all.

All in all, MXML files as well as metadata-annotated ActionScript classes might not get the expect-ed initialization support if you put them in RSL and don’t explicitly reference them in the calling application.

Self-Initializing Libraries – Applications Enough of the problems, let’s get back to positive thinking. Here’s a short description of the solu-tion:

We will politely decline a suggestion to “AutoExtract” SWF from SWC. We will be making the right SWF ourselves via an Ant build file that controls how we build the SWF. Namely, we will build it as an application to guarantee that, effectively, our library will initialize itself. As far as SWC goes, we will restrict its role to merely supporting the name resolution during the compilation of the main application.

To that end, we’ll add the following FlexLibraryMain.xml application and FlexLibraryMain.as class (Listings 10.6 and 10.7, respectively):

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

<!-- FlexLibrary.mxml -->

<FlexLibraryMain xmlns=”*”/>

Listing 10.6 FlexLibrary.mxml

// FlexLibraryMain.as

package {

import mx.core.SimpleApplication;

public class FlexLibraryMain extends SimpleApplication {

public function FlexLibraryMain() {

// Custom library initialization code should go here

trace(“FlexLibrary.swf has been loaded and initialized”);

}

// Enforce merging-on of required classes by static references here

private var linkage:Object = {

r1:CustomPanel

Working with Large Applications CHAPTER 10

Page 415: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 413

};

}//FlexLibraryMain

}

Listing 10.7 FlexLibraryMain.as class

Inheriting your FlexLibraryMain from mx.core.SimpleApplication ensures that the compiler will initialize all the classes referenced by your application, while wrapping it as FlexLibrary.mxml will support the CSS-style-related portion of the initialization.

We’ve said that we wanted to build the FlexLibrary.swf ourselves. Since we started with Flex Build-er’s Library Project, the default outcome of the project is only an SWC file. However, nothing stops us from creating the ANT file that will be calling the mxmlc compiler against our FlexLibrary.mxml “application.”2

Before we present the listing of the Ant build.xml file let’s ask ourselves several questions.

There could be references to the Flex framework classes from the FlexLibrary project. Do we want to merge-in the contents of the framework.swc, utilities.swc, etc., to the FlexLibrary.swf? If we want to keep our library tiny, the answer is “no,” assuming of course that the required Flex framework classes will somehow be present in the target application domain. That’s why in our build.xml we’ll extern the framework swc files with lines like the following:

<arg line=”-external-library-path=’${swclibs}/framework.swc’”/>

The next question is where is the deployment destination of FlexLibrary.swf? It should go into the same folder that the FlexApplication expects it to be. In our scenario, that means:

c:/theriabook/code/applications/FlexApplication/bin/FlexLibrary.swf

Ultimately we arrive at the following build.xml file:

<project name=”Lib-App” default=”compileMXML” basedir=”c:/theriabook/code/applications/

FlexLibrary” >

<target name=”compileMXML”>

<property name=”sdkdir” value=”C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex

SDK 2” />

<property name=”swclibs” value=”${sdkdir}/frameworks/libs” />

<property name=”fileName” value=”FlexLibrary.mxml” />

<exec executable=”${sdkdir}/bin/mxmlc.exe” dir=”${basedir}”>

<arg line=”-external-library-path=’${swclibs}/playerglobal.swc’”/>

<arg line=”-external-library-path=’${swclibs}/utilities.swc’”/>

<arg line=”-external-library-path=’${swclibs}/framework.swc’”/>

<arg line=”${fileName}”/>

Working with Large Applications CHAPTER 10

Page 416: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

414 RIA WITH ADOBE FLEX AND JAVA

<arg line=”-output

c:/theriabook/code/applications/FlexApplication/bin/FlexLibrary.swf”/>

</exec>

</target>

</project>

Listing 10.8 The Ant build file to create a self-sufficient FlexLibrary.swf

Please note that the value of the sdkdir property will be different if you use FlexBuilder instead of Flex Builder Plugin:

<property name=”sdkdir” value=”C:/Program Files/Adobe/Flex Builder 2/Flex SDK 2”

/>

When you “Run As Ant” the build.xml above will generate the customized FlexLibrary.swf that will replace the one previously auto-extracted during the build of the FlexApplication. Now we need to prevent future unwanted auto-extracts. To that end we need to revisit Flex Build Path and turn off the “Auto Extract swf” option for FlexLibrary.swc.

But wait, who’s going to ensure that everything from framework.swc, utilities.swc, etc., is going to await our FlexLibrary.swf in the runtime? Here’s the issue: the default setting for framework.swc, utilities.swc, flex.swc, and rpc.swc (although the later isn’t relevant to our example) was to “merge-in” their contents. This was meant to pick only statically referenced classes. When we denied the FlexApplication rights to auto-extract FlexLibrary.swf we also absolved it from responsibility for accounting for all the classes that FlexLibrary.swf might need from these “merge-ins” (remember catalog.xml ?).

Eureka! We’ll turn framework.swc, utilities.swc, and flex.swc to “auto-extract” RSLs. All the above Flex Application settings are presented on Figure 10.9:

Working with Large Applications CHAPTER 10

Page 417: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 415

Figure 10.9 The FlexApplication Build path extracts framework libraries and doesn’t extract FlexLibrary.swf

After we do a clean build (see Flex Builder’s Project menu) and run our application, it functions properly, as shown in Figure 10.10:

Figure 10.10 FlexApplication2 after two clicks on “Create Panel”

Working with Large Applications CHAPTER 10

Page 418: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

416 RIA WITH ADOBE FLEX AND JAVA

Figure 10.11 shows the deployment directory of the FlexApplication project at this point. You can see a major increase in the size of the initial download because of the 1Mb of framework.swf. This is the price paid for the utmost flexibility in the delivery of the modules and development produc-tivity.

Figure 10.11 Deployment directory of the FlexApplication project again

Recap of the TechniqueLet us go back to Chapter 8 and review the final sections where we introduced the idea of separat-ing the resources from the components. The next level of logical progression is to keep classes, which serve as resources in the resourse-only-libraries, separate from the components’ and appli-cation’s SWFs.

To illustrate this, we’ll create the application project ComboBoxCode that will contain everything from the original project ComboBox except the sub-folders com/theriabook/resources and com/the-riabook/composition/dto. We’ll also create the library project Resources containing precisely these sub-folders as shown in Figure 10.12.

Working with Large Applications CHAPTER 10

Page 419: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 417

Figure 10.12 Resources project.

Please note that we show the state of the Resources project after three extra files Resources.mxml, ResourcesMain.as, and build.xml have been added to the project. Let’s go over them one by one.

Resources.mxml is a clone of FlexLibrary.mxml from Listing 10.6 except that it refers to Resources-Main.as:

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

<!-- ResourcesMain.mxml -->

<ResourcesMain xmlns=”*”/>

Listing 10.9 ResourcesMain.mxml.

The file ResourcesMain.as enforces the static linkage of the classes EmployeeComboBoxResources and EmployeeDTO and registers the alias of the EmployeeDTO class:

// ResourcesMain.as

package {

import mx.core.SimpleApplication;

import com.theriabook.composition.dto.EmployeeDTO;

import com.theriabook.resources.EmployeeComboBoxResource;

import flash.net.registerClassAlias;

public class ResourcesMain extends SimpleApplication {

public function ResourcesMain() {

Working with Large Applications CHAPTER 10

Page 420: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

418 RIA WITH ADOBE FLEX AND JAVA

// Custom library initialization code should go here

registerClassAlias(

“com.theriabook.composition.dto.EmployeeDTO”,

EmployeeDTO

);

trace(“Resources.swf has been loaded and initialized”);

}

// Static linking of the required classes should go here

private var linkage:Object = {

t1:EmployeeComboBoxResource

};

}//ResourcesMain

Listing 10.10 ResourcesMain.as

The third file – build.xml – facilitates building Resources.swf with Ant and mxmlc. Please note that unlike the example in Listing 10.8, where we referenced the Flex SDK SWC files, here we point to WEB-INF/flex/libs folder. The reason is that we need fds.swc, which isn’t a part of the Flex SDK. Also note that we direct the Resources.swf output file straight into the deployment folder of the Combo-BoxCode application:

<project name=”Flex 2 Build File” default=”compileMXML” basedir=”c:/theriabook/code/ap-

plications/Resources” >

<target name=”compileMXML”>

<property name=”sdkdir” value=”C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex

SDK 2” />

<property name=”WEB-INF” value=”C:/fds/jrun4/servers/default/theriabook/WEB-INF”/>

<property name=”swclibs” value=”${WEB-INF}/flex/libs” />

<property name=”fileName” value=”Resources.mxml” />

<exec executable=”${sdkdir}/bin/mxmlc.exe” dir=”${basedir}”>

<arg line=”-external-library-path=’${swclibs}/playerglobal.swc’”/>

<arg line=”-external-library-path=’${swclibs}/utilities.swc’”/>

<arg line=”-external-library-path=’${swclibs}/framework.swc’”/>

<arg line=”-external-library-path=’${swclibs}/rpc.swc’”/>

<arg line=”-external-library-path=’${swclibs}/fds.swc’”/>

<arg line=”-keep-generated-actionscript=true “/>

<arg line=”${fileName}”/>

<arg line=”-output

C:/fds/jrun4/servers/default/theriabook/ComboBoxCode/Resources.swf”/>

</exec>

</target>

</project>

Listing 10.11 The Ant build.xml file to build Resources.swf library

Working with Large Applications CHAPTER 10

Page 421: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 419

When we run this build.xml as an Ant task, it produces a Resources.swf file of 20,851 bytes.

At the same time, let’s not forget the original purpose of Resources.swc – it should help in resolving all references during the application’s build. That’s why we check off all the classes the application might need as shown in Figure 10.13. The output folder for the Resources.swc will be WEB-INF\flex\user_classes:

Figure 10.13 Classes for .flexLibProperties for the Resources project

These checkboxes correspond to the following contents of the.flexLibProperties file:

<?xml version=”1.0” encoding=”UTF-8”?>

<flexLibProperties version=”1”>

<includeClasses>

<classEntry path=”com.theriabook.composition.dto.EmployeeDTO”/>

<classEntry path=”com.theriabook.resources.ComboBoxResource”/>

<classEntry path=”com.theriabook.resources.EmployeeComboBoxResource”/>

</includeClasses>

<includeResources/>

<namespaceManifests/>

Working with Large Applications CHAPTER 10

Page 422: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

420 RIA WITH ADOBE FLEX AND JAVA

</flexLibProperties>

Listing 10.12 The flexLibPropertiesAnt file of the Resource project

As you can see, out of the three files listed in (DOT) directly in front of flexLibProperties, our Re-sources.as, shown in Listing 10.10, explicitly references EmployeeComboBoxResource and Employ-eeDTO. We did not have to register the base class – ComboBoxResource – as this is the compiler’s job.

Now let’s take care of the application settings. For the ComboBoxCode project we’ll set the Link Type of all the libraries except Resources.swc to RSL with Auto extract, while Resources.swc will be set as RSL without Auto extract as in Figure 10.14.

Figure 10.14 The library path for the ComboBoxCode project

Finally, we’ll comment out of the linkage variable in the application, since the pre-load of Resourc-es.swf that we have built as a self-initializing library will automatically load the EmployeeCombo-BoxResource class in the currentDomain.

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

<!-- ResourceComboBoxDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns=”com.theriabook.controls.*” >

<mx:Script>

// import com.theriabook.resources.*;

Working with Large Applications CHAPTER 10

Page 423: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 421

// private var linkage:EmployeeComboBoxResource;

</mx:Script>

<ResourceComboBox id=”cbx_1”

width=”150”

resource=”com.theriabook.resources.EmployeeComboBoxResource”

/>

</mx:Application>

Listing 10.13 The code for the ResourceComboBoxDemo from the ComboBoxCode project

The application is ready to run and after a slight delay it will display a familiar picture:

Figure 10.15 The ResourceComboBoxDemo from the ComboBoxCode project

RSL versus Custom Loading of the Dynamic CodeUp till now, we’ve been building self-initializing libraries and pre-loading them during the applica-tion bootstrap in the RSL style. But there are different use cases, such as add-on modules that have to be loaded on demand. These use cases could be related to a portlet-style personalization of the applications and, in general, such add-ons may not even exist at the time of the application build. An extra benefit of explicit on-demand loading (applicable even if you know all your libraries in advance) is that it reduces the initial load time and memory footprint of the application.

Unlike RSLs, on-demand loading leaves open the choice of an application domain to load to. Ar-guably, complete independent subsystems should be loaded into separate domains. On the other hand, when we load a small “flexlet” that contains some extra business function to augment the existing application, we load it into the main application domain to allow seamless interoperability between the new and old classes.

Working with Large Applications CHAPTER 10

Page 424: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

422 RIA WITH ADOBE FLEX AND JAVA

That said, everything we ‘ve done so far to guarantee the self-sufficiency of the libraries is equally rele-vant to all of them irrespective of the exact loading method: on demand or pre-loaded as in RSL case.

The Custom Loading ExampleTo illustrate custom loading, we’ll create another application – FlexApplication3 – that will let us load a DataGrid definition from the FlexLibrary and show this DataGrid with the help of two but-tons “1.Load Library” and “2.Show Library Grid” as shown in Figure 10.16.

Figure 10.16 FlexApplication3 with DataGrid loaded on demand.

If you click on the “2. Show Library Grid” button, the application will assign an array of data to the DataGrid’s dataProvider as it would to any ordinary loaded DataGrid:

dg.dataProvider = [

{name:”Anatole Tartakovsky”, phone:”5618325611”},

{name:”Victor Rasputnis”, phone:”7184017234”},

{name:”Yakov Fain”,phone:”7322342654”}

];

Meanwhile the DataGrid won’t contain any data, being a pure “resource” file, as shown in Listing 10.14:

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

<!-- AuthorsGrid.mxml -->

Working with Large Applications CHAPTER 10

Page 425: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 423

<mx:DataGrid xmlns:mx=”http://www.adobe.com/2006/mxml”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”name” headerText=”Name” width=”150”/>

<mx:DataGridColumn dataField=”phone” headerText=”Phone”/>

</mx:Array>

</mx:columns>

</mx:DataGrid>

Listing 10.14 AuthorsGrid.mxml from FlexLibrary project

We’ll begin working on our application by adding the AuthorsGrid to the FlexLibrary project and registering it with the FlexLibraryMain.as class:

// FlexLibraryMain.as

package {

import mx.core.SimpleApplication;

public class FlexLibraryMain extends SimpleApplication {

public function FlexLibraryMain() {

// Custom library initialization code should go here

trace(“FlexLibrary.swf has been loaded and initialized”);

}

// Static linking of the required classes should go here

private var linkage:Object = {

t1:CustomPanel,

t2:AuthorsGrid

};

}//FlexLibraryMain

}

}

Listing 10.15 The Resources.as file with the static linkage of AuthorsGrid

At this point we can rerun the Ant’s build.xml file, which will place the updated FlexLibrary.swf in the same folder that we expect our FlexApplication3 to run from.

Let’s “prepare” this application by removing the FlexLibrary.swc from the FlexApplication project’s Build Path. Up till now we’ve been pre-loading the FlexLibrary.swf the RSL way, which we don’t want it any more. Accordingly, we will comment out the static reference to CustomPanel inside the FlexApplication.mxml, otherwise the project’s build will fail:

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

<!-- FlexApplication.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”

Working with Large Applications CHAPTER 10

Page 426: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

424 RIA WITH ADOBE FLEX AND JAVA

xmlns=”*”>

<!-- Comment out to illustrate on demand loading with FlexApplication3-->

<!--CustomPanel /-->

</mx:Application>

Listing 10.16 FlexApplication.mxml with a commented-out CustomPanel

Now we’ll code the component that facilitates the loading process – LibraryLoader; a partial listing of the component is shown in Listing 10.17. It can be further developed to provide the progress and error handling. We’ll inherit this component from the flash.display.Sprite class and make it contain a child instance of the flash.display.Loader:

public class LibraryLoader extends Sprite {

private var loader:Loader = null;

public function LibraryLoader() {

loader = new Loader();

addChild(loader);

. . . .

}

public function load(url:String):void {

var request:URLRequest = new URLRequest(url);

loader.load(request, …);

}

}

Listing 10.17 A partial listing of LibraryLoader.as

Since we prefer seamless programmatic access to classes of dynamically loaded resources, we’ll put the loaded classes in the loading application’s domain:

loaderContext = new LoaderContext();

loaderContext.applicationDomain = ApplicationDomain.currentDomain;

We’ll also listen to all relevant events on the loader.contentLoaderInfo object:

dispatcher.addEventListener(Event.COMPLETE, onEvent);

dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, onEvent);

dispatcher.addEventListener(Event.INIT, onEvent);

dispatcher.addEventListener(IOErrorEvent.IO_ERROR, onEvent);

dispatcher.addEventListener(Event.OPEN, onEvent);

dispatcher.addEventListener(ProgressEvent.PROGRESS, onEvent);

dispatcher.addEventListener(Event.UNLOAD, onEvent);

Working with Large Applications CHAPTER 10

Page 427: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 425

The full listing of LibraryLoader is presented below:

// LibraryLoader.as

package com.theriabook.util

{

import flash.display.Sprite;

import flash.display.Loader;

import flash.net.URLRequest;

import flash.events.*;

import flash.system.LoaderContext;

import flash.system.ApplicationDomain;

public class LibraryLoader extends Sprite {

private var loader:Loader = null;

private var loaderContext:LoaderContext = null;

public function LibraryLoader() {

loader = new Loader();

addChild(loader);

configureListeners(loader.contentLoaderInfo);

loaderContext = new LoaderContext();

loaderContext.applicationDomain = ApplicationDomain.currentDomain;

}

public function load(url:String):void {

var request:URLRequest = new URLRequest(url);

loader.load(request, loaderContext);

}

private function configureListeners(dispatcher:IEventDispatcher):void {

dispatcher.addEventListener(Event.COMPLETE, onEvent);

dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, onEvent);

dispatcher.addEventListener(Event.INIT, onEvent);

dispatcher.addEventListener(IOErrorEvent.IO_ERROR, onEvent);

dispatcher.addEventListener(Event.OPEN, onEvent);

dispatcher.addEventListener(ProgressEvent.PROGRESS, onEvent);

dispatcher.addEventListener(Event.UNLOAD, onEvent);

}

private function onEvent(event:Event):void {

trace(event.type + event);

}

}

}

Listing 10.18 LibraryLoader.as

Working with Large Applications CHAPTER 10

Page 428: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

426 RIA WITH ADOBE FLEX AND JAVA

We’re ready to write the application FlexApplication3.mxml. To illustrate that fonts bound to the application’s SystemManager are proliferated to the loaded classes, we’ll purposely throw in a non-standard style definition for the DataGrid fonts (please notice the larger than usual characters in Figure 10.16):

<mx:Style>

DataGrid {

fontFamily: Arial; fontSize: 14; headerStyleName:”dgHeader”;

}

.dgHeader {

fontFamily: Arial; fontSize: 14pt; fontWeight:bold;

}

</mx:Style>

We’ll prepare two functions, individually invoked from the buttons “1. Load Library” and “2. Show Library Grid.” The showGrid() function instantiates the DataGrid freshly loaded by loadLibrary(), given the name AuthorsGrid:

private function loadLibrary() :void {

var loader:LibraryLoader = new LibraryLoader();

loader.load(“FlexLibrary.swf”);

}

private function showGrid():void {

var clazz:Class = Class(getDefinitionByName(“AuthorsGrid”));

var dg:DataGrid = DataGrid(new clazz());

dg.dataProvider = [

{name:”Anatole Tartakovsky”, phone:”5618325611”},

{name:”Victor Rasputnis”, phone:”7184017234”},

{name:”Yakov Fain”,phone:”7322342654”}

];

addChild(dg);

}

The full listing of FlexApplication3.mxml is presented in Listing 10.19:

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

<!-- FlexApplication3.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”vertical”>

<mx:Style>

DataGrid {

fontFamily: Arial; fontSize: 14; headerStyleName:”dgHeader”;

}

.dgHeader {

Working with Large Applications CHAPTER 10

Page 429: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 427

fontFamily: Arial; fontSize: 14; fontWeight:bold;

}

</mx:Style>

<mx:Button label=”1. Load Library” click=”loadLibrary()”/>

<mx:Button label=”2. Show Library Grid” click=”showGrid()”/>

<mx:Script>

<![CDATA[

import mx.controls.DataGrid;

import com.theriabook.util.LibraryLoader;

private function loadLibrary() :void {

var loader:LibraryLoader = new LibraryLoader();

loader.load(“FlexLibrary.swf”);

}

private function showGrid():void {

var clazz:Class = Class(getDefinitionByName(“AuthorsGrid”));

var dg:DataGrid = DataGrid(new clazz());

dg.dataProvider = [

{name:”Anatole Tartakovsky”, phone:”5618325611”},

{name:”Victor Rasputnis”, phone:”7184017234”},

{name:”Yakov Fain”,phone:”7322342654”}

];

addChild(dg);

}

]]>

</mx:Script>

</mx:Application>

Listing 10.19 FlexApplication3.mxml

Let’s run the application in the debug mode to see the debugger’s trace messages. This is what you’ll see in the console window when the application starts:

[SWF] C:\TheRIABook\code\applications\FlexApplication\bin\utilities.swf - 1,827 bytes

after decompression

[SWF] C:\TheRIABook\code\applications\FlexApplication\bin\flex.swf - 49,833 bytes after

decompression

[SWF] C:\TheRIABook\code\applications\FlexApplication\bin\FlexApplication3-debug.swf -

104,664 bytes after decompression

[SWF] C:\TheRIABook\code\applications\FlexApplication\bin\framework.swf - 2,235,667

bytes after decompression

Listing 10.20 Console window messages when FlexApplication3 starts

Working with Large Applications CHAPTER 10

Page 430: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

428 RIA WITH ADOBE FLEX AND JAVA

These messages reflect the fact that all of the above libraries are marked as RSLs with “Auto extract.” Now let’s press the “1. Load Library” button. The following output will go to the console:

open[Event type=”open” bubbles=false cancelable=false eventPhase=2]

progress[ProgressEvent type=”progress” bytesLoaded=0 bytesTotal=16440]

progress[ProgressEvent type=”progress” bytesLoaded=8192 bytesTotal=16440]

progress[ProgressEvent type=”progress” bytesLoaded=16384 bytesTotal=16440]

progress[ProgressEvent type=”progress” bytesLoaded=16440 bytesTotal=16440]

[SWF] C:\TheRIABook\code\applications\FlexApplication\bin\FlexLibrary.swf - 36,126 bytes

after decompression

init[Event type=”init” bubbles=false cancelable=false eventPhase=2]

httpStatus[HTTPStatusEvent type=”httpStatus” bubbles=false cancelable=false eventPhase=2

status=0]

complete[Event type=”complete” bubbles=false cancelable=false eventPhase=2]

Module file://C:\TheRIABook\code\applications\FlexApplication\bin\FlexLibrary.swf com-

plete.

FlexLibrary.swf has been loaded and initialized

Listing 10.21 Console messages in response to library loading

At this point we can press the button “2. Show Library Grid.” The grid with the data will appear exactly as shown in Figure 10.16.

Congrats! We’ve just loaded a visual dynamic object AuthorsGrid that can access other objects and be accessed the same way as any other class. In particular, AuthorsGrid adheres to global styles while its properties and methods are directly accessible from the application class.

Embedded Applications and the SWFLoader ObjectIn this section we’ll discuss plug-ins. Fortunately, Flex has a perfect out-of-the-box solution for this type of integration via the SWFLoader control, which lets you embed any SWF file that represents Flex 2.0 application or just any Flash movie. Let’s embed one application, InnerApplication.swf, into another application, OuterApplication.swf.

Here is the listing of InnerApplication.mxml, which shows the Panel with two label controls on a light pink background:

<?xml version=”1.0”?>

<!-- InnerApplication.mxml-->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” backgroundColor=”0xffeeff”>

<mx:Script>

[Bindable]

public var value:String = “From InnerApp with love!”;

</mx:Script>

Working with Large Applications CHAPTER 10

Page 431: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 429

<mx:Panel title=”This Panel is a part of InnerApplication” >

<mx:Label id=”label_1” text=”This label contains static text” />

<mx:Label text=”This label is bound to ‘value’, currently - {value}”/>

</mx:Panel>

</mx:Application>

Listing 10.22 InnerApplication.mxml

Before compiling InnerApplication.mxml into InnerApplication.swf, let’s figure out the Link Type for the Flex framework libraries. If we plan to run the InnerApplication as standalone and offer it to the broad unprepared clientele over the Internet, we need to keep the size minimal, so we would choose to merge the framework libraries. However, this is not our case.

If the purpose of the InnerApplication is to be host to a suite of other applications or portlets, we would choose Auto extract. This is not our case either.

Specifics of our case is that the InnerApplication is being brought to life by the OuterApplication, which has already taken care of the framework libraries. That is why we will mark all the framework libraries as external.

But, if we’re going to keep the OuterApplication.mxml and InnerApplication.mxml in the same Flex Builder project, they will compete for a single set of Flex Build Path settings. Settings for OuterAp-plication are not supposed to extern framework libraries, rather they should AutoExtract them. To avoid the problem and allow both InnerApplication and OuterApplication to stay in one project, we will leave the Library path to the OuterApplication and take care of the InnerApplication with the additional InnerApplication-config.xml file to the project, as in Listing 10.22. Both Flex Builder and a command-line mxmlc compiler try to read an <ApplicationName>-config.xml file (aka, local configuration guide) before using the project properties (you can read more on the precedence of files and command-line parameters in the Adobe Flex manual in the section “Using Command-line Com-pilers”). InnerApplication-config.xml, shown below, exclusively externs all the framework libraries for the InnerApplication:

<flex-config>

<!--InnerApplication-config.xml-->

<compiler>

<external-library-path>

<path-element>C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex SDK 2/frame-

works/libs/playerglobal.swc</path-element>

<path-element>C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex SDK 2/frame-

works/libs/framework.swc</path-element>

<path-element>C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex SDK 2/frame-

works/libs/utilities.swc</path-element>

<path-element>C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex SDK 2/frame-

works/libs/flex.swc</path-element>

Working with Large Applications CHAPTER 10

Page 432: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

430 RIA WITH ADOBE FLEX AND JAVA

</external-library-path>

</compiler>

</flex-config>

Listing 10.23 InnerApplication-config.xml

Let’s mark the InnerApplication as the default one and build the project. This will build an InnerAp-plication.swf.

Now let’s focus on the OuterApplication. First and foremost, it will to have an instance of SWFLoad-er, which loads the InnerApplication.swf:

<mx:SWFLoader id=”swfLoader” source=”InnerApplication.swf” />

Next, it will have a label, whose value gets replaced by the value obtained from the label_1 of the InnerApplication when the “Read inner label” button is clicked:

<mx:Label id=”label_1” text=”This ‘outer’ label contains static text” />

<mx:Button label=”Read Inner Label” click=”readInnerLabel();”/>

It will also contain a text field, where we’ll type some text, and two buttons to pass this text directly into the InnerApplication’s label_1 and the corresponding value variable: <mx:Button label=”Modify Inner Label” click=”modifyInnerLabel();”/>

<mx:Button label=”Change Inner Variable” click=”changeInnerVariable();”/>

<mx:TextInput id=”new_value” />

The default setting of the loaderContext of the SWFLoader is to load new definitions into the child domain. Accordingly, to access the label_1 inside the InnerApplication we may do something like this: var systemManager:SystemManager = SystemManager(swfLoader.content);

var innerApplication:Application = Application(systemManager.application);

trace(innerApplication[“label_1”].text);

(Flex provides several other ways Outer- and Inner-applications can interoperate, such as Share-dObjects, LocalConnection, and ExternalInterface. We discuss the last two in Chapter 15.)

The full listing of the OuterApplication.mxml is presented below:

<?xml version=”1.0”?>

<!-- OuterApplication.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”>

<mx:Script>

Working with Large Applications CHAPTER 10

Page 433: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 431

<![CDATA[

import mx.managers.SystemManager;

public var innerApplication:Application = null;

private function getInnerApplication():Application {

var systemManager:SystemManager = SystemManager(swfLoader.content);

return Application(systemManager.application);

}

public function readInnerLabel():void {

if (!innerApplication) innerApplication = getInnerApplication();

label_1.text=innerApplication[“label_1”].text;

}

public function modifyInnerLabel():void {

if (!innerApplication) innerApplication = getInnerApplication();

innerApplication[“label_1”].text = new_value.text;

}

public function changeInnerVariable():void {

if (!innerApplication) innerApplication = getInnerApplication();

innerApplication[“value”] = new_value.text;

}

]]>

</mx:Script>

<mx:Label id=”label_1” text=”This ‘outer’ label contains static text” />

<mx:SWFLoader id=”swfLoader” source=”InnerApplication.swf” />

<mx:Button label=”Read Inner Label” click=”readInnerLabel();”/>

<mx:Button label=”Modify Inner Label” click=”modifyInnerLabel();”/>

<mx:Button label=”Change Inner Variable” click=”changeInnerVariable();”/>

<mx:TextInput id=”new_value” />

</mx:Application>

Listing 10.24 OuterApplication.mxml

When we run the OuterApplication it displays the picture shown in Figure 10.17. As you can see, ap-plications are isolated in different application domains and their style settings (backGround color) are different:

Working with Large Applications CHAPTER 10

Page 434: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

432 RIA WITH ADOBE FLEX AND JAVA

Figure 10.17 The screen showing the OuterApplication at startup

Now let’s type “Passed From Outer App” in the text field and click all three buttons in a row. The first button click will read the control’s value from the InnerApplication, while the clicks on the second and the third buttons will modify the values of the InnerApplication’s control and public variable, respectively. The outcome is shown in Figure 10.18.

Figure 10.18 The OuterApplication after clicking on “Modify Inner Label” and “Change Inner Variable”

Working with Large Applications CHAPTER 10

Page 435: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 433

You may say that the communications between the applications is not as seamless as it is with add-ons. This is correct. But let’s face it, plug-ins serve a different purpose: they’re an easy way to add third-party content to your application. They’re complete applications with their own UI, including styling. Their functionality doesn’t depend on host applications. In fact, you have to be very careful with references to the objects from the plug-in, since live references will prevent the unloading of plug-in classes.

Modules and ModuleLoadersWeeks before this book was going to print Adobe released Flex 2.0.1, introducing the mx.modules package with ModuleLoader and Module classes, in particular. ModuleLoader behaves very much like SWFLoader with some nuances, for example, ModuleLoader extends VBox while SWFLoader is only a UIComponent.

ModuleLoader facilitates the loading of modules. Take a regular Flex application, replace <mx:Ap-plication> with <mx:Module>, compile, and you have the module. Not surprisingly, modules are completely self-initialized, much like normal applications.

Programmatically, loading modules looks almost identical to using SWFLoader. To illustrate this point, we have rewritten our OuterApplication/InnerApplication example and created InnerMod-ule and OuterModuleLoader.

All in all, modules seem to be an ideal way of plugging in independent UI widgets, subsystems, or applications, similar to SWFLoader.

Listing 10.23 presents the code for InnerModule. We cloned the code from InnerApplication and added the lines in bold:

<?xml version=”1.0”?>

<!-- InnerModule.mxml-->

<mx:Module xmlns:mx=”http://www.adobe.com/2006/mxml” implements=”IHeyModule”

backgroundColor=”0xffeeff”>

<mx:Script>

[Bindable]

public var value:String = “From InnerModule with love!”;

public function getProperty(name:String):* {

if (name==”value”) return value; else return undefined;

}

public function setProperty(name:String, newVal:*):void {

if (name==”value”) value=newVal;

}

</mx:Script>

<mx:Panel title=”This Panel is a part of InnerModule” >

<mx:Label id=”label_1” text=”This label contains static text” />

Working with Large Applications CHAPTER 10

Page 436: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

434 RIA WITH ADOBE FLEX AND JAVA

<mx:Label text=”This label is bound to ‘value’, currently - {value}”/>

</mx:Panel>

</mx:Module>

Listing 10.23 InnerModule.mxml

You’ll notice that our module implements the IHeyModule interface, but please don’t get the idea that modules have anything to do with interfaces. Likewise, modules and applications loaded by SWFLoader are free to implement any interface, much as OuterApplication and OuterModule-Loader (later in this section) are free to ignore an interface as long as the names of the public prop-erties are known and there is an abundance of double quotes.

We modeled InnerModule-config.xml, Listing 10.24, after InnerApplication-config.xml. Since we externed the Flex framework, the build results in a 12K size of the InnerModule.swf.

<flex-config>

<!--InnerModule-config.xml-->

<compiler>

<external-library-path>

<path-element>C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex SDK 2/frame-

works/libs/playerglobal.swc</path-element>

<path-element>C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex SDK 2/frame-

works/libs/framework.swc</path-element>

<path-element>C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex SDK 2/frame-

works/libs/utilities.swc</path-element>

<path-element>C:/Program Files/Adobe/Flex Builder 2 Plug-in/Flex SDK 2/frame-

works/libs/flex.swc</path-element>

</external-library-path>

</compiler>

</flex-config>

Listing 10.24 Local config file InnerModule-config.xml

Here is the definition of the IHeyModule interface:

// IHeyModule.as

package

{

public interface IHeyModule

{

function getProperty(name:String):*;

function setProperty(name:String, value:* ):void;

Working with Large Applications CHAPTER 10

Page 437: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 435

}

}

Listing 10.25 Interface IHeyModule

Then, we cloned OuterModuleLoader from OuterApplication. The only difference in the code is that while with SWFLoader we had to look at the loaded SWF as an instance of the SystemManager, here we are dealing with an instance of DisplayObject:

<?xml version=”1.0”?>

<!-- OuterModuleLoader.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”>

<mx:Script>

<![CDATA[

import mx.managers.SystemManager;

public var innerDisplayObject:DisplayObject = null;

private function getInnerDisplayObject():DisplayObject {

return moduleLoader.child;

}

public function readInnerLabel():void {

if (!innerDisplayObject) innerDisplayObject = getInnerDisplayObject();

label_1.text=innerDisplayObject[“label_1”].text;

}

public function modifyInnerLabel():void {

if (!innerDisplayObject) innerDisplayObject = getInnerDisplayObject();

innerDisplayObject[“label_1”].text = new_value.text;

}

public function changeInnerVariable():void {

if (!innerDisplayObject) innerDisplayObject = getInnerDisplayObject();

// If you know name of the properties/methods of the InnerModule, you can

// use them directly:

// innerDisplayObject[“value”] = new_value.text;

// or, alternatively, if InnerModule “implements” IHeyModule you can use

// interface to dereference available properties & methods

IHeyModule(innerDisplayObject).setProperty(“value”,new_value.text);

}

]]>

</mx:Script>

<mx:Label id=”label_1” text=”This ‘outer’ label contains static text” />

<mx:ModuleLoader id=”moduleLoader” url=”InnerModule.swf” creationComplete=”module

Loader.loadModule()” />

Working with Large Applications CHAPTER 10

Page 438: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

436 RIA WITH ADOBE FLEX AND JAVA

<mx:Button label=”Read Inner Label” click=”readInnerLabel();”/>

<mx:Button label=”Modify Inner Label” click=”modifyInnerLabel();”/>

<mx:Button label=”Change Inner Variable” click=”changeInnerVariable();”/>

<mx:TextInput id=”new_value” />

</mx:Application>

Listing 10.26 OuterModuleLoader application

When you run OuterModuleLoader, its look and behavior will be indistinguishable from OuterAp-plication, Figure 10.18.

Finally, we present samples of using the IHeyModule interface by InnerApplication and OuterAp-plication, Listings 10.27 and 10.28, respectively.

<?xml version=”1.0”?>

<!-- InnerApplication.mxml-->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” implements=”IHeyModule”

backgroundColor=”0xffeeff” >

<mx:Script>

[Bindable]

public var value:String = “From InnerApp with love!”;

public function getProperty(name:String):* {

if (name==”value”) return value; else return undefined;

}

public function setProperty(name:String, newVal:*):void {

if (name==”value”) value=newVal;

}

</mx:Script>

<mx:Panel title=”This Panel is a part of InnerApplication”>

<mx:Label id=”label_1” text=”This label contains static text” />

<mx:Label text=”This label is bound to ‘value’, currently - {value}”/>

</mx:Panel>

</mx:Application>

Listing 10.27 Version of InnerApplication that implements the IHeyModule interface

<?xml version=”1.0”?>

<!-- OuterApplication.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”>

<mx:Script>

<![CDATA[

Working with Large Applications CHAPTER 10

Page 439: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 437

import mx.managers.SystemManager;

public var innerApplication:Application = null;

private function getInnerApplication():Application {

var systemManager:SystemManager = SystemManager(swfLoader.content);

return Application(systemManager.application);

}

public function readInnerLabel():void {

if (!innerApplication) innerApplication = getInnerApplication();

label_1.text=innerApplication[«label_1»].text;

}

public function modifyInnerLabel():void {

if (!innerApplication) innerApplication = getInnerApplication();

innerApplication[«label_1»].text = new_value.text;

}

public function changeInnerVariable():void {

if (!innerApplication) innerApplication = getInnerApplication();

// If you know name of the properties/methods of the InnerApp, you can

// use them directly:

// innerApplication[«value»] = new_value.text;

// or, alternatively, if InnerApp «implements» IHeyModule you can use

// interface to dereference available properties & methods

IHeyModule(innerApplication).setProperty(“value”,new_value.text);

}

]]>

</mx:Script>

<mx:Label id=”label_1” text=”This ‘outer’ label contains static text” />

<mx:SWFLoader id=”swfLoader” source=”InnerApplication.swf” />

<mx:Button label=”Read Inner Label” click=”readInnerLabel();”/>

<mx:Button label=”Modify Inner Label” click=”modifyInnerLabel();”/>

<mx:Button label=”Change Inner Variable” click=”changeInnerVariable();”/>

<mx:TextInput id=”new_value” />

</mx:Application>

Listing 10.28 Version of OuterApplication that makes use of the IHeyModule interface

Working with Large Applications CHAPTER 10

Page 440: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

438 RIA WITH ADOBE FLEX AND JAVA

When Size MattersFlex compilers support the -link-report option. It lets you specify a file to be populated with the linker dependencies found during the build of your SWF. There is a matching -load-externs com-piler option that lets you specify the classes you don’t want to link, but rather prefer to extern. Con-veniently, -load-externs anticipates the input to be in exactly the same XML format as is produced by -link-report.

Speaking of modules and applications, just as an example case, this pair of options enables you to extern for the module everything that will be loaded by the application. However, if you plan to ever reuse the same module for the different hosting application, this technique is not applicable.

There is an opposite way to use the -link-report option. Earlier we explained why you should use self-initialized RSLs for component and resource libraries (as opposed to visual plugins, where modules and applications reign). If you recall, we managed to keep these RSLs extremely tiny by externing an entire set of Flex framework SWC files under the assumption that the main applica-tion would supply those via AutoExtract. The only downside has been the indiscriminating extract of the entire set of classes (resulting in a large 1Mb framework.swf in particular). But why tolerate a wholesale extract? If you use the compiler’s option –include you can go back to merge-in Link Type and have the application merge in all the classes required by your libraries.

In this scenario, which applies to self-initialized RSLs as well as modules, you run -link-report on the module or library and instead of optimizing the module, you optimize the application. No need to rebuild your libraries or modules to satisfy an individual application – they stay 100% reusable. Instead, you tune your applications.4

And, speaking of size, don’t forget the -debug option. By turning it to false, you may strip up to 30% of the size taken by the debugging information. By the same token, you may want to recompile framework.swf from the Adobe Flex Framework source files to take its size down in the first place, prior to resorting to –include.

SummaryIn this chapter we went through a number of techniques of utmost importance when it comes to large application development. We showed you how to:

• Break a monolithic build process into a set of smaller independent builds• Enable developers to work with isolated libraries of components and resources• Do “on-demand” loading of functionality that’s impossible to forecast and package in advance• Optimizate the total size of the application’s size

Working with Large Applications CHAPTER 10

Page 441: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 439

Endnotes1. A word of advice: make a decision where you want to keep your SWC files and stick to it. Avoid the situ-

ation where the SWC linked in by your application is not the same one that you’re making with the Flex

Builder Project.

2. In Flex 2.0.1 you can coordinate running the Ant file with the build of the SWC by adding the extra

“builder” to the project.

3. During the lifetime of any resource project or component library project, the corresponding Resource-

Main.as file will need to be kept in sync with the project contents. One way to achieve this is to include a

code-generation target in your Ant script and treat the contents of .flexLibProperties as input data. Alter-

natively, to automate this and all other Flex build-related tasks, you can use the Flex2Ant utility developed

by FarataSystems, see http://www.myflex.org for more information.

4. The current release of Flex does not provide automation tools for this optimization, so you may want to

create the automation process yourself. Alternatively, you can use Flex2Ant, by FarataSystems, see http://

www.myflex.org for more information

Working with Large Applications CHAPTER 10

Page 442: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 441

Advanced DataGrid

11CHAPTER

Page 443: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

442 RIA WITH ADOBE FLEX AND JAVA

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml layout=”vertical”

xmlns:fx=”com.theriabook.controls.*”>

<fx:DataGridEx id=”dg”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployees”

creationComplete=”dg.fill()”

/>

</mx:Application>

In the listing below we’ve subclassed the standard Flex DataGrid. The code in our DataGrid bor-rows the relevant code from Listing 8.12:

// DataGrid.as

package com.theriabook.controls {

import mx.controls.DataGrid;

public class DataGrid extends mx.controls.DataGrid {

import mx.rpc.remoting.mxml.RemoteObject;

import mx.rpc.AbstractOperation;

import mx.rpc.events.*;

import mx.controls.Alert;

import mx.managers.CursorManager;

public var destination:String=null, method : String = null;

public var autoFill : Boolean = true;

protected var ro:RemoteObject = null;

public function fill(... args): void {

if( ro==null ) {

if( destination==null || destination.length==0 )

throw new Error(“No destination specified”);

if( method==null || method.length==0 )

throw new Error(“No retrieveMethod specified”);

ro = new RemoteObject(destination);

ro.showBusyCursor = true;

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

}

var operation:AbstractOperation = ro.getOperation(method);

operation.arguments = args;

operation.send();

The subject of DataGrid control, let alone advanced control, has no limits. In any UI framework, the robustness of the DataGrid, or whatever the name might be, depends on formatting and validating utilities as well as a whole suite of data input controls: CheckBoxes, ComboBoxes, RadioButtons, all sorts of Inputs, Masks, and so on. Using theatrical terminology, the role of the king is played by his entourage. Practically speaking, touching up on the DataGrid is touching up on a large part of the Flex framework. Hence this chapter is a ride with fast-changing scenery.

We’ll start our DataGrid journey upgrading a standard DataGrid to a “destination-aware” control capable of populating itself. Since we introduced this technique in earlier chapters you should ease right on in. Next, we’ll look at formatting DataGrid columns and that would naturally lead us to a hidden treasury of the DataGridColumn, which, once you start treating it like a companion rather than a dull element of MXML syntax, can help you do amazing things.

To set the stage for the DataGrid’s satellite controls, we’ll lead you through making a reusable Flex library that supports the mapping of your custom tags to the arbitrary hierarchy of implementation classes. We will show you the power of computed expressions in place of styles such as color, and font, making our DataGrid truly data-driven. We will build a suite of effective controls to use as item renderers/editors, as well as standalone. Then we will step up the offense and show you how the entire sets of DataGridColumn definitions, including item editors and renderers, can be dynami-cally computed based on the data!

Making DataGrid Destination-AwareIn Chapters 7 and 8 we introduced the concept of destination-awareness for collections and con-trols. It helps to eliminate the tedious effort required to populate your controls with Remoting or DataServices-based data. Here’s how an MXML application with a destination-aware DataGrid might like if we apply the familiar remoting destination com_theriabook_composition_Employ-eeDAO:

<!-- DestinationAwareDataGridExDemo.mxml-->

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

Advanced DataGridCHAPTER 11

Advanced DataGrid

Page 444: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 443

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml layout=”vertical”

xmlns:fx=”com.theriabook.controls.*”>

<fx:DataGridEx id=”dg”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployees”

creationComplete=”dg.fill()”

/>

</mx:Application>

In the listing below we’ve subclassed the standard Flex DataGrid. The code in our DataGrid bor-rows the relevant code from Listing 8.12:

// DataGrid.as

package com.theriabook.controls {

import mx.controls.DataGrid;

public class DataGrid extends mx.controls.DataGrid {

import mx.rpc.remoting.mxml.RemoteObject;

import mx.rpc.AbstractOperation;

import mx.rpc.events.*;

import mx.controls.Alert;

import mx.managers.CursorManager;

public var destination:String=null, method : String = null;

public var autoFill : Boolean = true;

protected var ro:RemoteObject = null;

public function fill(... args): void {

if( ro==null ) {

if( destination==null || destination.length==0 )

throw new Error(“No destination specified”);

if( method==null || method.length==0 )

throw new Error(“No retrieveMethod specified”);

ro = new RemoteObject(destination);

ro.showBusyCursor = true;

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

}

var operation:AbstractOperation = ro.getOperation(method);

operation.arguments = args;

operation.send();

The subject of DataGrid control, let alone advanced control, has no limits. In any UI framework, the robustness of the DataGrid, or whatever the name might be, depends on formatting and validating utilities as well as a whole suite of data input controls: CheckBoxes, ComboBoxes, RadioButtons, all sorts of Inputs, Masks, and so on. Using theatrical terminology, the role of the king is played by his entourage. Practically speaking, touching up on the DataGrid is touching up on a large part of the Flex framework. Hence this chapter is a ride with fast-changing scenery.

We’ll start our DataGrid journey upgrading a standard DataGrid to a “destination-aware” control capable of populating itself. Since we introduced this technique in earlier chapters you should ease right on in. Next, we’ll look at formatting DataGrid columns and that would naturally lead us to a hidden treasury of the DataGridColumn, which, once you start treating it like a companion rather than a dull element of MXML syntax, can help you do amazing things.

To set the stage for the DataGrid’s satellite controls, we’ll lead you through making a reusable Flex library that supports the mapping of your custom tags to the arbitrary hierarchy of implementation classes. We will show you the power of computed expressions in place of styles such as color, and font, making our DataGrid truly data-driven. We will build a suite of effective controls to use as item renderers/editors, as well as standalone. Then we will step up the offense and show you how the entire sets of DataGridColumn definitions, including item editors and renderers, can be dynami-cally computed based on the data!

Making DataGrid Destination-AwareIn Chapters 7 and 8 we introduced the concept of destination-awareness for collections and con-trols. It helps to eliminate the tedious effort required to populate your controls with Remoting or DataServices-based data. Here’s how an MXML application with a destination-aware DataGrid might like if we apply the familiar remoting destination com_theriabook_composition_Employ-eeDAO:

<!-- DestinationAwareDataGridExDemo.mxml-->

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

Advanced DataGridCHAPTER 11

Advanced DataGrid

Page 445: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

444 RIA WITH ADOBE FLEX AND JAVA

}

private function ro_onFault(evt:FaultEvent):void {

CursorManager.removeBusyCursor();

Alert.show(“Failed retrieving data: “+evt.message, “[DestinationAwareDataGrid]” +

id);

}

private function ro_onResult(evt:ResultEvent):void {

CursorManager.removeBusyCursor();

if (evt.result.length != 0)

dataProvider = evt.result;

}

}

}

Listing 11.1 DataGrid.as, the first version with “destination-awareness”

If we run the application, our screen will show the employee records grid in its default formatting:

Figure 11.1 The “destination-aware” DataGrid demo running

Formatting with labelFunction We’ve just looked at the default data formatting provided by DataGrid out-of-the-box. The easiest way to improve column formatting is by supplying a labelFunction (introduced in Chapter 4) for each column that requires extra attention:

<mx:DataGridColumn dataField=”PHONE” labelFunction=”phoneLabelFunction” />

Advanced DataGridCHAPTER 11

Page 446: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 445

A labelFunction is a callback and being invoked by the DataGrid, it receives the appropriate data item plus information about the column, so that you can technically apply one function to more than one column. As far as the formatting techniques per se, Flex offers plenty of pre-defined for-matters that can be used out-of-the-box or be customized to your specific needs. For instance, to format social security numbers (SS_NUMBER) we could have used mx.formatters.SwitchSymbol-Formatter and created the following function:

import mx.formatters.SwitchSymbolFormatter;

private var sf:SwitchSymbolFormatter;

private function ssnLabelFunction(item:Object, column:DataGridColumn):String {

if (!sf) {

sf = new SwitchSymbolFormatter();

}

return sf.formatValue(“###-##-####”, item[“SS_NUMBER”]);

}

Then inside the DataGridColumn we can mention this function name:

<mx:DataGridColumn dataField=”SS_NUMBER” labelFunction=”ssnLabelFunction” />

Similarly, in Listing 11.2 we can apply the same technique to the PHONE field, setting the format-String to (###)###-####:

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

<!-- LabelFunctionFormattingDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns=”*” layout=”vertical”

xmlns:fx=”com.theriabook.controls.*”>

<fx:DataGrid id=”dg” creationComplete=”dg.fill()”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

>

<fx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<mx:DataGridColumn dataField=”PHONE” labelFunction=”phoneLabelFunction”

/>

<mx:DataGridColumn dataField=”SS_NUMBER” labelFunction=”ssnLabelFunction”

/>

</mx:Array>

</fx:columns>

</fx:DataGridEx>

<mx:Script>

<![CDATA[

Advanced DataGridCHAPTER 11

Page 447: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

446 RIA WITH ADOBE FLEX AND JAVA

import mx.formatters.SwitchSymbolFormatter;

private var sf:SwitchSymbolFormatter;

private function ssnLabelFunction(item:Object, column:DataGridColumn):String {

if (!sf) {

sf = new SwitchSymbolFormatter();

}

return sf.formatValue(“###-##-####”, item[“SS_NUMBER”]);

}

private function phoneLabelFunction(item:Object, column:DataGridColumn):String {

if (!sf) {

sf = new SwitchSymbolFormatter();

}

return sf.formatValue(“(###)###-####”, item[column.dataField]);

}

]]>

</mx:Script>

</mx:Application>

Listing 11.2 LabelFunctionFormattingDemo.mxml

Here’s how our formatting looks on the screen:

Figure 11.2 Screenshot of the running LabelFunctionFormatting demo

Formatting with Extended DataGridColumn The labelFunction-based formatting does the job. The price tag is hard-coding labelFunction(s) names in the DataGridColumn definitions. If you packaged a set of label functions in the mydo-main.LabelFunction class, your column definitions might look like this:

Advanced DataGridCHAPTER 11

Page 448: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 447

<mx:DataGridColumn dataField=”PHONE” labelFunction=”mydomain.LabelFunctions.

phone” />

<mx:DataGridColumn dataField=”SS_NUMBER” labelFunction=” mydomain.LabelFunc-

tions.ssn” />

A more pragmatic approach is to introduce the formatString as an extra attribute for DataGridCol-umn and have it encapsulate the implementation details. We’re talking about the following alterna-tive:

<fx:DataGridColumn dataField=”PHONE” formatString=”phone” />

<fx:DataGridColumn dataField=”SS_NUMBER” formatString=”ssn” />

Implementation of such syntax is within arm’s reach. We just need to extend – well, not the arm, but the DataGridColumn, so that instead of mx:DataGridColumn we would use, say, our fx:DataGridColumn. The mx.controls.dataGridClasses.DataGridColumn is just a respository of styles and properties to be used by the DataGrid. In the Flex class hierarchy it merely extends CSS-StyleDeclaration. Nothing prevents us from extending it further and adding an extra attribute. In the case of formatString we delegate the actual job of assigning the value of labelFunction to a helper class - FormattingManager:

public class DataGridColumn extends mx.controls.dataGridClasses.DataGridColumn {

public function set formatString( fs:String ) : void{

FormattingManager.setFormat(this, fs);

}

}

Wait a minute, where did the FormattingManager come from? Well, we’ll get to the implementation of that class a bit later. At this point, we have to eliminate a possible naming collision between our to-be-made DataGridColumn and the standard mx.controls.dataGridClasses.DataGridColumn.

Introducing a Component Manifest File Up till now we’ve been keeping folders with classes of our custom components under the applica-tion MXML files folder. To reference these components we’ve been declaring namespaces point-ing to some hard-coded albeit relative paths such as xmlns:lib=”com.theriabook.controls.*” or xmlns=”*”. The problem with this approach is that these namespaces point to one folder at a time. As a result, we end up with either multiple custom namespaces or a wild mix of components in one folder.

To break the spell and abstract the namespace from the exact file location, we have to use the com-ponent manifest file. Component manifest is an XML file that allows mapping component names to the implementing classes. Below is an example of a component manifest that combines our custom DataGrid and DataGridColumn located in different folders:

Advanced DataGridCHAPTER 11

Page 449: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

448 RIA WITH ADOBE FLEX AND JAVA

<?xml version=”1.0”?>

<componentPackage>

<component id=”DataGrid” class=”com.theriabook.controls.DataGrid”/>

<component id=”DataGridColumn”

class=”com.theriabook.controls.dataGridClasses.DataGridColumn”/>

</componentPackage>

To benefit from the use of this component manifest you have to compile your components with the compc or use the Flex Library project. To be more specific, you have to instruct compc to select the URL that your application can later use in place of the hard-coded folder in the xmlns declaration. So we’ll create a new FlexLibrary project – theriabook, where we will put the theriabook-manifest.xml containing the XML above and set the relevant project properties as shown in the Figure 11.3:

Figure 11.3 The manifest file and namespace definition for the Flex Library Project

We will add theriabook project to the Flex Build Path of our application project as “SWC folder”.Now we can move the DataGrid from our application project to theriabook and replace xmlns:fx=”com.theriabook.controls” with xmlns:fx=”http://www.theriabook.com/2006”, provided that the Flex Build Path of our application project includes a reference to theriabook.swc. As a result, our application will reference fx:DataGrid and fx:DataGridColumn, irrespective of their physical loca-tion.

Having done that, let’s get back to customizing the DataGridColumn.

Advanced DataGridCHAPTER 11

Page 450: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 449

More on Customizing the DataGridColumn We’ll put our DataGridColumn in the subfolder dataGridClasses as a sign of our respect to the well-thought-out directory structure of the Flex framework:

// DataGridColumn.as (first version)

package com.theriabook.controls.dataGridClasses {

import mx.controls.dataGridClasses.DataGridColumn;

public class DataGridColumn extends mx.controls.dataGridClasses.DataGridColumn {

public function set formatString( fs:String ) : void{

FormattingManager.setFormat (this, fs);

}

}

}

Listing 11.3 DataGridColumn.as, the first version

As we mentioned, the “dirty” job of locating and assigning the proper label function has been del-egated to the helper class FormattingManager. This class, presented in Listing 11.4, should be put into our theriabook project:

// FormattingManager.as, first version

package com.theriabook.controls.dataGridClasses

{

public class FormattingManager

{

import mx.controls.dataGridClasses.DataGridColumn;

import mx.formatters.SwitchSymbolFormatter;

private static var sf:SwitchSymbolFormatter;

public static function setFormat(dgc:mx.controls.dataGridClasses.DataGridColumn

formatString:String):void {

switch (formatString.toLowerCase()) {

case “ssn”:

dgc.labelFunction = ssnLabelFunction;

case “phone”:

dgc.labelFunction = phoneLabelFunction;

}

}

private static function ssnLabelFunction(item:Object, column:

mx.controls.dataGridClasses.DataGridColumn):

String {

Advanced DataGridCHAPTER 11

Page 451: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

450 RIA WITH ADOBE FLEX AND JAVA

if (!sf) {

sf = new SwitchSymbolFormatter();

}

return sf.formatValue(“###-##-####”, item[“SS_NUMBER”]);

}

private static function phoneLabelFunction(item:Object, column:DataGridColumn):S

tring {

if (!sf) {

sf = new SwitchSymbolFormatter();

}

return sf.formatValue(“(###)###-####”, item[column.dataField]);

}

}

}

Listing 11.4 FormattingManager.as, the first version

Tada! And the winner is… the developer. Once we add theriabook.swc (with DataGrid, DataGridCol-umn, and FormattingManager) to the library path, the application code gets reduced to the following:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:fx=”http://www.theriabook.com/2006”>

<fx:DataGrid id=”dg” creationComplete=”dg.fill()”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

>

<fx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<fx:DataGridColumn dataField=”PHONE” formatString=”phone” />

<fx:DataGridColumn dataField=”SS_NUMBER” formatString=”ssn” />

</mx:Array>

</fx:columns>

</fx:DataGridEx>

</mx:Application>

Listing 11.5 A Simple application illustrating FormattingManager.as

Improving FormattingManager In the previous examples we’ve used the SwitchSymbolFormatter for both phone and ssn format-

Advanced DataGridCHAPTER 11

Page 452: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 451

ting. As soon as we start formating numbers or currency values, it’s natural to use NumberFormat-ter or CurrencyFormatter – descendants of mx.formatters.Formatter. In fact, Flex offers a dedicated formatter even for the phone formatting.

While SwitchSymbolFormatter derives from Object, all the rest of the formatters descend from For-matter. By encapsulating this specific SwitchSymbolFormatter in the custom class MaskFormatter, we’ll help ourselves to base the next version of FormattingManager entirely on Formatters:

//MaskFormatter.as

package com.theriabook.formatters {

import mx.formatters.Formatter;

import mx.formatters.SwitchSymbolFormatter;

public class MaskFormatter extends Formatter {

private var formatString:String;

private var sf:SwitchSymbolFormatter;

public function MaskFormatter( fs:String) {

formatString = fs;

sf = new SwitchSymbolFormatter();

}

public override function format(val:Object):String {

return sf.formatValue( formatString, val);

}

}

}

Listing 11.6 MaskFormatter.as

Look how this MaskFormatter1 simplifies our FormattingManager: we can replace all private meth-ods with an anonymous function, as shown in Listing 11.7. Please note that the reference to the appropriate formatter is preserved with the closure:

//com.theriabook.controls.dataGridClasses.FormattingManager.as

package com.theriabook.controls.dataGridClasses

{

public class FormattingManager

{

import mx.controls.dataGridClasses.DataGridColumn;

import mx.formatters.*;

import com.theriabook.formatters.MaskFormatter;

public static function setFormat(

Advanced DataGridCHAPTER 11

Page 453: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

452 RIA WITH ADOBE FLEX AND JAVA

dgc:mx.controls.dataGridClasses.DataGridColumn,

formatString:String):void {

var formatter:Formatter = null;

switch (formatString.toLowerCase()) {

case “ssn”:

formatter = new MaskFormatter(“###-##-####”);

break;

case “money”:

formatter = new CurrencyFormatter();

CurrencyFormatter(formatter).precision=2;

break;

case “phone”:

formatter = new PhoneFormatter();

break;

case “shortdate”:

formatter = new DateFormatter();

break;

case “zip”:

formatter = new ZipCodeFormatter();

break;

}

if (formatter) {

dgc.labelFunction = function (

item:Object,

dgc:mx.controls.dataGridClasses.DataGridColumn):String

{

return formatter.format(item[dgc.dataField]);

}

}

}

}

}

Listing 11.7 FormattingManager.as

Here is the testing application FormatStringDemo, see Listing 11.8:

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

<!-- FormatStringDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:fx=”http://www.theriabook.com/2006”>

<fx:DataGrid id=”dg” creationComplete=”dg.fill()”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

Advanced DataGridCHAPTER 11

Page 454: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 453

>

<fx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<fx:DataGridColumn dataField=”SALARY” formatString=”money” />

<fx:DataGridColumn dataField=”PHONE” formatString=”phone” />

<fx:DataGridColumn dataField=”SS_NUMBER” formatString=”ssn” />

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.8 FormatStringDemo.mxml

If you run it, the DataGrid dg will be formatted as shown in Figure 11.4:

Figure 11.4 DataGrid formatted with columns’ formatString attributes

Let’s focus on the hard-coding that we allowed in the case of the money value:

case “money”:

formatter = new CurrencyFormatter();

CurrencyFormatter(formatter).precision=2; break;

This hard-coding reflects, perhaps, the most “popular” case. But what if we want to have the full advantage of the properties of the corresponding formatter such as precision in the case of the CurrencyFormatter? To address these cases we’re going to introduce one more fx:DataGridColumn property – formatData. Here’s how it will be used in the application MXML:

<fx:DataGridColumn dataField=”SALARY” >

Advanced DataGridCHAPTER 11

Page 455: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

454 RIA WITH ADOBE FLEX AND JAVA

<fx:formatData>

<mx:Object formatString=”money” precision=”0”/>

</fx:formatData>

</fx:DataGridColumn>

The elegance of MXML lets us implement this extension with just a few lines of extra code in com.theriabook.controls.dataGridClasses.DataGridColumn2:

public function set formatData(fd :Object) : void{

FormattingManager.setFormat(this, fd);

}

Then, to accommodate the change on the FormattingManager side, we’ll iterate through all the properties of the formatData object and attempt to assign them to the appropriate properties of the formatter with an emphasis on the word “appropriate.” The MXML compiler isn’t going to help us check the properties of the unsealed <mx:Object> against the properties of the formatter. So, to protect ourselves from the no-such-property-exceptions, we surround the property assignments with try/catch:

public static function setFormat(

dgc:mx.controls.dataGridClasses.DataGridColumn,

formatData:Object

):void {

. . . . .

if (!(formatData is String)) {

for (var property:String in formatData) { try {

formatter[property] = formatData[property];

} catch (err:Error) {

// Property does not match formatter type

}

}

}

. . . . .

}

The complete listing of renewed FormattingManager is presented below3. Of course, when maintain-ing your own framework, you’d transform this class to accommodate your particular requirements:

//FormattingManager.as

package com.theriabook.controls.dataGridClasses

{

public class FormattingManager

{

import mx.controls.dataGridClasses.DataGridColumn;

Advanced DataGridCHAPTER 11

Page 456: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 455

import mx.formatters.*;

import com.theriabook.formatters.MaskFormatter;

public static function setFormat(

dgc:mx.controls.dataGridClasses.DataGridColumn,

formatData:Object):void {

var formatter:Formatter = null;

var fs:String;

if (formatData is String)

fs = formatData as String;

else

fs = formatData.formatString;

switch (fs.toLowerCase()) {

case “ssn”:

formatter = new MaskFormatter(“###-##-####”);

break;

case “money”:

formatter = new CurrencyFormatter();

CurrencyFormatter(formatter).precision=2;

break;

case “phone”:

formatter = new PhoneFormatter();

break;

case “shortdate”:

formatter = new DateFormatter();

break;

case “zip”:

formatter = new ZipCodeFormatter();

break;

default:

if (fs.indexOf(“#”)!=-1) {

formatter = new MaskFormatter(fs);

};

}

if (!(formatData is String)) {

for (var property:String in formatData) {

try {

formatter[property] = formatData[property];

} catch (err:Error) {

// Property does not match formatter type

}

}

Advanced DataGridCHAPTER 11

Page 457: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

456 RIA WITH ADOBE FLEX AND JAVA

}

if (formatter) {

dgc.labelFunction = function (

item:Object,

dgc:mx.controls.dataGridClasses.DataGridColumn

):String {

return formatter.format(item[dgc.dataField]);

}

}

}

}

}

Listing 11.9 FormattingManager.as, the complete listing

Finally, here’s the sample application to test our changes, FormatDataDemo:

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:fx=”http://www.theriabook.com/2006”>

<!—FormatDataDemo.mxml -->

<fx:DataGrid id=”dg” creationComplete=”dg.fill()”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

>

<fx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<fx:DataGridColumn dataField=”BIRTH_DATE” formatString=”shortdate”/>

<fx:DataGridColumn dataField=”SALARY” >

<fx:formatData>

<mx:Object formatString=”money” precision=”0”/>

</fx:formatData>

</fx:DataGridColumn>

<fx:DataGridColumn dataField=”PHONE”>

<fx:formatData>phone</fx:formatData>

</fx:DataGridColumn>

<fx:DataGridColumn dataField=”SS_NUMBER” formatString=”ssn” />

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.10 FormatDataDemo.mxml

Advanced DataGridCHAPTER 11

Page 458: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 457

When you run the application it will produce the DataGrid shown in Figure 11.5:

Figure 11.5 DataGrid formatted with formatString and formatData attributes

We’ll continue beefing up our custom DataGridColumn after a short detour into CheckBox and RadioButton controls.

CheckBox as a Drop-In Renderer As we warned at the beginning of the chapter, DataGrids rarely come alone. In this section we’re going to suggest customizing the CheckBox, which will help us illustrate the additional features of custom DataGridColumns.

The state of a CheckBox control is managed by the Boolean property selected. At the same time, many business systems use either Y/N or, sometimes, 0/1 flags. As a result, translating business-specific values into selected and vice versa burdens the application code. Listing 11.11 presents a custom CheckBox that supports application-specific on and off values along with the current value:

//CheckBox.as

package com.theriabook.controls

{

import mx.controls.CheckBox;

public class CheckBox extends mx.controls.CheckBox

{

public var onValue:Object=true;

public var offValue:Object=false;

private var _value:Object;

public function set value(val:Object) :void {

Advanced DataGridCHAPTER 11

Page 459: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

458 RIA WITH ADOBE FLEX AND JAVA

_value = val;

invalidateProperties();

}

public function get value():Object {

return selected?onValue:offValue;

}

override protected function commitProperties():void {

selected = (_value == onValue);

super.commitProperties();

}

}

}

Listing 11.11 CheckBox.as, first version

So, using this CheckBox, we could have written <fx:CheckBox value=”Y” onValue=”Y” offValue=”N” />

to have selected CheckBox, or <fx:CheckBox value=”N” onValue=”Y” offValue=”N” />

to set selected to false.

DataGridColumn as ItemRenderer’s Knowledge BaseNow let’s get back to the DataGrid world. What if we wanted to use our CheckBox as the DataGrid item renderer? Here’s a suggested use case example:

<fx:DataGridColumn dataField=”BENE_DAY_CARE”

itemRenderer=”com.theriabook.controls.CheckBox” >

</fx:DataGridColumn>

Obviously, we have to modify the CheckBox some more to take care of the value in the data setter:

override public function set data(item:Object):void

{

super.data = item;

if( item!=null ) {

value = item[DataGridListData(listData).dataField];

}

}

Advanced DataGridCHAPTER 11

Page 460: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 459

But how will we communicate to our CheckBox-turned-itemRenderer the offValue and onValue properties? Ideally, we’d need something like: <fx:DataGridColumn dataField=”BENE_DAY_CARE”

itemRenderer=”com.theriabook.controls.CheckBox” >

<fx:extendedProperties>

<mx:Object onValue=”Y” offValue=”N” />

</fx:extendedProperties>

</fx:DataGridColumn>

Flex creators thought of this in advance. An object referenced by itemRenderer isn’t a CheckBox, but rather an instance of mx.core.ClassFactory wrapped around the CheckBox. The mechanism of mx.core.ClassFactory lets Flex generate multiple instances of another class – com.theriabook.controls.CheckBox, in our case. Importantly, each instance created by the factory is assigned iden-tical properties borrowed from the properties of the factory object. So, all we have to do is pass the value of the extendedProperties as properties of the itemRenderer.

//DataGridColumn.as

package com.theriabook.controls.dataGridClasses{

import mx.controls.dataGridClasses.DataGridColumn;

public class DataGridColumn extends mx.controls.dataGridClasses.DataGridColumn {

public function set extendedProperties(val:Object) :void {

this.itemRenderer[“properties”] = val;

}

public function set formatString( fs :String ) : void{

FormattingManager.setFormat(this, fs);

}

public function set formatData( fd :Object ) : void{

FormattingManager.setFormat(this, fd);

}

}

}

Listing 11.12 DataGridColumn, third version

Below is the listing of the test application, ExtendedPropertiesDemo. When you run it, it produces the DataGrid shown in Figure 11.6.

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

<!-- ExtendedPropertiesDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

Advanced DataGridCHAPTER 11

Page 461: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

460 RIA WITH ADOBE FLEX AND JAVA

xmlns:fx=”http://www.theriabook.com/2006”>

<fx:DataGrid id=”dg” creationComplete=”dg.fill()”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

>

<fx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<fx:DataGridColumn dataField=”BENE_DAY_CARE” itemRenderer=”com.theriabook.

controls.CheckBox” >

<fx:extendedProperties>

<mx:Object onValue=”Y” offValue=”N” />

</fx:extendedProperties>

</fx:DataGridColumn>

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.13 ExtendedPropertiesDemo.mxml

Figure 11.6 Custom CheckBox used as a drop-in renderer with extended properties

We should have mentioned the alternative run-of-the-mill approach with inline itemRenderer, Listing 11.14:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical” xmlns:

fx=”com.theriabook.controls.*”>

<fx:DataGrid id=”dg”

destination=”com_theriabook_composition_EmployeeDAO”

method=”getEmployees” creationComplete=”dg.fill()” >

<fx:columns>

Advanced DataGridCHAPTER 11

Page 462: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 461

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<mx:DataGridColumn dataField=”BENE_DAY_CARE”>

<mx:itemRenderer>

<mx:Component>

<fx:CheckBox onValue=”Y” offValue=”N”/>

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.14 Using CheckBox as an inline renderer

Arguably, the extendedProperties approach is more efficient, since it absolves MXML of generating an extra nested class (mx:Component) for each column of this kind4. We’ve introduced you to yet another way of customizing a DataGridColumn, and we’ll continue building on top of it in the fol-lowing sections.

Nitpicking CheckBox There are some additional remarks that we ought to make about our CheckBox implementation at this point.

The first one is related to the horizontal alignment of the CheckBox. Instinct tells us that a label-free checkbox should be centered in the column rather than stuck in the left-most position. At first, you may try applying a textAlign style to the DataGridColumn – to no avail. Then you might resort to another run-of-the-mill approach to center the checkbox by putting it inside a container such as an HBox. Here’s the performance-based advice endorsed by Flex Framework engineers: avoid containers inside the DataGrid cell at all reasonable cost. In particular, instead of using HBox, why not subclass the CheckBox and override the updateDisplayList() method? It gets quite natural, once you’ve stepped on this path, so we’ll add the code shown below to our CheckBox (the complete code for the com.theriabook.controls.CheckBox is presented in Listing 11.15):

import mx.core.mx_internal;

use namespace mx_internal; . . . .

override protected function updateDisplayList(

unscaledWidth:Number, unscaledHeight:Number):void

{

super.updateDisplayList(unscaledWidth, unscaledHeight);

if (currentIcon) {

Advanced DataGridCHAPTER 11

Page 463: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

462 RIA WITH ADOBE FLEX AND JAVA

var style:String = getStyle(“textAlign”);

if ((!label) && (style==”center”) ) {

currentIcon.x = (unscaledWidth - currentIcon.measuredWidth)/2;

}

}

}

Note the use of namespace mx_internal. It’s required to reference the currentIcon that visualizes the checkbox, since currentIcon – the child of the original CheckBox – is originally scoped as mx_internal.

Now we modify the testing application to include textAlign=”center”:

<fx:DataGridColumn dataField=”BENE_DAY_CARE” textAlign=”center”

itemRenderer=”com.theriabook.controls.CheckBox” >

<fx:extendedProperties>

<mx:Object onValue=”Y” offValue=”N” />

</fx:extendedProperties>

</fx:DataGridColumn>

And, when we run it, all checkboxes are in their proper place:

Figure 11.7 CenteredCheckBoxDemo screenshot

The second nitpicking point is related to undefined as a possible value of a property. Under our cur-rent business scenario, we can assume that some of the employees are not eligible for the daycare benefit, and relevant items in the dataProvider’s collection are lacking the BENE_DAY_CARE prop-erty, which for dynamic items can be expressed as item.BENE_DAY_CARE==”undefined.”

Advanced DataGridCHAPTER 11

Page 464: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 463

Does it make sense to show checkboxes for non-eligible employees? Perhaps, it doesn’t. In this case we’d make currentIcon invisible. You can select a different approach and show a fuzzy check-box image instead, but this is beyond the point5. The following modification of updateDisplayList() does the job of removing checkBox when the value is undefined:

override protected function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void

{

super.updateDisplayList(unscaledWidth, unscaledHeight);

if (currentIcon) {

var style:String = getStyle(“textAlign”);

if ((!label) && (style==”center”) ) {

currentIcon.x = (unscaledWidth - currentIcon.measuredWidth)/2;

}

currentIcon.visible = (_value!=undefined);

}

}

To accommodate this change we have to loosen up the class definitions for value as shown in List-ing 11.15, where we change Object to undefined.

The next and probably the most important fix is that our CheckBoxes have been silenced. Try to click on one, scroll the row out of view and scroll it back in. The checkbox doesn’t retain your se-lection and it shouldn’t; we’ve never communicated the change to the underlying data. To remedy the situation we’ll add the constructor method, where we’d start listening on the “change” event; once the event is intercepted we’ll modify the data item with the CheckBox value. That, in turn, will result in either onValue or offValue, as per our value getter:

public function CheckBox() {

super();

addEventListener(Event.CHANGE,

function(event:Event):void{

if (listData && listData is DataGridListData

) {

data[DataGridListData(listData).dataField] = value;

}

}

);

}

And the last point: Flex collections by themselves do not notice any changes done to the underlying

Advanced DataGridCHAPTER 11

Page 465: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

464 RIA WITH ADOBE FLEX AND JAVA

data items; it is the application’s responsibility to keep the collections informed, especially if you have more then one view based on the same collection. In our case, change of the data would go totally unnoticed by the collection displayed by DataGrid, until we explicitly notify it with the COLLECTION_CHANGE event. The complete code for the second version of CheckBox is presented in Listing 11.15:

//CheckBox.as

package com.theriabook.controls

{

import flash.events.Event;

import mx.controls.CheckBox;

import mx.controls.dataGridClasses.DataGridListData;

import mx.core.mx_internal;

use namespace mx_internal;

public class CheckBox extends mx.controls.CheckBox

{

public var onValue:Object=true;

public var offValue:Object=false;

private var _value:*;

public function CheckBox() {

super();

addEventListener(Event.CHANGE,

function(event:Event):void{

if (listData && listData is DataGridListData) {

data[DataGridListData(listData).dataField] = value;

var evt:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_

CHANGE, false, false, CollectionEventKind.UPDATE, -1, -1, [data]);

mx.controls.DataGrid(DataGridListData(listData).owner).dataProvider.

dispatchEvent(evt);

}

}

);

}

public function set value(val:*) :void {

_value = val;

invalidateProperties();

}

public function get value():Object {

if (_value==undefined)

return _value;

else

return selected?onValue:offValue;

}

Advanced DataGridCHAPTER 11

Page 466: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 465

override protected function commitProperties():void {

if (_value!=undefined)

selected = (_value == onValue);

super.commitProperties();

}

override public function set data(item:Object):void {

super.data = item;

if( item!=null ) {

value = item[DataGridListData(listData).dataField];

}

}

override protected function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void

{

super.updateDisplayList(unscaledWidth, unscaledHeight);

if (currentIcon) {

var style:String = getStyle(“textAlign”);

if ((!label) && (style==”center”) ) {

currentIcon.x = (unscaledWidth - currentIcon.measuredWidth)/2;

}

currentIcon.visible = (_value!=undefined);

}

}

}

}

Listing 11.15 CheckBox.as, the second version

Next comes the test application. We’ve added the “Revoke day care benefit” button, which turns DAY_CARE_BENE into undefined on the currently selected DataGrid item. We also had to notify the collection with the itemUpdated() call:

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

<!-- UndefinedCheckBoxDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:fx=”http://www.theriabook.com/2006”>

<mx:Button label=”Revoke day care benefit”

click=”dg.selectedItem.BENE_DAY_CARE=undefined;

dg.dataProvider.itemUpdated(dg.selectedItem);” />

<fx:DataGrid id=”dg” creationComplete=”dg.fill();dg.selectedIndex=0;”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

Advanced DataGridCHAPTER 11

Page 467: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

466 RIA WITH ADOBE FLEX AND JAVA

>

<fx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<fx:DataGridColumn dataField=”BENE_DAY_CARE” textAlign=”center”

itemRenderer=”com.theriabook.controls.CheckBox” >

<fx:extendedProperties>

<mx:Object onValue=”Y” offValue=”N” />

</fx:extendedProperties>

</fx:DataGridColumn>

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.16 UndefinedCheckBoxDemo.mxml

When you run the above application, you’ll see that checkboxes retain the selection after scrolling out and back into view. If you click the “Revoke” button for the first two rows, you’re going to see a picture similar to the one below:

Figure 11.8 UndefinedCheckBoxDemo partial screenshot

The last CheckBox fix will come in handy once you declare the DataGrid editable. Why declare it editable in the first place if we seem to be editing the DataGrid already? Let’s not forget that the only field we’ve been editing so far is the checkbox BENE_DAY_CARE. Should you decide to allow editing of the text fields, you would have to change the definition of the DataGrid as it is shown in bold in the following snippet6:

Advanced DataGridCHAPTER 11

Page 468: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 467

<fx:DataGrid id=”dg” creationComplete=”dg.fill();dg.selectedIndex=0;”

editable=”true”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

>

But once you do that, a click on the beautiful checkbox of ours would turn it into a default editor – TextInput, quite like in a Cinderella story. To make the miracle last, you’d declare that your ren-derer is good to go as an editor as well:

<fx:DataGridColumn dataField=”BENE_DAY_CARE” textAlign=”center”

itemRenderer=”com.theriabook.controls.CheckBox” rendererIsEditor=”true”>

. . .

</fx:DataGrid>

By default, DataGrid reads the text property of the item editor. You can nominate a different prop-erty via editorDataField (in our case that would be value). Alternatively, and that will help us later in the chapter, you can “upgrade” the checkbox to carry the text property:

public function set text(val:String) :void {

value = val;

}

public function get text():* {

return value;

}

We leave it to the reader to try the latest changes in the CheckBox and test the application. Or you can find the solution in the source code that comes with the book. We’ll be counting on it later in the chapter.

RadioButtonGroupBox as Drop-In RendererWe can apply similar techniques to RadioButton controls. Here’s the code snippet suggesting how the group of RadioButton controls can be used as a drop-in item renderer (and editor). Instead of an onValue/offValue pair, we’re introducing an array of options7:

<fx:DataGridColumn dataField=”STATUS” width=”300” headerText=”Status”

rendererIsEditor=”true” itemRenderer=”com.theriabook.containers.RadioButtonGroupBox”>

<fx:options>

<mx:Array id=”options”>

<mx:Object data=”A” label=”Active”/>

<mx:Object data=”T” label=”Terminated”/>

<mx:Object data=”L” label=”On leave”/>

Advanced DataGridCHAPTER 11

Page 469: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

468 RIA WITH ADOBE FLEX AND JAVA

</mx:Array>

</fx:options>

</fx:DataGridColumn>

To support this use case we need to build the renderer class and make the DataGridColumn pass it the array of options. The latter can be done by adding the following options setter to our DataGrid-Column:

package com.theriabook.controls.dataGridClasses{

. . . . .

public class DataGridColumn extends mx.controls.dataGridClasses.DataGridColumn

{

. . . . .

public function set options(val:Array):void {

if (itemRenderer) itemRenderer [“properties”] = {options:val};

}

}

}

Now let’s build the renderer. By definition, to be an item renderer, the component has to imple-ment an IListItemRenderer interface. To qualify as drop-in, a component also has to implement IDropInListItemRenderer. A standard CheckBox implements both interfaces, so when we were ex-tending CheckBox in the last section, we didn’t have to mention a single implements and just mer-rily used data and listData at our convenience.

This is not the case now. Had RadioButtonGroup been at least a UIComponent, we’d need to im-plement IDataRenderer and IDropInListItemRenderer interfaces and be done. But RadioButton-Group isn’t even a DisplayObject! So we’ll base our renderer on mx.containers.Box with RadioBut-tonGroup embedded8:

private var group:RadioButtonGroup=null ;

public function RadioButtonGroupBox() {

super();

group = new RadioButtonGroup();

}

Having a RadioButtonGroup is just the beginning. Whenever our component gets assigned options, we’ll translate them into a set of RadioButton controls. Each RadioButton will be added as a child of the renderer (container):

private var _options:Array=null;

public function set options(opt:Array):void {

var i:int;

Advanced DataGridCHAPTER 11

Page 470: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 469

. . . .

_options=opt;

for (i= 0; i < opt.length; i++) {

var rb:RadioButton = new RadioButton();

rb.label = opt[i].label;

rb.value = opt[i].data;

addChild(rb);

}

}

override public function addChild(child:DisplayObject):DisplayObject {

if (child is RadioButton) {

(child as RadioButton).group = group;

group.addInstance(child as RadioButton);

}

return super.addChild(child);

}

}

Note how subscribing a RadioButton to the group is delegated to the overridden addChild() meth-od:

(child as RadioButton).group = group;

group.addInstance(child as RadioButton);

Had we done it directly in the options setter, there would be no need for addChild() at all, so why go the convoluted way? The answer is: to enable the potential use of RadioButtonGroup-Box as a regular container, outside the renderer context. In other words, whenever a RadioBut-ton gets added to the component – as part of the options or not – it gets associated with the group.

Next, since we want the component as a drop-in renderer, we need to implement the IDropInLis-tItemRenderer interface, so that the extra information about the hosting List will be at our fingertips:

private var _listData:BaseListData=null;

public function get listData():BaseListData {

return _listData;

}

public function set listData(value:BaseListData):void {

_listData = value;

}

And once we have the listData, we can offer the following override of the data setter of IDataRen-derer:

Advanced DataGridCHAPTER 11

Page 471: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

470 RIA WITH ADOBE FLEX AND JAVA

override public function set data(item:Object):void {

super.data = item;

if( item!=null ) {

group.selectedValue = item[DataGridListData(listData).dataField];

}

}

Similarly, we consider both use cases of the standalone component and item renderer while imple-menting the property value. In the case of the item renderer, our component updates the underly-ing data:

public function get value():Object {

return group.selectedValue;

}

public function set value(v:Object) : void {

group.selectedValue = v;

if (listData && listData is DataGridListData) {

data[DataGridListData(listData).dataField] = group.selectedValue;

var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLEC

TION_CHANGE, false, false, CollectionEventKind.UPDATE, -1, -1, [data]);

DataGrid(DataGridListData(listData).owner).dataProvider.

dispatchEvent(event);

}

}

Finally, how about capturing the selection of a radiobutton? Since we need to listen to the change event on the RadioButtonGroup, we’ll set up the listener right in the constructor method, handling the Event.CHANGE with the anonymous function: public function RadioButtonGroupBox() {

. . . .

group = new RadioButtonGroup();

group.addEventListener(Event.CHANGE,

function event:Event):void {

value = event.target.selectedValue;

}

);

}

The complete code of the RadioButtonGroupBox is presented in Listing 11.17. See if you can dis-cover the discrepancies between the listing and what we outlined in our snippets. There are really just a few things.• We’ve added the text property so that in the item editor use case we don’t have to specify

editorValue=”value”.

Advanced DataGridCHAPTER 11

Page 472: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 471

• We’ve made the properties text and value bindable by “change” and “valueCommit” events, dispatching events being done by the anonymouos Event.CHANGE handler and value setter correspondingly.

• We allowed the dynamic re-assignment of options by removing the existing dynamic RadioButtons before building new ones from the options array.

// RadioButtonGroupBox.as

package com.theriabook.containers

{

import flash.display.DisplayObject;

import flash.events.Event;

import mx.containers.Box;

import mx.core.IDataRenderer;

import mx.controls.dataGridClasses.DataGridListData;

import mx.controls.listClasses.BaseListData;

import mx.controls.listClasses.IDropInListItemRenderer;

import mx.controls.RadioButton;

import mx.controls.RadioButtonGroup;

import mx.events.FlexEvent;

import mx.core.mx_internal;

use namespace mx_internal;

public class RadioButtonGroupBox extends Box implements IDataRenderer, IDropInLis-

tItemRenderer

{

private var group:RadioButtonGroup=null ;

public function RadioButtonGroupBox() {

super();

verticalScrollPolicy = “off”;

horizontalScrollPolicy = “off”;

group = new RadioButtonGroup();

group.addEventListener(Event.CHANGE,

function (event:Event):void {

value = event.target.selectedValue;

dispatchEvent(event);

}

);

}

override public function addChild(child:DisplayObject):DisplayObject {

if (child is RadioButton) {

(child as RadioButton).group = group;

Advanced DataGridCHAPTER 11

Page 473: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

472 RIA WITH ADOBE FLEX AND JAVA

group.addInstance(child as RadioButton);

}

return super.addChild(child);

}

override public function set data(item:Object):void {

super.data = item;

if( item!=null ) {

group.selectedValue = item[DataGridListData(listData).dataField];

}

}

[Bindable(“valueCommit”)]

[Bindable(“change”)]

public function get text():Object {

return value;

}

public function set text(v:Object) : void {

value = v;

}

[Bindable(“valueCommit”)]

[Bindable(“change”)]

[Inspectable(category=”General”)]

public function get value():Object {

return group.selectedValue;

}

public function set value(v:Object) : void {

group.selectedValue = v;

if (listData && listData is DataGridListData) {

data[DataGridListData(listData).dataField] = group.selectedValue;

var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_

CHANGE, false, false, CollectionEventKind.UPDATE, -1, -1, [data]);

DataGrid(DataGridListData(listData).owner).dataProvider.dispatchEvent(event);

}

dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));

}

private var _listData:BaseListData=null;

public function get listData():BaseListData {

return _listData;

}

public function set listData(value:BaseListData):void {

Advanced DataGridCHAPTER 11

Page 474: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 473

_listData = value;

}

private var _options:Array=null;

public function set options(opt:Array):void {

var i:int;

if (_options!=null) {

for (i=0; i<_options.length; i++) {

var child:RadioButton = group.getRadioButtonAt(i);

removeChild(child);

}

}

_options=opt;

for (i= 0; i < opt.length; i++) {

var rb:RadioButton = new RadioButton();

rb.label = opt[i].label;

rb.value = opt[i].data;

addChild(rb);

}

}

}

}

Listing 11.17 RadioButtonGroupBox.as

To take the standalone RadioButtonGroupBox for a spin, we wrote the small application RadioBut-tonGroupBoxStandAloneDemo presented in Listing 11.18. When you run it, Figure 11.9, you can check that it’s still a normal Box container enriched with the ability to dynamically create as many RadioButton controls as there are option values.

Advanced DataGridCHAPTER 11

Page 475: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

474 RIA WITH ADOBE FLEX AND JAVA

Figure 11.9 A snapshot of the RadioButtonGroupBoxStandaloneDemo

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

<!-- RadioButtonGroupBoxStandaloneDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:fx=”http://www.theriabook.com/2006”

layout=”vertical”>

<mx:Text text=”Selected value is: {rbgb.value}”/>

<fx:RadioButtonGroupBox id=”rbgb” value=”L” direction=”vertical”>

<fx:options>

<mx:Array id=”options”>

<mx:Object data=”A” label=”Active”/>

<mx:Object data=”T” label=”Terminated”/>

<mx:Object data=”L” label=”On leave”/>

</mx:Array>

</fx:options>

<mx:HRule/>

<mx:RadioButton value=”R” label=”Retired”/>

</fx:RadioButtonGroupBox>

</mx:Application>

Listing 11.18 UndefinedCheckBoxDemo.mxml

Now let’s test the itemRenderer/itemEditor scenario. Not that it’s required, but we would prefer to lay out the radiobuttons horizontally. So, we’ll create RadioButtonGroupHBox as a simple exten-sion of RadioButtonGroupHBox, taking care of the box’s direction and adding a couple of padding pixels (the default Box padding is 0):

Advanced DataGridCHAPTER 11

Page 476: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 475

// RadioButtonGroupHBox.as

package com.theriabook.containers

{

public class RadioButtonGroupHBox extends RadioButtonGroupBox {

public function RadioButtonGroupHBox() {

super();

direction = “horizontal”;

setStyle(“paddingLeft”, “5”);

}

}

}

Listing 11.19 RadioButtonGroupHBox.as

A screenshot of the test application RadioButtonGroupHBoxDemo is in Figure 11.10. As you may notice in the corresponding Listing 11.20, we’ve declared the entire DataGrid editable and indi-cated for the “status” column that rendererIsEditor=”true”:

Figure 11.10 A snapshot of the RadioButtonGroupHBoxDemo

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

<!-- RadioButtonGroupHBoxDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:fx=”http://www.theriabook.com/2006”

layout=”vertical”>

<fx:DataGrid id=”dg” creationComplete=”dg.fill();dg.selectedIndex=0;” editable=”true”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

>

Advanced DataGridCHAPTER 11

Page 477: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

476 RIA WITH ADOBE FLEX AND JAVA

<fx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” headerText=”Last Name” />

<mx:DataGridColumn dataField=”EMP_FNAME” headerText=”First Name” />

<fx:DataGridColumn dataField=”STATUS” width=”270” headerText=”Status”

rendererIsEditor=”true” itemRenderer=”com.theriabook.containers.RadioButtonGroupHBox”>

<fx:options>

<mx:Array id=”options”>

<mx:Object data=”A” label=”Active”/>

<mx:Object data=”T” label=”Terminated”/>

<mx:Object data=”L” label=”On leave”/>

</mx:Array>

</fx:options>

</fx:DataGridColumn>

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.20 RadioButtonGroupHBoxDemo.as

Computed Column ColorEnough of control detours! Getting back to the DataGrid, we’ll look at controlling the style of the DataGridColumn: such as color, backGroundcolor, font, and so on. To be exact, we’ll focus on de-fining the style in such a way that it is re-evaluated along with the data changes. Many times there are business requirements that call for the style to be different from row to row. Suppose we have to highlight the salaries of the high-paid employees in red and show the regular salary value in green. Leaving aside the twilight subject of what “high-paid” is, let’s put the threshold at 50k. One solution is to use an in-line itemRenderer:

<mx:DataGridColumn dataField=”SALARY” textAlign=”right”>

<mx:itemRenderer>

<mx:Component>

<mx:Label>

<mx:Script>

<![CDATA[

override protected function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void

{

super.updateDisplayList(unscaledWidth, unscaledHeight);

if (data && listData) { // Check that we are in a List and we are

“smart”

if (data.SALARY > 50000) {

setStyle(“color”, “red”);

Advanced DataGridCHAPTER 11

Page 478: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 477

} else {

setStyle(“color”, “green”);

}

}

}

]]>

</mx:Script>

</mx:Label>

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

As you can see, we used Label, an immediate descendant of UIComponent, as a component base. Why? Because updateDisplayList(), a UIComponent’s method, would be out of reach for a standard DataGridItemRenderer based on the UITextField. Alernatively, we could have achieved the same functionality with a more elegant binding expression syntax:

<mx:DataGridColumn dataField=”SALARY” textAlign=”right”>

<mx:itemRenderer>

<mx:Component>

<mx:Label

color=”{data.SALARY&gt;50000?255*256*256:255*256}”

>

</mx:Label>

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

Listing 11.21 presents the complete code for the sample application StandardDynamicStyleDemo. Besides the DataGrid, we’ve thrown in “Increase” and “Decrease” buttons that let us modify the salary values in increments of 10k:

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

<!-- StandardDynamicStyleDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:fx=”http://www.theriabook.com/2006”>

<mx:HBox >

<mx:Button label=”Increase”

click=”dg.selectedItem.SALARY+=10000.00;

dg.dataProvider.itemUpdated(dg.selectedItem);” />

<mx:Button label=”Decrease”

click=”dg.selectedItem.SALARY-=10000.00;

dg.dataProvider.itemUpdated(dg.selectedItem,’SALARY’);” />

Advanced DataGridCHAPTER 11

Page 479: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

478 RIA WITH ADOBE FLEX AND JAVA

</mx:HBox> <fx:DataGrid id=”dg” creationComplete=”dg.fill();dg.selectedIndex=0;”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

>

<fx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<mx:DataGridColumn dataField=”SALARY” textAlign=”right”>

<mx:itemRenderer>

<mx:Component>

<mx:Label

color=”{data.SALARY&gt;50000?255*256*256:255*256}”

/>

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.21 StandardDynamicStyleDemo.mxml

When you run StandardDynamicStyleDemo, you’ll see the picture in Figure 11.11:

Figure 11.11 A snapshot of the StandardDynamicStyleDemo

Advanced DataGridCHAPTER 11

Page 480: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 479

Computed Column BackgroundWe can’t apply the same technique for background color because Label doesn’t support back-groundColor style. We can resort to TextInput, which does have backgroundColor, but missing some backgroundColor doesn’t seem a good reason for giving up a lightweight Label in favor of TextInput. After all, the beauty of Flex is that framework controls are open to extension. So, here it is, our custom Label extended with backgroundColor support, Listing 11.22:

// Label.as (theriabook.swc)

package com.theriabook.controls

{

import mx.controls.Label;

[Style(name=”backgroundAlpha”, type=”Number”, inherit=”no”)]

[Style(name=”backgroundColor”, type=”uint”, format=”Color”, inherit=”no”)]

dynamic public class Label extends mx.controls.Label

{

override protected function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

if (getStyle(“backgroundColor”)){

graphics.clear();

graphics.beginFill(getStyle(“backgroundColor”), getStyle(“backgroundAlpha”));

graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);

graphics.endFill();

}

}

}

}

Listing 11.22 Label.as

As you can see, we’ve defined not one, but two styles – backgroundAlpha and backgroundColor – and we use both values with graphics.beginFill() inside the overridden implementation of up-dateDisplayList(). Once we add Label.as to theriabook.swc and register it in the component mani-fest XML, the DataGridColumn can be redefined as in the following snippet:

<mx:DataGridColumn dataField=”SALARY” textAlign=”right”>

<mx:itemRenderer>

<mx:Component>

<fx:Label

backgroundColor=”{data.SALARY&gt;50000?255*256*256:255*256}”

/>

Advanced DataGridCHAPTER 11

Page 481: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

480 RIA WITH ADOBE FLEX AND JAVA

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

The difference between the approach above and the way we defined a similar DataGridColumn in Listing 11.22 is that now we compute backgroundColor instead of the color and use fx:Label instead of the one from mx namespace. If you do the replacement and run the program, you’ll see the picture in Figure 11.12.

Figure 11.12 Snapshot of the background color computed with binding expression

Despite the seemingly satisfying result, there’s something wrong here: using the powerful item ren-derer mechanism to manage styles seems to be design overkill. Let’s speak our minds: we’re after dynamic runtime styles, right? So, wouldn’t it be nice if we added an extra DataGridColumn attri-bute, called, say, runtimeStyles, where we could list all styles and abstract from the implementa-tion. Below is an example:

<fx:DataGridColumn dataField=”SALARY” textAlign=”right” formatString=”money”>

<fx:runtimeStyles>

<mx:Object

backgroundColor=”{function(item:Object):String {return (item.SALARY&g

t;50000)?’red’:’green’;}}”

/>

</fx:runtimeStyles>

</fx:DataGridColumn>

This approach would let developers concentrate on the substance rather than on the process. Let’s make it happen.

Advanced DataGridCHAPTER 11

Page 482: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 481

Runtime Column Styles UnleashedHere’s the plan. We’ll upgrade the default itemRenderer of the fx:DataGrid from UITextField to our custom fx:Label. We’ll extend fx:DataGridColumn with an extra property – runtimeStyles. Finally, we’ll intercept data changes to an item renderer, whether default or not, to reassign all runtime Styles on each.

Here’s how the constructor of the standard DataGrid assigns itemRenderer:

package mx.controls {

public class DataGrid extends DataGridBase implements IIMESupport

{

public function DataGrid()

{

super();

itemRenderer = new ClassFactory(DataGridItemRenderer);

. . . . . .

}

}

}

We have to fight the temptation to replace the assignment of the itemRenderer with

itemRenderer = new ClassFactory(com.theriabook.controls.Label);

Here is why. A ClassFactory instance is a “factory object,” which is used to generate instances of an-other class (aka a generator class), with the newInstance() method. According to our plan we need to intercept data changes to any instance of the generator class item renderer. Precisely, we’ll have to listen to the FlexEvent.DATA_CHANGE event on every instance of the com.theriabook.controls.Label created by “the factory.” Hmm, what could be simpler than adding the needed event listener to the controls? That would be okay if we commit ourselves to fx:Label as the only item renderer and by no means do we propose to take the power of custom item renderers away. To make the runtimeStyles control mechanism agnostic to the type of renderer, we’d like to listen to FlexEvent.DATA_CHANGE on the instances of any generator class.

It only sounds difficult. After all, a ClassFactory is nothing but an implementation of the IFactory interface with a single property – properties and single method – newInstance(). That’s it. So we can easily wrap a standard ClassFactory inside our custom one to intercept the newInstance() call. We’ll call our wrapping class factory the UIClassFactory:

function DataGrid() {

super();

itemRenderer = new UIClassFactory(ClassFactory(com.theriabook.controls.La-

bel));

}

Advanced DataGridCHAPTER 11

Page 483: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

482 RIA WITH ADOBE FLEX AND JAVA

The constructor of the UIClassFactory would simply store the reference to the instance of the real class factory – cf. Meanwhile UIClassFactory’s newInstance() would delegate the call to the cf.newInstance(). It would also register the listener to the FlexEvent.DATA_CHANGE event as shown below:

public class UIFactory implements IFactory

{

. . . . .

public function UIClassFactory( cf:ClassFactory ) {

wrappedClassFactory = cf;

}

public function newInstance():* {

var obj:* = wrappedClassFactory.newInstance();

obj.addEventListener(FlexEvent.DATA_CHANGE, onDataChange);

return obj;

}

private function onDataChange(event:FlexEvent):void{

. . . . .

}

}

As a reminder, properties of factory-manufactured objects get assigned by iterating over… fasten your seat belts please…properties of the specific property of the factory object. The name of this aggregating property is properties. That way all instances are initialized with the same values. Wrap-ping the properties property is quite simple:

public function set properties(v:Object):void {

wrappedClassFactory.properties = v;

}

public function get properties():* {

return wrappedClassFactory.properties ;

}

The complete code of UIClassFactory is presented in Listing 11.23. The only part of it that remains uncovered is the onDataChange() handler.

The implementation of the default DataGridUtemRenderer emits a DATA_CHANGE event from two places: setter of data and setter of listData. We will ignore changes of listData and, for every data change, we will reassign all style name properties carried by runtimeStyles. The values of these properties may be literal or, alternatively, function references. In the latter case, we apply the call operator () before setting the style value with the setStyle():

Advanced DataGridCHAPTER 11

Page 484: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 483

//UIClassFactory.as

package com.theriabook.util

{

import mx.core.IFactory;

import mx.core.ClassFactory;

import mx.events.FlexEvent;

import mx.controls.dataGridClasses.DataGridColumn;

public class UIClassFactory implements IFactory

{

private var wrappedClassFactory : ClassFactory;

public function set properties(v:Object):void {

wrappedClassFactory.properties = v;

}

public function get properties():* {

return wrappedClassFactory.properties ;

}

public function UIClassFactory( cf:ClassFactory ) {

wrappedClassFactory = cf;

}

public function newInstance():* {

var obj:* = wrappedClassFactory.newInstance();

obj.addEventListener(FlexEvent.DATA_CHANGE, onDataChange);

return obj;

}

private function onDataChange(event:FlexEvent):void{

var renderer:Object = event.currentTarget;

// In the default DataGridItemRenderer both data and listData are

// Bindable(“dataChange”)]

// We want to skip assinments to listData

if (renderer.data is mx.controls.dataGridClasses.DataGridColumn) return;

// Act only on ‘dynamic style’ columns

if (renderer.styleName && renderer.styleName.hasOwnProperty(“runtimeStyles”)) {

var runtimeStyles:Object = renderer.styleName[“runtimeStyles”];

for (var style:String in runtimeStyles) {

if ( runtimeStyles[style] is Function ) {

var functionObject : Function = runtimeStyles[style];

renderer.setStyle(style, functionObject(renderer.data));

Advanced DataGridCHAPTER 11

Page 485: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

484 RIA WITH ADOBE FLEX AND JAVA

}

else

renderer.setStyle(style, runtimeStyles[style]);

}

renderer.invalidateDisplayList();

}

}

}

}

Listing 11.23 UIClassFactory.as, first version

One chore is remaining. As long as we want to communicate the runtimeStyles to any item ren-derer, including the ones that are individually set on a per-column basis, we need to route them through our UIClassFactory. Accordingly we will modify our DataGridColumn and override the implementation of the DataGridColumn’s itemRenderer setter:

override public function set itemRenderer( val : IFactory ) : void {

super.itemRenderer = new UIClassFactory(val as ClassFactory);

}

The full listing of our DataGridColumn is shown in Listing 11.24

// DataGridColumn.as

package com.theriabook.controls.dataGridClasses{

import com.theriabook.util.UIClassFactory;

import mx.controls.dataGridClasses.DataGridColumn;

import mx.core.ClassFactory;

import mx.core.IFactory;

public class DataGridColumn extends mx.controls.dataGridClasses.DataGridColumn {

public var runtimeStyles:Object = null;

public var runtimeProperties:Object = null;

public function set extendedProperties(val:Object) :void {

if (itemRenderer)

itemRenderer[“properties”] = val;

}

override public function set itemRenderer( val : IFactory ) : void {

super.itemRenderer = new UIClassFactory(val as ClassFactory);

}

Advanced DataGridCHAPTER 11

Page 486: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 485

public function set formatString( fs :String ) : void{

formatData = fs;

}

public function set formatData( fd :Object ) : void{

FormattingManager.setFormat(this, fd);

}

}

}

Listing 11.24 DataGridColumn.as

Here is our test application – RuntimeStyleDemo, Listing 11.25:

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

<!-- RuntimeStylesDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:fx=”http://www.theriabook.com/2006”>

<mx:Script>

<![CDATA[

import com.theriabook.composition.dto.EmployeeDTO;

private var linkage:com.theriabook.composition.dto.EmployeeDTO

private function computedFontWeight(item:Object):String {

return (item.SALARY>55000)?’bold’:’normal’;

}

]]>

</mx:Script>

<mx:Button label=”Increase”

click=”dg.selectedItem.SALARY+=10000.00;

dg.dataProvider.itemUpdated(dg.selectedItem);” />

<mx:Button label=”Decrease”

click=”dg.selectedItem.SALARY-=10000.00;

dg.dataProvider.itemUpdated(dg.selectedItem,’SALARY’);” />

<fx:DataGrid id=”dg” creationComplete=”dg.fill();dg.selectedIndex=0;”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

>

<fx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<fx:DataGridColumn dataField=”SALARY” textAlign=”right”

formatString=”money”>

<fx:runtimeStyles>

Advanced DataGridCHAPTER 11

Page 487: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

486 RIA WITH ADOBE FLEX AND JAVA

<mx:Object

backgroundColor=”{function(item:Object):String {return

(item.SALARY&gt;50000)?’red’:’green’;}}”

fontWeight=”{computedFontWeight}”

/>

</fx:runtimeStyles>

</fx:DataGridColumn>

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.25 RuntimeStyleDemo.mxml

Figure 11.13 depicts RuntimeStylesDemo running9:

Figure 11.13 A RuntimeStyleDemo application

So far we’ve shown that it’s possible to control runtime styles via anonymous or explicit func-tions (backgroundColor versus computedFontWeight in the demo application above). You can take our approach further and completely outsource the dynamic styling to a separate con-troller object flexibly instantiated via the getDefinitionByName() method. Come to think of it, you’d completely shield developers from formatting and styling problems of a particular project!

Let’s leave the reader with this thought and turn our attention from formatting to the editing side of the DataGrid. Before we go there though, we’d like to take one more detour, introducing a couple of additional controls: MaskedInput and NumericInput.

Advanced DataGridCHAPTER 11

Page 488: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 487

Masked Input and Numeric InputInput masking is akin to crime prevention. The least that input masking does is stop a user from entering non-appropriate characters. Stricter masks can prevent users from entering inomplete numbers, less-than-required text, etc. – details are always implementation-specific. Taken to the extreme, masks can completely obliterate validation programming, albeit at the cost of fixing a specific user experience.

The first control of this section – MaskedInput – was created by Peter Ent from Adobe. We’ll be us-ing it only for illustrative purposes and the code walkthrough of MaskedInput is beyond the scope of this chapter. MaskedInput is a lightweight mask where you can indicate the maximum number of positions in the mask and prescribe the type of the characters the user can type. The movement of the insertion point is controlled by the control. For example, if you set a mask for entering a U.S. phone number as (###) ###-#### in response to the end user typing 6175551212, the control will display (617) 555-1212. Alpha keys will be blocked, but the completeness of the phone is up to the user. The control is an extension of the mx.controls.TextInput and its text returns the “mask-free” data input, i.e., 6175551212 in our use case. The main controlling property of MaskedInput is the inputMask, which can consist of any characters except: • #, which stands for a single digit• C, which capitalizes a letter (no digits allowed)• C, which forces a letter to lowercase (no digits allowed)• A or a, which allows any character.

Like all other components in this chapter, we’ve added a MaskedInput to theriabook.swc and reg-istered it in the component manifest file theriabook-manifest.xml. The only modifications we’ve done to the original MaskedInput: changed the original packaging of the MaskedInput to com.theriabook.controls and overrode the implementation of the TextInput data setter to accommodate its use in the DataGrid:

public override function set data(data:Object):void {

super.data = data;

var dgListData:DataGridListData = DataGridListData(listData);

text = data[dgListData.dataField];

}

The complete code for com.theriabook.controls.MaskedInput is provided in the accompanying DVD.

The second mask that we present in this section is NumericInput. It doesn’t control the insertion point so that typing and pasting isn’t restricted. However, it blocks any typing or pasting that con-tains invalid characters. Here’s the first iteration of the NumericInput code:

// NumericInput.as

package com.theriabook.controls

Advanced DataGridCHAPTER 11

Page 489: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

488 RIA WITH ADOBE FLEX AND JAVA

{

import mx.controls.TextInput;

import flash.events.Event;

public class NumericInput extends TextInput

{

public function NumericInput() {

super();

addEventListener(flash.events.TextEvent.TEXT_INPUT, onTextInput);

}

private function onTextInput(event:flash.events.TextEvent):void {

// TODO Find out number separators from the locale settings

var re:RegExp =

new RegExp(“[^0-9,.-]”, “i”);

var illegalCharacterFound:Boolean = re.test(event.text);

if (illegalCharacterFound) {

event.preventDefault();

}

}

}

}

Listing 11.26 NumericInput.as, first version

As you can see, we listen to TextEvent.TEXT_INPUT, which corresponds to a character or sequence of characters (paste) entered by the user. Whatever’s been entered comes as event.text and we test it with a regular expression trying to find illegal characters. The string literal that we used for RegExp reads as “anything but characters in the range 0-9 or comma or dot or minus.” If an illegal character is found, we reject the typing or pasting by cancelling the default behavior of the event10 with:

event.preventDefault();

That’s all it takes to bulletproof your input fields from undesired characters. Two more small patch-es before we leave the NumericInput, though.

In the course of marshalling the Java data across the wire, chances are your numeric data will come as Number.NaN, a direct counterpart of Java Double.NaN or Float.NaN. In general, unless you use special DTOs with embedded null indicators, this is the natural way to marshal numeric nulls; no one would appreciate the lettering NaN staring at the user instead of the empty cell. So, following the established pattern, we’re going expand our NumericInput with a property value, then have it produce the appropriate text required for presentation. Hence the following addition to Numer-icInput code:

Advanced DataGridCHAPTER 11

Page 490: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 489

private var _value:*;

[Bindable(“change”)]

public function set value(v:*):void {

_value = v;

if ((isNaN(v)) || (v==null /*null or undefined*/)) {

text=””;

} else {

text = String(v as Number);

}

}

public function get value():* {

// Preserve NaN | null | undefined, when nothing has been entered

if (((_value!=null )&& (String(_value)!=”NaN”)) || (text!=””) ) {

_value = Number(text.replace(/,/g,””)); // deformat first

}

return _value;

}

public override function set data(item:Object):void {

if (listData && listData is DataGridListData) {

var dgListData:DataGridListData = DataGridListData(listData);

value = item[dgListData.dataField];

}

super.data = item;

}

In the code fragment above we intervened in the setter for data property in case the NumericInput is embedded in the DataGrid as a renderer. There we modify the value and let the value, in turn, modify the text.

In the value getter we return the original content: null, undefined, or Number.NaN provided the user hasn’t entered anything. Otherwise, we use a regular expression to convert the text to Number globally, eliminating the spaces and thousand separators first:

_value = Number(text.replace(/,/g,””));

Strictly speaking we should have operated with a locale-specific thousands-separator character instead of “,”. For reference, U.S. English-specific definitions of String constants decimalSeparator and thousandsSeparator (“.” and “,”) are located in the file Flex SDK/ 2/frameworks/locale/en_US/validator.properties.

If you’re delivering your application to Brasil, you could create a similar or smaller file in the Flex SDK/ 2/frameworks/locale/pr_BR folder, where you’d redefine decimalSeparator as “,” and thou-

Advanced DataGridCHAPTER 11

Page 491: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

490 RIA WITH ADOBE FLEX AND JAVA

sandsSeparator as “.”. Supposedly you’d keep the original name11 – validators.properties. Then you’d modify compiler options for theriabook.swc to specify the pr_BR locale and rebuild NumericInput after adding the following code:

import mx.resources.ResourceBundle;

public class NumericInput extends TextInput {

. . . .

[ResourceBundle(“validators”)]

private static var rb:ResourceBundle;

public static var decimalSeparator:String;

public static var thousandsSeparator:String;

// Load resources during class definition loading

loadResources();

private static function loadResources():void {

decimalSeparator = rb.getString(“decimalSeparator”);

thousandsSeparator = rb.getString(“thousandsSeparator”);

}

}

Let’s move on to the testing application – NumericInputDemo. When you run it, try to type any-thing but digits, commas, and dots into the “Number” column. You won’t be able to. Again, please make no mistake: NumericInput is a lightweight mask and it doesn’t replace the need to validate. In particular, if you like regular expressions as much as we do, you may base the validation of the currency field on the mx.vaidators.RegExpValidator, applying the regular expression that’s the best match for your use case, for instance, something like:

^\$?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\.[0-

9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)

We leave it up to the reader to try. Another feature to notice while running the demo app is how NaN, null, and undefined values are preserved in the absence of a meaningful input in the corre-sponding cells:

Advanced DataGridCHAPTER 11

Page 492: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 491

Figure 11.14 NumericInputDemo application

The code of the testing application is shown below. The helper class NumberScope.as is presented in Listing 11.28:

When you run it, try to type anything but digits, comma and dot into the

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

<!--NumericInputDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:fx=”http://www.theriabook.com/2006”

layout=”vertical”

creationComplete=”onCreationComplete();”>

<mx:Script>

<![CDATA[

import mx.collections.*;

[Bindable]

private var collection:ArrayCollection;

private function onCreationComplete():void {

collection = new ArrayCollection ([

new NumberScope(124000.00),

new NumberScope(Number.NaN),

new NumberScope(null),

new NumberScope(undefined)

]);

}

]]>

</mx:Script>

Advanced DataGridCHAPTER 11

Page 493: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

492 RIA WITH ADOBE FLEX AND JAVA

<fx:DataGrid id=”dg” dataProvider=”{collection}” editable=”true” >

<fx:columns>

<mx:Array>

<fx:DataGridColumn dataField=”description” headerText=”Description”

editable=”false”/>

<mx:DataGridColumn dataField=”number” headerText=”Raw Number”

editable=”false”/>

<fx:DataGridColumn dataField=”number” headerText=”Number” editable=”true”

editorDataField=”value” itemEditor=”com.theriabook.controls.NumericInput”

formatString=”money”/>

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.27 NumericInputDemo.mxml

//NumberScope.as, helper class for NumericInputDemo.mxml

package {

public class NumberScope {

public var number:*;

public function get description():String {

if (number===null) return ‘null’;

if (number===undefined) return ‘undefined’;

return (typeof number);

}

public function NumberScope(n:*) {

number=n;

}

}

}

Listing 11.28 NumberScope.as, helper class for NumericInputDemo

Finally, Listing 11.29 depicts the complete code for NumericInput:

// NumericInput.as

package com.theriabook.controls

{

import mx.controls.TextInput;

import flash.events.Event;

import mx.controls.List;

public class NumericInput extends TextInput

Advanced DataGridCHAPTER 11

Page 494: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 493

{

import mx.controls.listClasses.BaseListData;

import mx.events.FlexEvent;

import mx.controls.dataGridClasses.DataGridListData;

public function NumericInput() {

super();

addEventListener(flash.events.TextEvent.TEXT_INPUT, onTextInput);

}

private var _value:*;

[Bindable(“change”)]

public function set value(v:*):void {

_value = v;

if ((String(v)==”NaN”) || (v==null /*or undefined*/)) {

text=””;

} else {

text = String(v as Number);

}

}

public function get value():* {

// Preserve NaN - null - undefined, when nothing has been entered

if (((_value!=null )&& (String(_value)!=”NaN”)) || (text!=””) ) {

_value = Number(text.replace(/,/g,””)); // deformat first

}

return _value;

}

public override function set data(item:Object):void {

if (listData && listData is DataGridListData) {

var dgListData:DataGridListData = DataGridListData(listData);

value = item[dgListData.dataField];

dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));

return;

}

super.data = item;

}

private function onTextInput(event:flash.events.TextEvent):void {

// TODO Find out number separators from the locale settings

var re:RegExp = new RegExp(“[^0-9,.-]”, “i”);

var illegalCharacterFound:Boolean = re.test(event.text);

Advanced DataGridCHAPTER 11

Page 495: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

494 RIA WITH ADOBE FLEX AND JAVA

if (illegalCharacterFound) {

event.preventDefault();

}

}

}

}

Listing 11.29 NumericInput.as, final version

DataGrid with Automatic Item EditorsArmed with MaskedInput and NumericInput, we can proceed with automating DataGrid editing features. Here’s the issue at hand: once you make your DataGrid as editable, you are in charge of manually assigning the appropriate item editors for each column. Compare this to the standard formatting approach, which assumes that for each column, you manually assign an item renderer or labelFunction. Do you see the 100% similarity? Make no mistake we’re not comparing item edi-tors with label function. It’s the “manually” we’re after.

As the reader is probably sensing by now, we are about to automate the programming of editors with the same technique we applied for formatting. Namely, we’ll take the burden off developers’ shoulders and make DataGrid accountable for picking up and initializing proper item editors.

If you recall, mapping label functions, albeit anonymous ones, had been delegated to a separate class, FormattingManager. Quite similarly, we are about to introduce an additional class Editing-Manager, with a single method setFormat() so that our custom DataGridColumn will be calling it right after it invokes the similar method of the FormattingManager:

// DataGridColumn.as

package com.theriabook.controls.dataGridClasses{

import com.theriabook.util.UIClassFactory;

import mx.controls.dataGridClasses.DataGridColumn;

import mx.core.ClassFactory;

import mx.core.IFactory;

public class DataGridColumn extends mx.controls.dataGridClasses.DataGridColumn {

public var runtimeStyles:Object = null;

public var runtimeProperties:Object = null;

public function set extendedProperties(val:Object) :void {

if (itemRenderer)

itemRenderer[“properties”] = val;

}

override public function set itemRenderer( val : IFactory ) : void {

Advanced DataGridCHAPTER 11

Page 496: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 495

super.itemRenderer = new UIClassFactory(val as ClassFactory);

}

public function set formatString( fs :String ) : void{

formatData = fs;

}

public function set formatData( fd :Object ) : void{

FormattingManager.setFormat(this, fd);

EditingManager.setFormat(this, fd);

}

}

}

Listing 11.30 DataGridColumn.as, improved with EditingManager

Now let’s focus on EditingManager.

Its method setFormated has to assign the value of the itemEditor property for the column. The deci-sion of which itemEditor to choose should be based entirely on the formatString. As you can see, the setFormat() method carries all the required information in its signature, and column and formatting information as method parameters. Before we go coding, let’s recap that itemEditor is a property of type IFactory. When you set its value in MXML, the compiler automatically creates an instance of ClassFactory, a class that implements the IFactory interface based on your class as “generator.” Our approach is different and we’ll be creating the ClassFactory12 explicitly.

Here’s an example of creating the ClassFactory for the column with the “shortdate” format. We create the ClassFactory based on the DateField and set its properties with an anonymous object so that, ultimately, each instance of the manufactured DateField will have its formatString set to “MM/DD/YY.” Not only do we assign the itemEditor to the DataGridColumn, we also specify the editorDataField (DataGrid assumes the text property otherwise):

case “shortdate”:

cf = new ClassFactory(DateField);

cf.properties = { formatString : “MM/DD/YY”};

dgc.editorDataField=”selectedDate”;

dgc.itemEditor = cf;

break;

Like the “zip,” “phone,” and “ssn” formats, all of which conform nicely to MaskedInput, we encap-sulate the assignment of itemEditor inside the helper function setMaskedEditor():

private static function setMaskedEditor(dgc:DataGridColumn, mask:String):

void{

Advanced DataGridCHAPTER 11

Page 497: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

496 RIA WITH ADOBE FLEX AND JAVA

var cf:ClassFactory;

cf = new ClassFactory(MaskedInput);

cf.properties = {inputMask : mask};

dgc.itemEditor = cf;

}

The full code for EditingManager is presented in Listing 11.31. Note that, like FormattingManager, we programmed the default case to handle the non-listed formatString with a “#” character in it as an unknown mask:

// EditingManager.as

package com.theriabook.controls.dataGridClasses

{

import mx.core.ClassFactory;

import mx.controls.TextInput;

import com.theriabook.controls.MaskedInput;

import com.theriabook.controls.NumericInput;

import mx.controls.DateField;

public class EditingManager

{

import com.theriabook.controls.dataGridClasses.DataGridColumn;

import mx.formatters.*;

import com.theriabook.formatters.MaskFormatter;

private static function setMaskedEditor(dgc:DataGridColumn, mask:String):void{

var cf:ClassFactory;

cf = new ClassFactory(MaskedInput);

cf.properties = {inputMask : mask};

dgc.itemEditor = cf;

}

public static function setFormat(dgc:DataGridColumn, formatData:Object):void{

var fs:String;

var cf:ClassFactory;

if (formatData is String)

fs = formatData as String;

else

fs = formatData.formatString;

switch (fs.toLowerCase()) {

case “ssn”:

setMaskedEditor(dgc, “###-##-####”);

break;

case “money”:

Advanced DataGridCHAPTER 11

Page 498: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 497

cf = new ClassFactory(NumericInput);

dgc.itemEditor = cf;

break;

case “shortdate”:

cf = new ClassFactory(DateField);

cf.properties = { formatString : “MM/DD/YY”};

dgc.editorDataField=”selectedDate”;

dgc.itemEditor = cf;

break;

case “zip”:

setMaskedEditor(dgc, “#####-####”);

break;

case “phone”:

setMaskedEditor(dgc, “(###) ###-####”);

break;

default:

if (fs.indexOf(“#”)!=-1) {

setMaskedEditor(dgc, fs);

};

}

}

}

}

Listing 11.31 EditingManager.as

Finally, Listing 11.32 presents the test application – ComputedEditorDemo:

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

<!-- ComputedEditorDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:fx=”http://www.theriabook.com/2006”>

<mx:Script>

<![CDATA[

import com.theriabook.composition.dto.EmployeeDTO;

private var linkage:com.theriabook.composition.dto.EmployeeDTO

]]>

</mx:Script>

<fx:DataGrid id=”dg” creationComplete=”dg.fill()” editable=”true”

destination=”com_theriabook_composition_EmployeeDAO” method=”getEmployees”

>

<fx:columns>

<mx:Array>

Advanced DataGridCHAPTER 11

Page 499: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

498 RIA WITH ADOBE FLEX AND JAVA

<mx:DataGridColumn dataField=”EMP_LNAME” />

<mx:DataGridColumn dataField=”EMP_FNAME” />

<fx:DataGridColumn dataField=”PHONE” formatString=”phone” />

<fx:DataGridColumn dataField=”SS_NUMBER” formatString=”ssn” />

<fx:DataGridColumn dataField=”START_DATE” formatString=”shortdate” />

<fx:DataGridColumn dataField=”SALARY” textAlign=”right”

formatString=”money”/>

</mx:Array>

</fx:columns>

</fx:DataGrid>

</mx:Application>

Listing 11.32 ComputedEditorDemo.as

No application code to populate the DataGrid, no coding to display, no coding to edit. Just indicate the format of the column and off you go. And, keep in mind our quest for DataGrid automation isn’t over yet! Meanwhile, if you run the application it will resemble Figure 11.15:

Figure 11.15 A snapshot of the ComputedEditorDemo application

Data-Driven Programming UnleashedSo far we’ve been slowly but surely reducing the manual effort of defining DataGrid columns. How about taking it to the extreme and completely eliminating the need to define DataGridColumns? The following snippet illustrates the dynamic creation of the columns we have in mind:

private function onCreationComplete() :void {

// Load metadata from external datasource …

Advanced DataGridCHAPTER 11

Page 500: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 499

var columns :Array = new Array();

for ( var i:int = 0; i < cols.length; i++) {

var dgc:DataGridColumn= new DataGridColumn();

// Assign column properties from metadata

. . . .

columns.push(dgc);

}

dg.columns = columns;

}

. . . .

<fx:DataGrid id=”dg” dataProvider=”{dp}” />

The properties of each column could be derived from some external respository, for instance – a database. But if we plan to dynamically assign properties like itemRenderer, we need to“train” our UIClassFactory to dynamically create the generator object given its class name.

As a reminder, UIClassFactory is a typical wrapper class. The constructor of UIClassFactory accepts an instance of the wrapped ClassFactory: ` public function UIClassFactory( cf:ClassFactory ) {

wrappedClassFactory = cf;

}

Accordingly, our up-to-the-minute implementation of the newInstance() delegates real instantia-tion to the wrappedClassFactory:

public function newInstance():* {

var obj:* = wrappedClassFactory.newInstance();

obj.addEventListener(FlexEvent.DATA_CHANGE, onDataChange);

return obj;

}

Let’s modify the constructor to allow String arguments in addition to ClassFactory ones. We will loosen the strict definition of the argument type from ClassFactory to Object. In case of the String, the constructor will attempt to load the correposnding class definition dynamically13 and then cre-ate the matching ClassFactory:

function UIClassFactory( cf: Object ) {

if ( cf is ClassFactory) {

wrappedClassFactory = ClassFactory(cf);

} else if (cf is String) {

var className:String = String(cf);

var clazz:Class = getDefinitionByName(className);

wrappedClassFactory = new ClassFactory(clazz);

Advanced DataGridCHAPTER 11

Page 501: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

500 RIA WITH ADOBE FLEX AND JAVA

} else {

throw new Error(“Invalid argument for UIClassFactory constructor”);

}

}

As soon as you think “definition by name,” you have to ask yourself: “Is the class definition acces-sible?” If the answer is positive, it means you are confident about the way you build and load your SWFs. To force SWF to contain an application-specific class, you can simply declare a variable of the class. We don’t find this appropriate for libraries, hence the need for the library initialization sections that we explained in Chapter 9. Following the pattern established in Chapter10, we’ll cre-ate theriabook.mxml, theriabook_code.as and build.xml files in the root of the theriabook project. Below is Listing 11.33 of theriabook_code.as. The other two files we will omit for brevity’s sake (you can find them on the accompanying DVD):

//theriabook_code.as

package {

import mx.core.SimpleApplication;

import com.theriabook.containers.*;

import com.theriabook.controls.*;

import com.theriabook.controls.dataGridClasses.*;

import com.theriabook.formatters.*;

import com.theriabook.util.UIClassFactory;

public class theriabook_code extends SimpleApplication {

public function theriabook_code() {

// Custom library initialization code should go here

trace(“theriabook.swf has been loaded and initialized 2”);

}

private var linkage:Object = {

t0:com.theriabook.containers.RadioButtonGroupBox,

t1:com.theriabook.containers.RadioButtonGroupHBox,

t2:com.theriabook.controls.CheckBox,

t3:com.theriabook.controls.DataGrid,

t4:com.theriabook.controls.DateField,

t5:com.theriabook.controls.Label,

t6:com.theriabook.controls.MaskedInput,

t7:com.theriabook.controls.NumericInput,

t8:com.theriabook.controls.dataGridClasses.DataGridColumn,

t9:com.theriabook.controls.dataGridClasses.EditingManager,

t10:com.theriabook.controls.dataGridClasses.FormattingManager,

t11:com.theriabook.formatters.MaskFormatter,

t12:com.theriabook.util.UIClassFactory

};

Advanced DataGridCHAPTER 11

Page 502: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 501

}

}

Listing 11.33 theriabook_code.as

Using build.xml we will run Ant to build the theriabook.swf. As per the DVD version of the code, the Flex application folder for this chapter has been named AdvancedDataGrid and build.xml cre-ates theriabook.swf in C:/fds/jrun4/servers/default/theriabook/AdvancedDataGrid. Accordingly, in the AdvancedDataGrid project, we will to go to Properties->FlexBuildPath-> LibraryPath and turn the Link Type for all SWC entries to RSL/AutoExtract(YES). Then we will remove the reference to theriabook project as “SWC Folder”, adding instead a reference to theriabook.swc as a file. We will set the Link Type of theriabook.swc to RSL/AutoExtract (NO), since we have already produced the SWF during the Ant step.

Now we are fully prepared to cut the application code, which is presented in Listing 11.34:

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

<!-- DataGridAutomationDemo.mxml.

Please make sure to use theriabook.swf as self-initialized library.

-->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:fx=”http://www.theriabook.com/2006” creationComplete=”onCreationComplete()”>

<mx:Script>

<![CDATA[

import com.theriabook.controls.dataGridClasses.DataGridColumn;

import com.theriabook.util.UIClassFactory;

[Bindable]

private var dp:Array = [];

private var cols:Array = [];

//import com.theriabook.containers.RadioButtonGroupHBox;

//private var linkage:Object={a:com.theriabook.containers.RadioButtonGroupHBox};

private function onCreationComplete() :void {

cols =[

{dataField:”firstName”, headerText:”First Name”},

{dataField:”lastName”, headerText:”Last Name”},

{dataField:”phone”, headerText:”Phone”, formatString:”phone”},

{dataField:”status”,headerText:”First Name”,width:280,

itemRenderer:”com.theriabook.containers.RadioButtonGroupHBox”,

options:[

{data:”A”,label:”Active”},

{data:”T”,label:”Terminated”},

Advanced DataGridCHAPTER 11

Page 503: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

502 RIA WITH ADOBE FLEX AND JAVA

{data:”L”,label:”On leave”}

]

}

];

dp=[

{firstName:”Anatole”, lastName:”Tartakovsky”, phone:”7141237890”,

status:”A”},

{firstName:”Victor”, lastName:”Rasputnis”, phone:”3053425470”, status:”L”},

{firstName:”Yakov”, lastName:”Fain”, phone:”2013335748”, status:”A”}

];

var columns :Array = new Array();

for ( var i:int = 0; i < cols.length; i++) {

var dgc:DataGridColumn= new DataGridColumn();

for (var prop:String in cols[i]) {

if (prop==”itemRenderer”) {

dgc.itemRenderer = new UIClassFactory(cols[i][prop]);

dgc.rendererIsEditor=true;

} else {

dgc[prop] = cols[i][prop];

}

}

columns.push(dgc);

}

dg.columns = columns;

}

]]>

</mx:Script>

<fx:DataGrid id=”dg” dataProvider=”{dp}” editable=”true” height=”100%” >

</fx:DataGrid>

</mx:Application>

Listing 11.34 DataGridAutomationDemo.mxml

Please notice two arrays: cols, with metadata for DataGrid columns, and dp, with the dataProvider-to-be data. Both arrays get hard-coded inside the onCreationComplete() method, but you can eas-ily imagine how they get loaded, say, from a database.

Right above the onCreationComplete() there are commented-out imports of the RadioButton-GroupHBox and the linkage variable declaration. These lines, if left to compile, could absolve us from turning the theriabook.swc into a self-initialized library. But, how can we know in advance which class definitions are required by the column’s metadata if it gets loaded from the database? We can’t and, again, we recommend making libraries self-sufficient as a “rule of thumb.”

The last comment to the application concerns the way we’ve assigned DataGridColumn proper-

Advanced DataGridCHAPTER 11

Page 504: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 503

ties: we clearly sided out itemRenderer to make the corresponding UIClassFactory (had we wanted to, a similar route would have been taken for itemEditor):

var dgc:DataGridColumn= new DataGridColumn();

for (var prop:String in cols[i]) {

if (prop==”itemRenderer”) {

dgc.itemRenderer = new UIClassFactory(cols[i][prop]);

dgc.rendererIsEditor=true;

} else {

dgc[prop] = cols[i][prop];

}

}

Figure 11.16 A snapshot of the DataGridAutomationDemo application

Figure 11.14 depicts the DataGridAutomation running14. We hope it sparks the reader’s interest in dynamic applications. What could be next here? Well, the structure of the metadata cols, resembles one-to-one the MXML section of the DataGridColumn definitions. So, we could possibly store the MXML in the database and interpret it with ActionScript as E4X. Potentially, we could even allow interactive editing of such MXML, making it a self-modifying program. But this would take us way beyond the boundaries of this chapter15.

Pivoted DataGrid or Property BagTo complete our journey into DataGrid automation land we’ll show how DataGrid can be applied in a very popular use case: a scrollable property bag. Let’s look at Figure 11.15. How many DataGrids do you think are there? The answer is two. The left part of the screen is not a Form but a DataGrid. We took off the vertical grid lines, removed the headers, and had the alternate line colors match the

Advanced DataGridCHAPTER 11

Page 505: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

504 RIA WITH ADOBE FLEX AND JAVA

background of the application, but underneath this veneer it’s still a good old DataGrid.

<fx:DataGrid id=”dg” dataProvider=”{dp}” editable=”true” height=”100%”

showHeaders=”false” alternatingItemColors=”[#869CA7,#869CA7]”

verticalGridLines=”false” variableRowHeight=”true” >

. . .

</fx:DataGrid>

Both DataGrids share the dataProvider – the ArrayCollection based on an array of ColumnRecord types:

//ColumnRecord.as

package

{

public class ColumnRecord

{

public var columnLabel:String;

public var columnType:String;

public var columnValue:*;

public var expando:Object;

public function ColumnRecord(l:String, t:String, v:*, x:Object=null) {

columnLabel=l;

columnType=t;

columnValue=v;

expando=x;

}

}

}

Listing 11.35 ColumnRecord.as, used by PropertyBagDemo

Advanced DataGridCHAPTER 11

Page 506: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 505

Figure 11.17 The PropertyBagDemo application

The complete code for the application – PropertyBagDemo – is in Listing 11.36:

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

<!-- PropertyBagDemo.mxml -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

xmlns:fx=”http://www.theriabook.com/2006” creationComplete=”init()”>

<mx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

import mx.controls.dataGridClasses.DataGridColumn;

import mx.controls.dataGridClasses.DataGridItemRenderer;

import mx.controls.RadioButton;

import mx.controls.RadioButtonGroup;

import mx.controls.TextInput;

import com.theriabook.controls.NumericInput;

import com.theriabook.containers.RadioButtonGroupBox;

import com.theriabook.controls.CheckBox;

import com.theriabook.controls.DateField;

import com.theriabook.controls.Label;

import com.theriabook.controls.MaskedInput;

import com.theriabook.util.UIClassFactory;

[Bindable]

private var dp:ArrayCollection;

private function init() :void {

dp= new ArrayCollection ([

Advanced DataGridCHAPTER 11

Page 507: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

506 RIA WITH ADOBE FLEX AND JAVA

new ColumnRecord(“First Name: “, “text”, “Michael” ),

new ColumnRecord(“Last Name: “, “text”, “Polyak” ),

new ColumnRecord(“Status: “, “radio”, “A”, {options:[

{data:”A”, label:”Active”},

{data:”T”, label:”Terminated”},

{data:”L”, label:”On leave”}

]} ),

new ColumnRecord(“Salary: “, “money”, 52525.25 ),

new ColumnRecord(“Birth Date: “, “date”, new Date(1970,4,29) ),

new ColumnRecord(“Social Security: “, “ssn”, “123704523” ),

new ColumnRecord(“Health Insurance: “, “checkbox”, “Y”, {onValue:”Y”,

offValue:”N”} ),

new ColumnRecord(“Child Care: “, “checkbox”, “Y”, {onValue:”Y”,

offValue:”N”} ),

new ColumnRecord(“Disability Insurance: “, “checkbox”, “Y”, {onValue:”Y”,

offValue:”N”} ),

new ColumnRecord(“Sex: “, “radio”, “M”, {options:[

{data:”M”, label:”Male”},

{data:”F”, label:”Female”}

]} )

]);

}

private function controlPicker(data:Object = null) :*{

if (data == null) return new Label();

switch(data.columnType) {

case “ssn”:

var mi:MaskedInput = new MaskedInput();

mi.inputMask = “###-##-####”;

return mi;

case “text”:

return new TextInput();

case “radio”:

var rbgb:RadioButtonGroupBox = new RadioButtonGroupBox();

rbgb.options = data.expando.options;

return rbgb;

case “checkbox”:

var cb:CheckBox = new CheckBox()

cb.offValue = data.expando.offValue;

cb.onValue = data.expando.onValue;

return cb;

case “money”:

return new NumericInput();

case “date”:

Advanced DataGridCHAPTER 11

Page 508: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 507

var df:DateField = new DateField();

df.editable = true;

return df;

default:

return new DataGridItemRenderer();

}

}

]]>

</mx:Script>

<mx:HDividedBox width=”100%” height=”100%”>

<fx:DataGrid id=”dg” dataProvider=”{dp}” editable=”true” height=”100%”

showHeaders=”false” alternatingItemColors=”[#869CA7,#869CA7]”

verticalGridLines=”false” variableRowHeight=”true” >

<fx:columns>

<mx:Array>

<mx:DataGridColumn width=”120” dataField=”columnLabel” headerText=”Field”

textAlign=”right” editable=”false”/>

<fx:DataGridColumn width=”150” preventRendererReuse=”true”

textAlign=”center” dataField=”columnValue” headerText=”Value”

wordWrap=”true” rendererIsEditor=”true”

itemRenderer=”{new UIClassFactory(function():* {return

controlPicker(dg.rendererData)})}”

/>

</mx:Array>

</fx:columns>

</fx:DataGrid>

<mx:DataGrid editable=”true” dataProvider=”{dp}” height=”100%”>

</mx:DataGrid>

</mx:HDividedBox>

</mx:Application>

Listing 11.36 PropertyBagDemo.mxml

The point of interest in the PropertyBagDemo is the definition of the DataGridColumn:

<fx:DataGridColumn width=”150” preventRendererReuse=”true”

textAlign=”center” dataField=”columnValue” headerText=”Value”

wordWrap=”true” rendererIsEditor=”true”

itemRenderer=”{new UIClassFactory(function():* {return

controlPicker(dg.rendererData)})}”

/>

Notice the property preventRendererReuse. It’s a property that we are going to introduce to toggle certain default functionality of the DataGrid on and off. As mentioned earlier, the DataGrid main-

Advanced DataGridCHAPTER 11

Page 509: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

508 RIA WITH ADOBE FLEX AND JAVA

tains a pool of reusable renderers per column. This is done for performance reasons to avoid the proliferation of renderers for each row of the dataProviders collection. Since our use case – the property bag – doesn’t assume many hundreds of lines, we’ve turned this functionality off, forcing the DataGrid to dynamically create a renderer for each line anew.

Next, notice how itemRenderer isn’t assigned a ClassFactory, or even a String, but rather a function that calculates the generator for the UIClassFactory based on the data. At the end of the day we rely on the quite simple application function controlPicker(), but the ability to pass Function as an argument to the UIClassFactory is another implementation task for us to accomplish.

Last, we introduced an additional DataGrid property – rendererData – that captures the last item the IViewCursor passed through, assigning the data to the renderer.

Now let’s look at the implementation details. We’ll start with UIClassFactory. In the previous sec-tion we made the UIClassFactory constructor accept the String argument. Here we add a private variable factoryFunction and modify the constructor again to take the Function in addition to the ClassFactory and String:

private var factoryFunction : Function = null;

. . . .

function UIClassFactory( cf: * ) {

if ( cf is ClassFactory) {

wrappedClassFactory = ClassFactory(cf);

} else if (cf is String) {

var className:String = String(cf);

var clazz:Class = getDefinitionByName(className) as Class;

wrappedClassFactory = new ClassFactory(clazz);

} else if (cf is Function) {

factoryFunction = cf;

} else {

throw new Error(“Invalid argument for UIClassFactory constructor”);

}

if (wrappedClassFactory) {

wrappedClassFactory = new ClassFactory(Object);

}

}

Appropriate provisions have to be made in the newInstance() method. Namely, when it’s time to manufacture an object via factoryFunction we invoke the function with the call operator() and then make sure the newly made object has the same properties as other instances do:

public function newInstance():* {

var obj:*;

if (factoryFunction!=null) {

Advanced DataGridCHAPTER 11

Page 510: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 509

obj = factoryFunction();

// Now we have to do what regular ClassFactory does by itself

if (properties != null) {

for (var p:String in properties) {

obj[p] = properties[p];

}

}

} else

obj = wrappedClassFactory.newInstance();

obj.addEventListener(FlexEvent.DATA_CHANGE, onDataChange);

return obj;

}

The final code of UIClassFactory is in Listing 11.37:

// UIClassFactory.as

package com.theriabook.util

{

import mx.events.FlexEvent;

import mx.controls.dataGridClasses.DataGridColumn;

import mx.core.ClassFactory;

import mx.core.IFactory;

public class UIClassFactory implements IFactory

{

private var wrappedClassFactory : ClassFactory;

private var factoryFunction : Function = null;

public function set properties(v:Object):void {

wrappedClassFactory.properties = v;

}

public function get properties():* {

return wrappedClassFactory.properties ;

}

function UIClassFactory( cf: * ) {

if ( cf is ClassFactory) {

wrappedClassFactory = ClassFactory(cf);

} else if (cf is String) {

var className:String = String(cf);

var clazz:Class = getDefinitionByName(className) as Class;

wrappedClassFactory = new ClassFactory(clazz);

} else if (cf is Function) {

factoryFunction = cf;

Advanced DataGridCHAPTER 11

Page 511: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

510 RIA WITH ADOBE FLEX AND JAVA

} else {

throw new Error(“Invalid argument for UIClassFactory constructor”);

}

if (wrappedClassFactory) {

wrappedClassFactory = new ClassFactory(Object);

}

}

public function newInstance():* {

var obj:*;

if (factoryFunction!=null) {

obj = factoryFunction();

// Now we have to do what internal CF does itself

if (properties != null) {

for (var p:String in properties) {

obj[p] = properties[p];

}

}

} else

obj = wrappedClassFactory.newInstance();

obj.addEventListener(FlexEvent.DATA_CHANGE, onDataChange);

return obj;

}

private function onDataChange(event:FlexEvent):void{

var renderer:Object = event.currentTarget;

// In the default DataGridItemRenderer both data and listData are

[Bindable(“dataChange”)]

// We want to skip assinments to listData

if (renderer.data is mx.controls.dataGridClasses.DataGridColumn) return;

// Act only on ‘dynamic style’ columns

if (renderer.styleName && renderer.styleName.hasOwnProperty(“runtimeStyles”)) {

var runtimeStyles:Object = renderer.styleName[“runtimeStyles”];

for (var style:String in runtimeStyles) {

if ( runtimeStyles[style] is Function ) {

var functionObject : Function = runtimeStyles[style];

renderer.setStyle(style, functionObject(renderer.data));

}

else

renderer.setStyle(style, runtimeStyles[style]);

}

Advanced DataGridCHAPTER 11

Page 512: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 511

renderer.invalidateDisplayList();

}

}

}

}

Listing 11.37 UIClassFactory.as, final version

Now let’s look at the finalized code for our custom DataGridColumn, Listing 11.38. Notice the defi-nition of the preventRendererReuse variable, which will be used a bit later by the DataGrid class. We also modified the itemRenderer setter slightly. Instead of:

super.itemRenderer = new UIClassFactory(val as ClassFactory);

we do

super.itemRenderer = val is UIClassFactory?val:new UIClassFactory(val);

to avoid wrapping on top of wrapping, when the passed value is already an UIClassFactory.

Here’s the finalized DataGridColumn:

//DataGridColumn.as

package com.theriabook.controls.dataGridClasses{

import com.theriabook.util.UIClassFactory;

import mx.controls.dataGridClasses.DataGridColumn;

import mx.core.ClassFactory;

import mx.core.IFactory;

public class DataGridColumn extends mx.controls.dataGridClasses.DataGridColumn {

public var runtimeStyles:Object = null;

public var runtimeProperties:Object = null;

public var preventRendererReuse :Boolean = false;

public function set extendedProperties(val:Object) :void {

if (itemRenderer)

itemRenderer[“properties”] = val;

}

public function set formatString( fs :String ) : void{

formatData = fs;

}

public function set formatData( fd :Object ) : void{

FormattingManager.setFormat(this, fd);

Advanced DataGridCHAPTER 11

Page 513: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

512 RIA WITH ADOBE FLEX AND JAVA

EditingManager.setFormat(this, fd);

}

override public function set itemRenderer( val : IFactory ) : void {

//was super.itemRenderer = new UIClassFactory(val as ClassFactory);

super.itemRenderer = val is UIClassFactory?val:new UIClassFactory(val);

}

}

}

Listing 11.38 DataGridColumn.as, final version

The last class to be modified in this chapter is the DataGrid itself. We’ll introduce a new read-only property, rendererData, which gives us the latest value that has been scanned over by the iterator, a protected member of the mx.controls.listClasses.ListBase:

public function get rendererData() : Object {

var data:Object = null;

if (iterator) {

iterator.movePrevious();

data = iterator.current;

iterator.moveNext();

}

return data;

}

As you’ll remember, we created a column-level property, preventRendererReuse, to help us turn the default renderer pooling on and off. Now, in the DataGrid, we override the addToFreeItemRender-ers() method of DataGridBase class: we check that the column is our custom type and, if so, do not populate the corresponding freeItemRenderersTable (see Listing 11.39), thus leaving it empty at all times. As a reminder, our goal wasn’t so much to prevent reuse of renderers as to enforce dynamic re-creation on them on every line, since in our use case every line may need a different control:

override protected function addToFreeItemRenderers(item:IListItemRenderer):void

{

if (columnMap[item.name] &&

columnMap[item.name] is com.theriabook.controls.dataGridClasses.DataGridColumn

&&

columnMap[item.name].preventRendererReuse) {

delete rowMap[item.name];

if (columnMap[item.name]) {

var c:Object = columnMap[item.name];

//Commented out 3 lines of original addToFreeItemRenderers

Advanced DataGridCHAPTER 11

Page 514: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 513

//if (freeItemRenderersTable[c] == undefined)

// freeItemRenderersTable[c] = [];

//freeItemRenderersTable[c].push(item);

delete columnMap[item.name];

}

item.parent.removeChild(DisplayObject(item));

} else

super.addToFreeItemRenderers(item);

}

Listing 11.39 Overriding of DataGrid’s addToFreeItemRenderers() method

The finalized code of our custom DataGrid is in Listing 11.40:

//com.theriabook.controls.DataGrid

package com.theriabook.controls {

import mx.controls.DataGrid;

public class DataGrid extends mx.controls.DataGrid {

import com.theriabook.controls.dataGridClasses.DataGridColumn;

import com.theriabook.controls.Label;

import com.theriabook.util.UIClassFactory;

import flash.display.DisplayObject;

import mx.controls.Alert;

import mx.controls.listClasses.IListItemRenderer;

import mx.core.ClassFactory;

import mx.managers.CursorManager;

import mx.rpc.AbstractOperation;

import mx.rpc.events.*;

import mx.rpc.remoting.mxml.RemoteObject;

function DataGrid() {

super();

itemRenderer = new UIClassFactory(new ClassFactory(Label));

}

public function get rendererData() : Object {

var data:Object = null;

if (iterator) {

iterator.movePrevious();

data = iterator.current;

iterator.moveNext();

}

Advanced DataGridCHAPTER 11

Page 515: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

514 RIA WITH ADOBE FLEX AND JAVA

return data;

}

override protected function addToFreeItemRenderers(item:IListItemRenderer):void

{

if (columnMap[item.name] &&

columnMap[item.name] is com.theriabook.controls.dataGridClasses.DataGridColumn &&

columnMap[item.name].preventRendererReuse) {

delete rowMap[item.name];

if (columnMap[item.name]) {

var c:Object = columnMap[item.name];

delete columnMap[item.name];

}

item.parent.removeChild(DisplayObject(item));

} else

super.addToFreeItemRenderers(item);

}

public var destination:String=null, method : String = null;

public var autoFill : Boolean = true;

protected var ro:RemoteObject = null;

public function fill(... args): void {

if( ro==null ) {

if( destination==null || destination.length==0 )

throw new Error(“No destination specified”);

if( method==null || method.length==0 )

throw new Error(“No retrieveMethod specified”);

ro = new RemoteObject(destination);

ro.showBusyCursor = true;

ro.concurrency = “last”;

ro.addEventListener(ResultEvent.RESULT, ro_onResult);

ro.addEventListener(FaultEvent.FAULT, ro_onFault);

}

var operation:AbstractOperation = ro.getOperation(method);

operation.arguments = args;

operation.send();

}

private function ro_onFault(evt:FaultEvent):void {

CursorManager.removeBusyCursor();

Advanced DataGridCHAPTER 11

Page 516: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 515

Alert.show(“Failed retrieving data: “+evt.message, “[DestinationAwareDataGrid]” +

id);

}

private function ro_onResult(evt:ResultEvent):void {

CursorManager.removeBusyCursor();

if (evt.result.length != 0)

dataProvider = evt.result;

}

}

}

Listing 11.40 DataGrid.as, the final version

SummaryThis chapter concentrates on the automation of DataGrid programming through what we call the data-driven approach. Along these lines we’ve extended the concept of destination-aware controls to the DataGrid, customized controls like CheckBox, created new ones like RadioButtonBox and NumericMask, and introduced computed styles and computed editors. The turnpoint techniques introduced in this chapter have been the custom DataGridColumn and UIClassFactory.

If you’ve made it this far, you’ll probably change the way you’ve dealt with DataGrid. Most impor-tantly, your work with DataGrid will become more productive and enjoyable.

Endnotes

1. As a reminder, all custom controls we hereafter add to theriabook.swc, extending the manifest and check-

ing the appropriate checkboxes under project properties/Flex Library Build Path-/Classes.

2. We omitted replicating the DataGridColumn code here, but please count this as the second version.

3. The content of FormattingManager may be modified on a per-project basis. Alternatively, you can come

up with a strategy that allows dynamic selection and loading the proper FormattingManager.

4. In fact, a quick peek in the generated folder can reveal that inline components references cause genera-

tion of three files.

5. The bigger underlying topic is the role of undefined in the data collection edited via the DataGrid. As a

use case let’s consider the DataGrid exposing combined data from two different arrays, which share all

but a few attribites. The DataGrid will show missing attributes – undefined – as empty cells. Once you

click on such a cell and leave it, DataGrid will attempt to dynamically create the property on the underly-

ing ActionScript object – that alone would fail on the sealed object – and then assign null to it. That might

Advanced DataGridCHAPTER 11

Page 517: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

516 RIA WITH ADOBE FLEX AND JAVA

not be a desired behavior.

6. Theoretically, you could go as far as to start assigning item renderers even to the text fields. In that case

there would be no need to declare DataGrid editable as long as your item renderers take care of changes

in the underlying data collection.

7. We could have gone further and upgraded <fx:options> to <fx:dataProvider> like the ButtonBar and Link-

Bar controls.

8. You may want to compare this situation with the HBox use merely for centering the CheckBox, which we

advocated against in the previous section. Back there, using an extra container was a luxury, expensive

as luxury items often are. Here we face the alternative of reusing the container versus developing a new

implementation for the whole set of interfaces: IFlexDisplayObject, ILayoutManagerClient, ISimple-

StyleClient, IUIComponent.

9. We can hear the inquisitive reader wondering about the performance implications of this solution, since

we’ve replaced the ultra-light UITextField with a heavier UIComponent-Label. The answer is that given

that the DataGrid recycles item renderers – by maintaining a pool of them to cover the visible portion of

the column – possible damage is limited by the number of visible rows. You may conduct your own stress

tests, although ours haven’t shown any noticeable difference.

10. As a reminder, to cancel the default event behavior with preventDefault() the event should be cancelable

in the first place. You can check the latter via cancelable property of the event object.

11. That would be a good decision after all, since mx.validators.NumberValidator also looks for this file.

12. Again, all object instances manufactured by a ClassFactory share the same initial values of their properties: the ClassFactory method newInstance() doesn’t take parameters and all future values of the properties are preset upfront as attributes of the dedicated properties object on the Class-Factory itself.

13. The method getDefinitionByName() will throw a ReferenceError in case the class image isn’t found in the

application or RSL SWFs.

14. When it comes to dynamically loaded libraries, during development we recommend Debug as…versus

Run as…to avoid Alerts, one per loaded RSL that come during the application startup. Please be assured

that these Alerts come only on developers’ machines that have a debugging version of the Flash Player

installed; your users are not going to see them.

15. This is exactly what is happening behind the scenes of FlexBI, a reporting/Business Intelligence product

and solution created by FarataSystems. FlexBI allows business users to modify layout, sorting, grouping

and formulas of their reports on the fly and persists these settings in the database; see http://www.myflex.

org for more information.

Advanced DataGridCHAPTER 11

Page 518: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 519

Logging and Debugging Flex and Java Applications

12CHAPTER

Page 519: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

520 RIA WITH ADOBE FLEX AND JAVA

No, this chapter is not about how to use the Flex Builder debugger. In this chapter we’ll discuss the techniques of logging and debugging mixed Flex/Java projects with or without Flex Builder.

Flex comes with a set of logging facilities and some prebuilt loggers. It also includes a blueprint of client logging and implementation utilizing tracing, which is built into the debug version of the Flash Player. We’ll start by covering the standard Flex logging facilities and use cases.

LoggingTypically, you use logging to solve the cases that can not be solved using the debugger. For example, if you try to validate the code that implements drag-and-drop, the Flex debugger will keep inter-rupting the program event flow and make debugging impossible.

Another use case for logging would be the need to test code on the communication protocol level, i.e., you’d like to know what’s happening under the hood during RPC and FDS calls. In the case of errors, developers often don’t even have error handling code and can not easily identify the prob-lem.

Also, there may be issues on the server, and they are not necessarily reported in full to the client.

Flex supports both client- and server-side logging, and it allows you to trace client/server messages both on the client and the server. Let’s take closer look.

Configuring Web Application (Server) Logging On the server side, you need logging in the following cases:

• You might want to log the methods being called as well as the data exchange between the cli-ent and server.

• You’re using the server-side Flex compiler and need to log errors for the developers or support personnel.

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Logging and Debugging Flex and Java Applications

Page 520: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 521

Applications that use Flex Data Services can utilize an extensive set of prebuilt logging features in the communication and execution packages. You can trace the inner workings of the system classes by using a detailed hierarchy of loggers. It’s a bit different from Java logging where you create loggers based on the package/class hierarchy. Adobe uses a logical hierarchy of the loggers that’s not directly related to the package structure, but you can select the layer with a pattern or particular end node that will help in logging the types of operations you are interested in. The logi-cal categories for logging are listed below: Configuration,

DataService.[* |General | Hibernate | Transaction],

Endpoint.[* | AMF | HTTP | RTMP | Deserialization |General],

Message.*, Message.Command.[* | subscribe | unsubscribe | poll | poll_interval |

client_sync | server_ping | client_ping | cluster_request | login | logout],

Message.General

Message.Data.[* | create | fill get | update | delete | batched | multi_batch |

transacted | page | count | get_or_create | create_and_sequence | get_sequence_id

| association_add | association_remove | fillids | refresh_fill | update_collec-

tion],

Message.RPC , MessageSelector ,

Resource ,

Security

Service.[* | Cluster | HTTP | Message Message.JMS | Remoting]

Brackets represent a choice of a pattern; for example, you can specify the logging category Data-Service.General. The code snippet below has some samples of using these patterns in the section filters.

You can log into a system console (Java’s System.out.println) or use regular logging configured with-in the Web application servlet context (context.log) by specifying the class attribute of the target file in the configuration. Below are some typical settings in /WEB-INF/flex/services-config.xml:

<logging>

<!-- You may also use class=”flex.messaging.log.ServletLogTarget” -->

<target class=”flex.messaging.log.ConsoleTarget” level=”Error”>

<properties>

<prefix>[Flex] </prefix>

<includeDate>false</includeDate>

<includeTime>true</includeTime>

<includeLevel>false</includeLevel>

<includeCategory>true</includeCategory>

</properties>

<filters>

<pattern>Endpoint.*</pattern>

<pattern>Service.*</pattern>

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 521: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

522 RIA WITH ADOBE FLEX AND JAVA

<pattern>Configuration</pattern>

</filters>

</target>

</logging>

The logging-level attribute can have one of the following values: all, debug, info, warn, error, or none.

You can also control the output of the server-side MXML compiler. You can log messages sent to the Web application server’s logging mechanism by using the log-compiler-errors property in the /WEB-INF/flex/flex-webtier-config.xml file. The Web application logger does not log runtime mes-sages, but only logs server-side compiler messages.

To log compiler errors to a file or console, you must set the value of the tag <production-mode> to false and <log-compiler-errors> to true.

<production-mode>false</production-mode>

<debugging>

<log-compiler-errors>true</log-compiler-errors>

</debugging>

The location, number of backups and log file sizes are specified in the logging

block of the flex-webtier-config.xml file.

<logging>

<level>info</level>

<console><enable>true</enable></console>

<file>

<enable>true</enable><file-name>/WEB-INF/flex/logs/flex.log</file-name>

<maximum-size>200KB</maximum-size>

<maximum-backups>3</maximum-backups>

</file>

</logging>

When switching to production mode, always switch the logging level to info or errors as logging tends to be expensive since it accesses the file system and performs data serialization. Don’t forget that verification of the logging levels in the code is target-oriented versus a typical Java package/class granularity, and this presents additional performance challenges. The same applies to the logging organization on the client. We will illustrate this in detail in the next section.

Client-Side LoggingThere are two commonly used techniques for logging the Flex programs. First, the debugging version of the player provides a built-in global function trace(). Its output goes to the file flashlog.txt that’s

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 522: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 523

located in the same folder as mm.cfg file. Please refer to the product documentation to find the name of the folder in your OS (see http://livedocs.macromedia.com/flex/2/docs/wwhelp/wwhimpl/com-mon/html/wwhelp.htm?context=LiveDocs_Parts&file=00001528.html). If you don’t file mm.cfg in this directory, create one with the following content:

ErrorReportingEnable=1TraceOutputFileEnable=1TraceOutputFileName=c:\flexLog.txtMaxWarnings=50.

In mm.cfg, you must set TraceOutputFileEnable to 1, and you can set a custom value for TraceOut-putFileName. More important, trace() automatically goes to the console panel of the Flex Builder. It’s a good way to start tracing, similar to System.out.println in Java. The only difference is that the latter works in both debug and runtime modes, while the function trace() works only in the debug mode.

The second approach is to use the Logging API, which allows developers to control logging based on two parameters – levels and categories – in a more convenient way. It also allows developers to create and designate targets – classes responsible for writing the logging messages to some output media, which can be local resources, server logs/messaging, or even a remote workstation used for debugging/support.

Let’s take a more detailed look at the Flex logging API.

Using the Logging APITo start logging messages you need two objects: a logger and a log target. The class mx.logging.Log out of the box provides basic logging functionality with a pre-built implementation of the logger interface ILogger with error(), info(), warn(), and debug() methods. Application developers just need to define the logger and call the messaging methods on its behalf:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”>

<mx:Script>

import mx.logging.Log;

import mx.logging.ILogger;

private static var logger:ILogger=Log.getLogger(“MyCategory.subcotegory.morede-

tails”);

</mx:Script>

<mx:Button label=”LogDebug” click=”logger.debug(‘ Debug Message Submitted’)”/>

</mx:Application>

In the code snippet above, MyCategory is just a logical name that is not linked to any real class name. This code is quite generic and does not specify where to output messages. Flex includes two standard targets: TraceTarget and MiniDebugTarget. TraceTarget uses trace to write out the messages and re-quires you to use a debugging version of the player and to do the trace setup described earlier. MiniDe-

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 523: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

524 RIA WITH ADOBE FLEX AND JAVA

bugTarget uses LocalConnection to transfer messages to other Flash/Flex applications. Here is a sam-ple of setting TraceTarget with some formatting to include time and level along with the message:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

creationComplete=”setTarget()” >

<mx:Script>

import mx.logging.targets.TraceTarget;

import mx.logging.Log;

import mx.logging.ILogger;

private static var logger:ILogger = Log.getLogger(“MyCategory”);

private function setTarget() : void {

var target : TraceTarget = new TraceTarget();

target.includeTime = target.includeLevel = true;

Log.addTarget( target );

};

</mx:Script>

<mx:Button label=”LogDebug” click=”logger.debug(‘Debug Message Submitted’)”/>

</mx:Application>

While TraceTarget is good for some cases, it’s important that a developer has the option to output to other services such as a server log, remote workstations, or OS consoles. Let’s create other targets.

Server Log TargetHere we’ll create a simple target to output messages to the server using Log4J, a popular Java tool. The Java portion implementing logging is very small:

package com.theriabook.logging;

import org.apache.log4j.Logger;

import org.apache.log4j.Level;

public class ServerFlexLogger{

static public void log(String levelStr, String message) {

logger.log(Level.toLevel(levelStr), message);

}

static Logger logger;

static {

logger = Logger.getLogger(“FlexLogger”);

logger.setLevel(Level.ALL);

}

}

Listing 12.1 ServerFlexLogger.java

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 524: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 525

Once compiled, deployed, and registered with Flex Remoting, the server-side Java class ServerFlexLogger is ready to be called from the client-side ActionScript class ServerTar-get:

package com.theriabook.util.logging {

import mx.logging.LogEvent;

import mx.rpc.remoting.RemoteObject;

import mx.rpc.events.FaultEvent;

import mx.logging.AbstractTarget;

import mx.logging.LogEventLevel;

// Outputs logs to the web server.

public class ServerTarget extends AbstractTarget {

private var _destination: String;

private var ro: RemoteObject;

public function ServerTarget(destination: String) {

super();

this.destination=destination;

}

override public function logEvent(event:LogEvent):void

{

var message:String = event.message;

var category:String = ILogger(event.target).category;

var level:int= event.level;

if ((category == “mx.messaging.Producer” ||

category == “mx.messaging.Channel” ||

category.substr(0,7) == “mx.rpc.” ) && level < LogEventLevel.ERROR)

return;

ro.log(LogEvent.getLevelString(level),message);

}

public function get destination(): String {

return _destination;

}

public function set destination(val: String): void {

_destination=val;

ro = new RemoteObject();

ro.destination=_destination;

ro.addEventListener(FaultEvent.FAULT,ro_onFault);

}

protected function ro_onFault(evt:FaultEvent):void {

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 525: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

526 RIA WITH ADOBE FLEX AND JAVA

trace(evt.fault.faultString+” --- “+evt.message.destination);

}

}

}

Listing 12.2 ServerTarget.as

We had to limit the logging of the messages from the mx.messaging and mx.rpc packages to avoid getting into an endless loop. The reason is that mx.messaging and mx.rpc packages contain their own logging messages about internal communication processes that allow application developers to debug the flow of server communications, and these messages can pile up pretty fast.

Finally, let’s create a Flex application that registers the ServerTarget:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

creationComplete=”setTarget()” >

<mx:Script>

import mx.logging.Log;

import mx.logging.ILogger;

private static var logger:ILogger = Log.getLogger(“MyCategory”);

private function setTarget() : void {

var target : ServerTarget = new ServerTarget(“logging”);

Log.addTarget( target );

};

</mx:Script>

<mx:Button label=”LogDebug” click=”logger.debug(‘Debug Message Submitted’)”/>

</mx:Application>

Listing 12.3 A Sample Flex Application with Logging

Server Log Target Using Plain HTTPListing 12.4 will use a raw socket communication on the client and a servlet on the server to remove the trap of falling into the cyclic logging of mx.rpc and mx.messaging.

package {

import mx.logging.AbstractTarget;

import flash.net.URLRequest;

import flash.net.sendToURL;

import mx.logging.LogEvent;

import mx.logging.ILogger;

import flash.net.URLVariables;

public class SocketTarget extends AbstractTarget {

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 526: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 527

public function SocketTarget(destination: String) {

super();

this.destination=destination;

}

private var _destination: String;

private var req: URLRequest;

override public function logEvent(event:LogEvent):void {

var msg : URLVariables = new URLVariables();

msg.level=LogEvent.getLevelString(event.level);

msg.message=event.message;

msg.category = ILogger (event.target).category;

req.data= msg;

sendToURL( req);

}

public function get destination(): String {

return _destination;

}

public function set destination(val: String): void {

_destination=val;

req = new URLRequest(val);

req.method = «POST»;

}

}

}

Listing 12.4 SocketTarget.as

In the application object we need to instantiate SocketTarget with the URL “logging”.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

creationComplete=”setTarget()” >

<mx:Script>

import mx.logging.Log;

import mx.logging.ILogger;

private static var logger:ILogger = Log.getLogger(“MyCategory”);

private function setTarget() : void {

var target : SocketTarget = new SocketTarget(“logging”);

Log.addTarget( target );

};

</mx:Script>

<mx:Button label=”LogDebug” click=”logger.debug(‘Debug Message Submitted’)”/>

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 527: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

528 RIA WITH ADOBE FLEX AND JAVA

</mx:Application>

Listing 12.5 Logging with SocketTarget

We will also need to convert the Java class from Listing 12.1 into a servlet:

package com.theriabook.logging;

import org.apache.log4j.Logger;

import org.apache.log4j.Level;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public class FlexLoggerServlet extends HttpServlet {

protected void doPost(final HttpServletRequest request, final HttpServletResponse re-

sponse) {

final String levelStr = request.getParameter(“level”);

final String message = request.getParameter(“message”);

logger.log(Level.toLevel(levelStr), message);

}

static Logger logger;

static

{

logger = Logger.getLogger(“MyFlexLogger”);

logger.setLevel(Level.ALL);

}

}

Listing 12.6 FlexLoggerServlet.java

You might want to include the client’s IP address and the user information in the log in order to distinguish the output from multiple clients.

The Client-Side TargetFinally, we’ll create a target to communicate log messages to a standalone Flash application using the class LocalConnection. Listing 12.7 provides the implementation of the LocalConnectionTar-get class:

package {

import mx.logging.LogEvent;

import mx.logging.AbstractTarget;

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 528: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 529

import mx.logging.Log;

import flash.events.StatusEvent;

import flash.net.LocalConnection;

import mx.controls.Alert;

import mx.logging.LogEventLevel;

import mx.logging.ILogger;

public class LocalConnectionTarget extends AbstractTarget {

private var _destination: String;

private var preventSubsequentErrors: Boolean = false;

private var conn:LocalConnection;

public function LocalConnectionTarget (destination: String) {

super();

this.destination=destination;

}

override public function logEvent(event:LogEvent):void {

var level:String = LogEvent.getLevelString(event.level);

var message:String = event.message;

var category : String= ILogger (event.target).category;

conn.send(destination, «lcHandler», message,category,level,new Date());

}

private function onStatus(event:StatusEvent):void {

switch (event.level) {

case «error»:

if (!preventSubsequentErrors){

Alert.show(«LocalConnection.send() failed\n»+

«destination: «+destination);

}

preventSubsequentErrors =true;

}

}

public function get destination(): String {

return _destination;

}

public function set destination(val: String): void {

_destination=val;

preventSubsequentErrors=false;

conn = new LocalConnection();

conn.addEventListener(StatusEvent.STATUS, onStatus);

}

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 529: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

530 RIA WITH ADOBE FLEX AND JAVA

public function releaseOutput ():void {

}

}

}

Listing 12.7 LocalConnectionTarget.as

The class LocalConnectionTarget can be used to deliver logging messages to any client application that either embeds a Flash component or implements LocalConnection with a native implementa-tion using low-level programming languages. The code that comes with the book includes a free native implementation for the Windows platform of a console application that allows you to output the log to the display monitor, the files, and the Windows native debugging log (see http://www.microsoft.com/technet/sysinternals/Miscellaneous/DebugView.mspx).

Creating a Tracing Console for LocalConnectionTo output the logging messages, we’ll need some display panels. We’ve created a simple DataGrid-based component LogsPanel that will display new log messages upon their arrival (see Figure 12.1).

Figure 12.1 Displaying local log messages using logspanel

Listing 12.8 provides sample code to direct the log messages to our panel:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”

applicationComplete=”onCreationComplete()” xmlns:fx=” *” >

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 530: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 531

<mx:Script>

<![CDATA[

import flash.net.LocalConnection;

import mx.controls.Alert;

private var conn:LocalConnection ;

public function onCreationComplete(): void {

conn = new LocalConnection();

conn.client = this;

conn.allowDomain(‘*’);

try {

// _LocalPanelLog is the connection name used by both the calling program

// and connection console

conn.connect(“_LocalPanelLog”);

conn.addEventListener(AsyncErrorEvent.ASYNC_ERROR, showError);

} catch (error:ArgumentError) {

Alert.show(“Can’t connect. The connection name is already being used by another SWF” +

error.message);

}

}

private function showError(evt: AsyncErrorEvent):void {Alert.show(evt.text )};

public function lcHandler(message:String, category:String, level:String,

time:Date):void {

log.addLog(message,category,level,time);

}

]]>

</mx:Script>

<fx:LoggingGrid id=”log” width=”100%” height=”100%”/>

</mx:Application>

Listing 12.8 A Sample Application that Logs to LogsPanel

The LoggingGrid component (see Listing 12.8) is inherited from the DataGrid:

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

<mx:DataGrid xmlns=”*” xmlns:mx=”http://www.adobe.com/2006/mxml” dataProvid-

er=”{[]}” >

<mx:columns>

<mx:DataGridColumn id=”levelColumn” dataField=”level” headerText=”Level” />

<mx:DataGridColumn dataField=”date” headerText=”Time” />

<mx:DataGridColumn dataField=”category” headerText=”Category” />

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 531: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

532 RIA WITH ADOBE FLEX AND JAVA

<mx:DataGridColumn dataField=”message” headerText=”Message Text” />

</mx:columns>

<mx:Script>

<![CDATA[

public function addLog(message:String, category:String, level:String,

date:Date):void {

this.dataProvider.addItem({message:message, category:category, level:level,

date:date});

}

]]>

</mx:Script>

</mx:DataGrid>

The RIA Aspect of Flex LoggingThe developer might want to change the settings of the logging while debugging the application. During the lifespan of the application, you may need to be able to redirect the log messages to a dif-ferent target(s). Such functionality can be implemented by developing a LoggingManager, which is a UI component that allows interactive control levels, categories, and targets.

A logging manager should support the following functionality on the client side:

• Automatically discover and list logging categories, and allow change in the logging level for each category or package.

• List registered targets/destinations, and set a default logging level and other common param-eters.

• Provide the client-side persistence of logging settings.

Figure 12.2 shows a sample logger panel that meets all of the above requirements.

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 532: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 533

Figure 12.2 A sample logging manager

Figure 12.3 shows an extension to our LoggingGrid component that provides client-side filtering, searching, and other custom logging functions.

Figure 12.3 A sample extension of a LoggingGrid component

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 533: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

534 RIA WITH ADOBE FLEX AND JAVA

There are a number of freeware and commercial components that allow you to direct and show logging data in Eclipse, a Web browser, and a standalone application. You can find a free open source tracing console called XPanel at the following URL: http://www.faratasystems.com/?page_id=45.

DebuggingIn this section we’ll go through some scenarios of debugging Java server-side code deployed in one of the Java EE application servers. We’ll discuss two modes:• Remote Debugging: When you can connect to a running application server through a dedi-

cated port and debug your Java application• Internal Debugging: When you start your application server inside the Eclipse IDE and then

debug the code according to set breakpoints

Remote DebuggingWe’ll illustrate remote debugging, assuming that you’ve installed Flex Data Services with the inte-grated JRun server, even though you can apply the same techniques with other Java EE application servers. The plan is to engage the Java Platform Debugger Architecture (JPDA), which allows you to debug applications (JRun in our case) running in a JVM. Besides running the server, JVM will also listen to a particular port, and the Flex application that we’d like to debug will attach to this port for debugging the server-side Java code.

Usually, you start the JRun server by opening the command window and typing the following (it’s the Windows version):

C:\fds2\jrun4\bin>jrun -start default

JRun uses settings from the configuration file jvm.config located in the bin directory of JRun. Our goal is to start JRun in the debug mode, and to do this we’ll modify the file jvm.config.

To load a reference implementation of JPDA, its manual (http://java.sun.com/j2se/1.5.0/docs/guide/jpda/conninv.html) suggests using the JVM option agentlib:jdwp for Java 5.0, and with the pre-5.0 JVM you should use –Xdebug to enable debugging and –Xrunjdwp. For example, the java.args line may look as follows:

java.args=-Xms32m -Xmx384m -Dsun.io.useCanonCaches=false -Xdebug -Xrunjdwp:

transport=dt_socket,address=8000,server=y,suspend=n

In the line above, -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n instructs the JVM to use a socket as a transport and listen for a connection on port 8000. The server doesn’t need to be suspended while waiting for a debugger application to connect.

After your JRun instance is started in the debug mode, you need to create the Eclipse debug con-

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 534: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 535

figuration for this remote Java application. Select the menus Run | Debug and right-click the option Remote Java Application (see Figure 12.4).

Figure 12.4 Creating the new debug configuration

In the popup window enter the name of the Eclipse Java project you’d like to debug that contains code deployed under JRun, as in Figure 12.5. Please note that the port number should match the one specified in JRun’s file jvm.config. As an example, we are using the Java Order Entry application from Chapter 7.

Figure 12.5 Mapping the Eclipse Java project to JVM’s JPDA port

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 535: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

536 RIA WITH ADOBE FLEX AND JAVA

This is pretty much it. Now you can set your breakpoints in the Java source code, which has to cor-respond to the code deployed in JRun.

Figures 12.6 and 12.7 show the snapshots of the Eclipse debugger perspective, illustrating break-points in both ActionScript and Java source code.

Figure 12.6 At the breakpoint in the ActionScript code

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 536: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 537

Figure 12.7 At the breakpoint in the Java code

Using Eclipse WTP for Debugging Java EE ServersOur next goal is to learn how to start your Java EE application server inside Eclipse. This time we’ll use Apache Tomcat 5.5, which is available for download at http://tomcat.apache.org/download-55.cgi.

If we are able to start the server inside Eclipse, the rest is easy: just set your breakpoints in the server-side Java code and off you go.

There could be different ways of doing this – in some cases it’s just a matter of configuring your server’s startup runtime in Eclipse. An alternative way is to find and install the Eclipse plugin for your Java EE server.

A more generic way is to use an open source Eclipse project called the Web Tools Platform (WTP), available from the Eclipse Foundation at the following Web site: http://www.eclipse.org/webtools/.

While Eclipse WTP adds various useful tools for the development of Java EE and Web applications, we’ll just explore how it can help us debug Java code deployed in application servers.

The easiest way to get WTP is to download its all-in-one zip file from http://download.eclipse.org/webtools/downloads/. It contains the Eclipse IDE with all required plugins. At the time of this writ-ing, WTP 1.5.x is meant for Eclipse 3.2 and WTP 1.0.x works with Eclipse 3.1.

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 537: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

538 RIA WITH ADOBE FLEX AND JAVA

To install this version of Eclipse with preconfigured WTP, simply unzip the contents of this file on your disk drive. Reinstall the Flex Builder plugin and start Eclipse, pointing at the workspace with your Flex projects.

Configuring TomcatAfter installing WTP, you’ll find a new perspective called J2EE. Open it, go to the menu Windows | Preferences, and you’ll find some new items there – one of which is called Server. Let’s add Tomcat as our default server by selecting Installed Runtimes under the Server item. After pressing the but-ton Add you’ll see a popup window listing a number of Java EE servers such as WebLogic or JBoss. If you don’t see the one you’re looking for (i.e., IBM’s WebSphere), click on the “Don’t see your server listed? Click Here” link shown in Figure 12.8.

Figure 12.8 Adding a new server Eclipse

Select Apache Tomcat 5.5, and specify its installation directory as in Figure 12.9.

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 538: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 539

Figure 12.9 Configuring Tomcat as a default server

Create a new Dynamic Web project called Portfolio_Java, where Tomcat is a target (menus File | New Project | Web | Dynamic Web project).

We’ll ask WTP to create a JavaServer Page file HelloWorld.jsp to pay respect to Hello World and ensure that we can debug any Java Web application. Just imagine that we are not debugging Hel-loWorld, but the Order Entry from Chapter 7, or TickerFeed.java from Chapter 5.

Open J2EE perspective and right-click on the WebContent under Portfolio_Java project. Select the menus New | JSP, enter the name HelloWorld, and the boilerplate JSP code will be created in a sec-ond.

We’ll just add one line <% out.print(“Hello World!!”); %> between the HTML <body> and </body> tags and set a breakpoint on this line (see Figure 12.10).

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 539: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

540 RIA WITH ADOBE FLEX AND JAVA

Figure 12.10 HelloWorld.jsp

Finally, let’s start HelloWorld.jsp under Tomcat in the debug mode: right-click on HelloWorld.jsp and select the options Debug As | Debug on Server. These actions will start Tomcat inside Eclipse, and it’ll open the debugger perspective as soon as it reaches our breakpoint. The screenshot from Figure 12.11 looks familiar, doesn’t it?

Figure 12.11 Debugging HelloWorld.jsp under the Tomcat server

If you just want to start the server without running any Web application, open the server’s view in the J2EE perspective, right-click on the server, and press the Start button.

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 540: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 541

Figure 12.12 Starting the Java EE Server in Eclipse WTP

In this section we’ve been using Apache Tomcat, but the same debugging techniques work for other Java EE servers as well.

Deploying a Web Application LocallyYour choice of debugging techniques depends on the development environment your team works in. A typical project consists of Flex and Java portions, and the Web application is being built and deployed using a common build script (i.e., Ant build.xml). This build may produce just one .war file that is deployed in a Java EE application server. What if you are experiencing errors in your Flex portion that did not surface prior to the final Flex-Java Ant build?

In this case, install and run the Java application server on your development machine, unzip the .war file into a separate directory, and deploy it in the exploded form from this directory. Make sure that the Flex Builder compiler’s options of your project include the option services pointing at the services-config.xml.

Note: This solution will work if your Flex Builder project type is Flex Data Services.

SummaryIt’s hard to overestimate the importance of debugging and logging in distributed Web applica-tions.

In this chapter we’ve shown different tools and techniques that can help you to prevent or localize and fix development and production problems in Flex/Java applications.

Logging and Debugging Flex and Java ApplicationsCHAPTER 12

Page 541: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 543

Building a SlideShow Application

By Ben Stucki

13CHAPTER

Page 542: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

544 RIA WITH ADOBE FLEX AND JAVA

Figure 13.1 The application’s design state

Like most RIAs, this application is going to include a number of custom components that are used together to create a unique experience. These components include the thumbnail scroller shown in Figure 13.1 as well as the SlideShow component that we’ll be developing in a moment. We’re also go-ing to cover timer events, working with the HTTPService control and creating rich user interfaces with ActionScript 3. We’ve got a lot to go over, so let’s get started by designing the SlideShow component.

Developing the SlideShow ComponentThe slideshow component encapsulates all the functionality needed to play and navigate a slide-show. Start by creating a new file named SlideShow.mxml in your component directory of choice. In this book we’re keeping user interface components at the relative path com/theriabook/controls, so the relative path from the application to this MXML file would be com/theriabook/controls/Slide-Show.mxml for me. In its simplest form the SlideShow component is just an Image inside a Canvas as shown in Listing 13.1.

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

<mx:Canvas xmlns:mx=”http://www.adobe.com/2006/mxml”

horizontalScrollPolicy=”off” verticalScrollPolicy=”off” verticalCenter=”0” background-

Color=”#000000” borderColor=”#ffffff” borderStyle=”solid”

borderThickness=”5”>

<mx:Image id=”image” horizontalCenter=”0” verticalCenter=”0”/>

</mx:Canvas>

Listing 13.1 The first version of the SlideShow MXML component

In this chapter we’ll be building a narrated slideshow application in Flex 2 that will use the popular Flickr (www.flickr.com) Web Services to retrieve the photos. Users will also be able to input a cus-tom URL as the photo or audio location and have a few extra settings and image effects that can be applied at runtime. When users are done creating slideshows, the application will provide them with a URL that others can use to view the result.

This application may be handy for Flickr users who want to share their personal photos with friends and family in a slideshow format, but it could be used for a number of business purposes as well. For example, a realty company may want to display photos of a property narrated by the local real-tor, or a car dealership may want to do the same for its cars. Some businesses may even use the application to give presentations similar to those Microsoft PowerPoint would provide.

No matter what the end result is, we want users to experience RIAs at their best. We’ll make the ap-plication so that users spend no more time creating a slideshow than they would spend watching it. Hopefully, with correct planning, you’ll spend no more time developing it than you would spend reading this chapter. Fortunately, Flex 2 makes both of these goals possible. Let’s take a look at what we’ll be building.

Application OverviewThe application will have two primary states: design and preview. We’ll start by developing a few components of the preview portion of the application. It will contain all the functionality needed to view and navigate a slideshow, including the timing control, audio playback, image effects process-ing, and a thumbnail preview of each image for navigation. The design state will let users explore photos from flickr.com, add and remove photos from the slideshow, include audio, and set the tim-ing and effects for each photo (see Figure 13.1).

Building a SlideShow ApplicationCHAPTER 13

Building a SlideShow Application

Page 543: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 545

Figure 13.1 The application’s design state

Like most RIAs, this application is going to include a number of custom components that are used together to create a unique experience. These components include the thumbnail scroller shown in Figure 13.1 as well as the SlideShow component that we’ll be developing in a moment. We’re also go-ing to cover timer events, working with the HTTPService control and creating rich user interfaces with ActionScript 3. We’ve got a lot to go over, so let’s get started by designing the SlideShow component.

Developing the SlideShow ComponentThe slideshow component encapsulates all the functionality needed to play and navigate a slide-show. Start by creating a new file named SlideShow.mxml in your component directory of choice. In this book we’re keeping user interface components at the relative path com/theriabook/controls, so the relative path from the application to this MXML file would be com/theriabook/controls/Slide-Show.mxml for me. In its simplest form the SlideShow component is just an Image inside a Canvas as shown in Listing 13.1.

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

<mx:Canvas xmlns:mx=”http://www.adobe.com/2006/mxml”

horizontalScrollPolicy=”off” verticalScrollPolicy=”off” verticalCenter=”0” background-

Color=”#000000” borderColor=”#ffffff” borderStyle=”solid”

borderThickness=”5”>

<mx:Image id=”image” horizontalCenter=”0” verticalCenter=”0”/>

</mx:Canvas>

Listing 13.1 The first version of the SlideShow MXML component

In this chapter we’ll be building a narrated slideshow application in Flex 2 that will use the popular Flickr (www.flickr.com) Web Services to retrieve the photos. Users will also be able to input a cus-tom URL as the photo or audio location and have a few extra settings and image effects that can be applied at runtime. When users are done creating slideshows, the application will provide them with a URL that others can use to view the result.

This application may be handy for Flickr users who want to share their personal photos with friends and family in a slideshow format, but it could be used for a number of business purposes as well. For example, a realty company may want to display photos of a property narrated by the local real-tor, or a car dealership may want to do the same for its cars. Some businesses may even use the application to give presentations similar to those Microsoft PowerPoint would provide.

No matter what the end result is, we want users to experience RIAs at their best. We’ll make the ap-plication so that users spend no more time creating a slideshow than they would spend watching it. Hopefully, with correct planning, you’ll spend no more time developing it than you would spend reading this chapter. Fortunately, Flex 2 makes both of these goals possible. Let’s take a look at what we’ll be building.

Application OverviewThe application will have two primary states: design and preview. We’ll start by developing a few components of the preview portion of the application. It will contain all the functionality needed to view and navigate a slideshow, including the timing control, audio playback, image effects process-ing, and a thumbnail preview of each image for navigation. The design state will let users explore photos from flickr.com, add and remove photos from the slideshow, include audio, and set the tim-ing and effects for each photo (see Figure 13.1).

Building a SlideShow ApplicationCHAPTER 13

Building a SlideShow Application

Page 544: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

546 RIA WITH ADOBE FLEX AND JAVA

Notice that I’ve set the horizontalScrollPolicy and verticalScrollPolicy attributes to off on the Can-vas and have given the Image an ID of image. We’re going to be adding some ActionScript to this component later, so it’s important that we can modify the image through code without creating unnecessary scrollbars. I’ve also included some styling attributes with this component to make it look a little more SlideShow-like. These attributes are available because they are part of the Canvas component, which SlideShow is inheriting, but they can be overridden when the component is included in an application.

To test our component we’ll have to create an application. In your application directory, create a new file named SlideShowPlayer1.mxml. Simply declare your component’s namespace and use the filename as the MXML element as shown in Listing 13.2.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=”com.theriabook.controls.*” layout=”absolute”>

<lib:SlideShow width=”100%” height=”100%” horizontalCenter=”0” />

</mx:Application>

Listing 13.2 Including an MXML component in an application

Compile and run your application and you should see a large blank Canvas (see Figure 13.2) with the styling attributes you’ve set in the component.

Figure 13.2 Running the SlideShow1.mxml

We’re going to add some ActionScript to the application and SlideShow component in a while to get our images working, but first we need to take a look at how slideshow data is represented.

Building a SlideShow ApplicationCHAPTER 13

Page 545: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 547

Loading SlideShow DataFor this application a slideshow is represented by any XML file with a root node of slideshow in which images are represented by a photo node and sound files by an audio node. Any child nodes of the root should include a source attribute (in which a URL with images and audio files is pro-vided), and photo nodes should include a duration attribute (in which a time duration measured in seconds is provided). Images must be JPEG, PNG, GIF, or SWF files, and audio must be an MP3 file. Create a new XML file named test.xml in your application root and populate it with slideshow data such as the XML shown in Listing 13.3.

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

<slideshow>

<audio source=”http://www.theriabook.com/narration.mp3” />

<photo duration=”10” source=”http://www.theriabook.com/mary.jpg” />

<photo duration=”20” source=”http://www.theriabook.com/lou.jpg” />

<photo duration=”20” source=”http://www.theriabook.com/mom.jpg” />

</slideshow>

Listing 13.3 SlideShow XML data

In this chapter we’re only displaying a single image at a time and using a single audio track to synch with the images, so the above XML format is all the data we need. However, this format could be expanded to create a more advanced application.

Now that we’ve got some slideshow data, let’s pull it into the application using an HTTPService object in MXML and feed the result into our SlideShow component as shown in Listing 13.4.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:lib=”com.theriabook.

controls.*” layout=”absolute” initialize=”init();”>

<mx:Script>

<![CDATA[

import mx.rpc.events.ResultEvent;

private function init():void {

service.send();

}

]]>

</mx:Script>

<mx:HTTPService id=”service” url=”test.xml” resultFormat=”e4x” result=”show.loadShow(

XML(event.result) );” />

<lib:SlideShow id=”show” width=”100%” height=”100%” horizontalCenter=”0” />

</mx:Application>

Listing 13.4 SlideShowPlayer application

Building a SlideShow ApplicationCHAPTER 13

Page 546: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

548 RIA WITH ADOBE FLEX AND JAVA

When our application starts, it will now make a request for test.xml and pass the result to the Slide-Show’s loadShow method. However, the Slideshow component from Listing 13.1 doesn’t have a load-Show method yet. We’ll need to add this method by creating the public function in the component file. There, we can also add the preloading functionality required. We’ll need to include a ProgressBar MXML component and some variables to hold the loaded content as shown in Listing 13.5.

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

<mx:Canvas xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:lib=”com.theriabook.controls.*” horizontalScrollPolicy=”off”

verticalScrollPolicy=”off”>

<mx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

import flash.display.Loader;

[Bindable]

private var _xml:XML;

private var photos:ArrayCollection = new ArrayCollection();

private var sound:Sound;

private var iLoaded:uint = 1;

private var isLoaded:Boolean = false;

public function loadShow( xml:XML ):void {

_xml = xml;

photos = new ArrayCollection();

iLoaded = 1;

isLoaded = false;

photoIndex = 0;

nextPhotoTime = 1;

progress.visible = true;

play.visible = false;

for each(var photoNode:XML in _xml.photo) {

var photo:Loader = new Loader();

photo.load(new URLRequest(photoNode.@source));

photo.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,

onProgress);

photo.contentLoaderInfo.addEventListener(Event.COMPLETE,

onComplete);

photos.addItem(photo);

}

progress.label=”Loading Image “ + iLoaded +” of “ + photos.length + “.”;

}

Building a SlideShow ApplicationCHAPTER 13

Page 547: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 549

private function onProgress( event:ProgressEvent ):void {

var loaded:Number = 0;

var total:Number = 0;

if(iLoaded<photos.length) {

for each(var item:Loader in photos) {

loaded += item.contentLoaderInfo.bytesLoaded;

total += item.contentLoaderInfo.bytesTotal;

}

} else {

loaded = event.bytesLoaded;

total = event.bytesTotal;

}

progress.setProgress(loaded,total);

}

private function onComplete( event:Event ):void {

if(iLoaded<photos.length) {

iLoaded++;

progress.label=”Loading Image “ + iLoaded + “of” +

photos.length;

} else if (sound==null && _xml.audio[0].@source!=””) {

sound = new Sound();

sound.addEventListener(ProgressEvent.PROGRESS, onProgress);

sound.addEventListener(Event.COMPLETE, onComplete);

sound.load(new URLRequest(_xml.audio[0].@source));

progress.label=”Loading Audio”;

} else {

progress.visible = false;

play.visible = true;

isLoaded = true;

}

}

]]>

</mx:Script>

<mx:LinkButton id=”play” label=”Play” horizontalCenter=”0” verticalCenter=”0”

visible=”false” color=”0xFFFFFF” />

<mx:ProgressBar id=”progress” mode=”manual” width=”60%” horizontalCenter=”0” vertical-

Center=”0” color=”0xFFFFFF” />

<mx:Image id=”image” horizontalCenter=”0” verticalCenter=”0” />

</mx:Canvas>Listing 13.5 The second version of the SlideShow component: SlideShow2.mxml

In the code above we’ve used ActionScript to create a Loader object for each image and store it in an ArrayCollection. The loadShow event kicks things off by loading the value of the source attribute for

Building a SlideShow ApplicationCHAPTER 13

Page 548: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

550 RIA WITH ADOBE FLEX AND JAVA

each photo node (represented by the variable photoNode) into a new Loader object and attaching event listeners for the Progress and Complete events. The Progress event will be broadcast each time a sub-stantial chunk of data has been downloaded from the file. This lets us update the ProgressBar MXML component programmatically to display the download progress (provided by the Loader object) to the user. When the images are done loading, the Complete event triggers the onComplete method, which loads the audio file in the same manner. Preloading makes audio and image syncing much simpler.

Since we know the audio and images will begin playing at the same time and that new images will display immediately when requested, we can simply represent syncing by assigning a time dura-tion to each photo. When all the audio and images are loaded, we display a LinkButton Play to the user. The play functionality isn’t implemented yet, but you can compile and run this application to test the loading and progress bar functionality (see Figure 13.3).

Figure 13.3 Running SlideShow2: loading images and the progress bar

Animating the SlideShowTo get our slideshow running, we need to create a Timer object that coordinates the images and has logic in its Timer event to change the image source at the specified time intervals. Let’s create a method called playShow to initialize and start the timer and set the play button’s click event to invoke this method.

private var timer:Timer = new Timer(1000,0);

private var photoIndex:int = 0;

private var nextPhotoTime:int = 1;

Building a SlideShow ApplicationCHAPTER 13

Page 549: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 551

public function playShow():void {

if(isLoaded) {

play.visible = false;

timer = new Timer(1000,0);

timer.addEventListener( TimerEvent.TIMER, onTime );

timer.start();

if(sound!=null) {sound.play();}

} else { loadShow( _xml ); }

}

private function onTime( event:TimerEvent ):void {

if( event.currentTarget.currentCount == nextPhotoTime ) {

if( photos.length > photoIndex ) {

image.load(Loader(photos[photoIndex]).content);

// using e4x to access the photo duration attribute

nextPhotoTime += int(_xml.photo[photoIndex].@duration);

photoIndex++;

} else {

stopShow();

}

}

}

public function stopShow():void {

timer.stop();

timer.reset();

image.source=””;

SoundMixer.stopAll();

photoIndex = 0;

nextPhotoTime = 1;

play.visible = true;

}

Listing 13.6 SlideShow3: the timer and play functionality

We’ve also included a function to stop the slideshow and display the play button again once the slideshow has finished. This will let users replay the slideshow after their first viewing, but there’s still no way to navigate from slide to slide while you’re watching.

Building a SlideShow ApplicationCHAPTER 13

Page 550: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

552 RIA WITH ADOBE FLEX AND JAVA

Figure 13.4 Running the SlideShowPlayer3.mxml

After adding the code from Listing 13.6 to the SlideShow component, we need to add the call to the playShow() method in the SlideShowPlayer.mxml. For example:

<mx:HTTPService id=”service” url=”test.xml” resultFormat=”e4x” result=”show.

loadShow( XML(event.result) ); show.playShow();” />

Adding Interactive Thumbnail NavigationSo far we’ve encapsulated all the logic needed to preload and play a slideshow. Our component even displays graphical progress indicators and syncs images with audio, but users still can’t view image thumbnails or skip ahead to the slide that interests them most. This is an especially important feature if this component is to be used for business. Let’s create another MXML component in the com/the-riabook/controls directory and put the code from Figure 13.7 inside. Alternately, you could download the Scroller component from the Flex 2 Exchange at http://www.adobe.com/exchange/.

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

<mx:Canvas xmlns:mx=”http://www.adobe.com/2006/mxml”

horizontalScrollPolicy=”off” initialize=”init()”>

<mx:Metadata>

[Event(“change”)]

</mx:Metadata>

<mx:Script>

<![CDATA[

Building a SlideShow ApplicationCHAPTER 13

Page 551: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 553

private const SENSITIVITY:Number = 15;

private const FRICTION:Number = 1.1;

private var momentum:Number = 0;

private var _dataProvider:Object = null;

public function set dataProvider(value:Object):void {

_dataProvider = value;

this.contents.removeAllChildren();

for each (var obj:Object in _dataProvider) {

var child:* = itemRenderer.newInstance();

child.data = obj;

child.addEventListener(MouseEvent.CLICK, onChange);

this.contents.addChild(child);

}

if(this.contents.numChildren>0) {

this.selectedItem = this.contents.getChildAt(0);

this.selectedIndex = 0;

}

}

public var itemRenderer:IFactory = null;

public var selectedItem:Object = null;

public var selectedIndex:int = -1;

private function init():void {

this.addEventListener(Event.ENTER_FRAME, onEnterFrame);

}

private function onEnterFrame( e:Event ):void {

if (this.mouseX >= 0 && this.mouseX <= this.width && this.mouseY >= 0 &&

this.mouseY <= this.height) {

momentum = ((this.mouseX - this.width/2)/SENSITIVITY)*-1;

} else {

momentum = momentum/FRICTION;

}

if((this.contents.x + momentum >= 0 && momentum > 0) ||

this.contents.width < this.width ) {

this.contents.x = 0;

this.momentum = 0;

}

else if(this.contents.x + momentum <= this.width - this.contents.width &&

momentum < 0) {

this.contents.x = this.width - this.contents.width;

this.momentum = 0;

Building a SlideShow ApplicationCHAPTER 13

Page 552: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

554 RIA WITH ADOBE FLEX AND JAVA

}

else {

this.contents.x += momentum;

}

this.filters = new Array(new BlurFilter(Math.abs(momentum)/2-3, 0));

}

private function onChange( event:MouseEvent ):void {

this.selectedItem = event.currentTarget.data;

this.selectedIndex =

contents.getChildIndex(DisplayObject(event.currentTarget));

this.dispatchEvent( new Event( “change”, true ) );

}

]]>

</mx:Script>

<mx:HBox id=”contents” clipContent=”true” width=”100%” height=”100%”

verticalAlign=”middle” />

</mx:Canvas>

Listing 13.7 Scroller component code

In this code the display objects are kept in a single container named contents so that they can be moved in unison. The set method for the dataProvider is used to clear any existing children in the contents object and create a new instance of the itemRenderer for each item in the dataProvider. This way, the component provides similar functionality to Flex’s TileList component, but it has a significantly different user interface. The Scroller tiles each gener-ates an item in a horizontal row and provides automatic scrolling based on mouse position. This rich interactive behavior is defined inside the onEnterFrame method (which is a listener for the ENTER_FRAME event). It moves the contents’ position based on the position of the mouse relative to the center (width/2) of the component. The constants SENSITIVITY and FRICTION can be used to adjust the component’s mouse sensitivity and speed degradation, respectively. It’s ideal for our thumbnail display since space is limited. We’re going to add it to the SlideShow component and bind it to the data we already have by adding some MXML to our SlideShow component as shown in the code snippet in Listing 13.8 (see the complete code of the SlideShow in Listing 13.11).

<lib:Scroller dataProvider=”{_xml.photo}”

itemRenderer=”com.theriabook.controls.SlideThumb”

width=”100%” height=”85” bottom=”0” />

Listing 13.8 Adding the Scroller component in MXML

Notice that in Listing 13.8 we’ve referenced com.theriabook.controls.SlideThumb as the item ren-

Building a SlideShow ApplicationCHAPTER 13

Page 553: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 555

derer. The SlideThumb component simply displays an Image inside a Canvas control. When the Scroller generates this component it will assign each instance the correct xml node using the data property. We just need to bind the Image’s source attribute (see Listing 13.3) to data.@source as in Listing 13.9 and our custom itemRenderer will load the image as expected.

<?xml version=”1.0”?>

<mx:Canvas xmlns:mx=”http://www.adobe.com/2006/mxml” width=”75” height=”75”

buttonMode=”true” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” horizontalCen-

ter=”0” verticalCenter=”0” >

<mx:Image id=”image” source=”{data.@source}” width=”75” height=”75” horizontalCen-

ter=”0” verticalCenter=”0” />

</mx:Canvas>

Listing 13.9 SlideThumb component

We can also include rich interactive behavior by adding code to the SlideThumb’s enterFrame event, which measures the distance from this image to the mouse position and uses the value to set the image’s width, height, and alpha transparency between a hard-coded maximum and minimum value. Insert the code from Listing 13.10 in the SlideThumb component and use the initialized event to invoke the init function. This will give each thumbnail fluid resizing behavior based on mouse position.

<mx:Script>

<![CDATA[

private function init():void {

this.addEventListener(Event.ENTER_FRAME, onEnterFrame);

}

private function onEnterFrame( event:Event ):void {

if (this.mouseY >= 0 && this.mouseY <= this.height) {

this.image.width += (75 -

Math.min(50,Math.abs(this.mouseX+this.width/2)/20) - this.image.width)/3;

this.image.height += (75 -

Math.min(50,Math.abs(this.mouseX+this.width/2)/20) - this.image.height)/3;

this.alpha = (75 -

Math.min(50,Math.abs(this.mouseX+this.width/2)/20))/85;

} else if(this.image.width > 51 || this.image.width < 49) {

this.image.width += (50 - this.image.width)/3;

this.image.height += (50 - this.image.height)/3;

}

}

]]>

</mx:Script>

Listing 13.10 Thumb component behavior code

Building a SlideShow ApplicationCHAPTER 13

Page 554: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

556 RIA WITH ADOBE FLEX AND JAVA

All of the components for the SlideShowPlayer have been created, but we still need to add code that will navigate to any given slide based on the user’s thumbnail selec-tion.

Let’s create a gotoSlide method in SlideShow.xml and use the Scroller’s change event to pass it the correct slide index. The method will need to loop through each node in the XML and sum the duration where the slide at the given index begins. It will then reset the sound to play, starting at the correct time, and set the current photoIndex and nextPhotoTime to dis-play the selected photo immediately. The full code for the SlideShow component is given in Listing 13.11.

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

<mx:Canvas xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:lib=”com.theriabook.

controls.*” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” backgroundCol-

or=”#000000” borderColor=”#ffffff” borderStyle=”solid” borderThickness=”5”>

<mx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

import flash.display.Loader;

[Bindable]

private var _xml:XML;

private var photos:ArrayCollection = new ArrayCollection();

private var sound:Sound;

private var iLoaded:uint = 1;

private var isLoaded:Boolean = false;

private var timer:Timer = new Timer(1000,0);

private var photoIndex:int = 0;

private var nextPhotoTime:int = 1;

public function loadShow( xml:XML ):void {

_xml = xml;

photos = new ArrayCollection();

iLoaded = 1;

isLoaded = false;

photoIndex = 0;

nextPhotoTime = 1;

progress.visible = true;

play.visible = false;

for each(var photoNode:XML in _xml.photo) {

var photo:Loader = new Loader();

photo.load(new URLRequest(photoNode.@source));

photo.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, on-

Building a SlideShow ApplicationCHAPTER 13

Page 555: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 557

Progress);

photo.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);

photos.addItem(photo);

}

progress.label=”Loading Image “+iLoaded+” of “+photos.length+”.”;

}

public function playShow():void {

if(isLoaded) {

play.visible = false;

timer = new Timer(1000,0);

timer.addEventListener( TimerEvent.TIMER, onTime );

timer.start();

if(sound!=null) {sound.play();}

} else { loadShow( _xml ); }

}

private function onProgress( event:ProgressEvent ):void {

var loaded:Number = 0;

var total:Number = 0;

if(iLoaded<photos.length) {

for each(var item:Loader in photos) {

loaded += item.contentLoaderInfo.bytesLoaded;

total += item.contentLoaderInfo.bytesTotal;

}

} else {

loaded = event.bytesLoaded;

total = event.bytesTotal;

}

progress.setProgress(loaded,total);

}

private function onComplete( event:Event ):void {

if(iLoaded<photos.length) {

iLoaded++;

progress.label=”Loading Image “+

iLoaded+ “ of “ +photos.length;

} else if (sound==null && _xml.audio[0].@source!=””) {

sound = new Sound();

sound.addEventListener(ProgressEvent.PROGRESS,

onProgress);

sound.addEventListener(Event.COMPLETE, onComplete);

sound.load(new URLRequest(_xml.audio[0].@source));

progress.label=”Loading Audio”;

Building a SlideShow ApplicationCHAPTER 13

Page 556: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

558 RIA WITH ADOBE FLEX AND JAVA

} else {

progress.visible = false;

play.visible = true;

isLoaded = true;

}

}

private function onTime( event:TimerEvent ):void {

if( event.currentTarget.currentCount == nextPhotoTime ) {

if( photos.length > photoIndex ) {

image.load(Loader(photos[photoIndex]).content);

nextPhotoTime += int(_xml.photo[photoIndex].@duration);

photoIndex++;

} else {

stopShow();

}

}

}

public function gotoSlide( index:int ):void {

var gotoTime:uint = 0;

for(var i:uint=0;i<=index;i++) {

gotoTime += int(_xml.photo[i].@duration);

}

nextPhotoTime = timer.currentCount+1;

photoIndex = index;

SoundMixer.stopAll();

if(sound!=null) { sound.play(gotoTime); }

if(!timer.running) { timer.start(); }

}

public function stopShow():void {

timer.stop();

timer.reset();

image.source=””;

SoundMixer.stopAll();

photoIndex = 0;

nextPhotoTime = 1;

play.visible = true;

}

]]>

</mx:Script>

Building a SlideShow ApplicationCHAPTER 13

Page 557: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 559

<mx:LinkButton id=”play” label=”Play” click=”playShow();” horizontalCenter=”0” verti-

calCenter=”0” visible=”false” color=”0xFFFFFF” />

<mx:ProgressBar id=”progress” mode=”manual” width=”60%” horizontalCenter=”0” vertical-

Center=”0” color=”0xFFFFFF” />

<mx:Image id=”image” horizontalCenter=”0” verticalCenter=”0” />

<lib:Scroller id=”scroller” dataProvider=”{_xml.photo}” change=”gotoSlide( event.

currentTarget.selectedIndex );” itemRenderer=”com.theriabook.controls.SlideThumb”

width=”100%” height=”85” bottom=”0” />

</mx:Canvas>

Listing 13.11 Slideshow component source code

Developing the SlideShow Player ApplicationTo finish the SlideShowPlayer we started earlier, let’s modify the code to accept a QueryString pa-rameter named slideshow and pass its value to the HTTPService. To do this we’ll reference the Application.application.parameters object, which contains any query string name-value pairs from the URL used to reference the SWF file (note that this may not be the URL used to reference the current Web page). Parameters can be specified directly in the URL as key-value pairs right after the question mark (http://myserver.com/myApp.swf?slideshow=”someURLofYourShow” ) or embedded in the HTML wrapper using flashvars parameters.

In this example the query string parameter we’re looking for is named slideshow. To specify your own slideshow, simply set slideshow equal to a URL that points to a valid slideshow XML file. The full code for the SlideshowPlayer is given in Listing 13.12, and the resulting SWF is shown in Figure 13.5.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:lib=”com.theriabook.

controls.*” layout=”absolute” initialize=”init();”>

<mx:Script>

<![CDATA[

import mx.rpc.events.ResultEvent;

private function init():void {

service.send();

}

]]>

</mx:Script>

<mx:HTTPService id=”service”

url=”{Application.application.parameters.slideshow}”

resultFormat=”e4x” result=”show.loadShow( XML(event.result) );” />

Building a SlideShow ApplicationCHAPTER 13

Page 558: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

560 RIA WITH ADOBE FLEX AND JAVA

<lib:SlideShow id=”show” width=”100%” height=”100%” horizontalCenter=”0”

verticalCenter=”0” backgroundColor=”#000000” borderColor=”#ffffff”

borderStyle=”solid” borderThickness=”5”/>

</mx:Application>

Listing 13.12 SlideshowPlayer application source

Figure 13.5 SlideshowPlayer application

Developing the SlideShow CreatorWe’ve already got a great slideshow player, but we still have to create an application that lets users make their own slideshow. To get started, let’s create a new MXML file named SlideShowCreator.mxml:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:lib=”com.theriabook.

controls.*” layout=”absolute” >

<mx:Panel layout=”absolute” title=”Slideshow Creator” id=”creator” left=”10”

right=”10” bottom=”100” top=”50”>

<mx:TileList id=”gallery” right=”310” bottom=”5” top=”5” left=”5”/>

<mx:ControlBar horizontalAlign=”left” verticalAlign=”middle”>

<mx:Label text=”Tags”/>

<mx:TextInput id=”tags”/>

<mx:Button label=”Search Flickr” id=”search”

click=”service.send();” />

Building a SlideShow ApplicationCHAPTER 13

Page 559: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 561

</mx:ControlBar>

</mx:Panel>

</mx:Application>

Listing 13.13 A fragment of the SlideShowCreator.mxml

In Listing 13.13, we’ve created a Panel area to search and select images based on a given tag. It includes a TileList to hold the images and a ControlBar with text input for search terms. Now we need a method for retrieving images based on the user input given. The variable service (see List-ing 13.14) represents a Web Service of the popular Web portal flickr.com that lets people store and share their photos.

Integrating with Flickr Web ServicesFlickr provides documentation on how to connect to its services, and many third-party products use them (see http://www.flickr.com/services/). There’s an API at http://www.flickr.com/services/api and its manual says that we can use query strings to pass our search text to a specific URL. It will then return an XML response containing the data we need to reference a given image. It will also let us know that we can use the images in Flex because flickr.com has put a cross-domain XML file in the appropriate domain. This file is required to allow any Flash-based application to access images outside the Web domain in which it runs. Based on that documentation, we can define and use an HTTPService control that includes the dynamic user input from the TextInput control in its URL as shown in Listing 13.14

<mx:HTTPService id=”service” url=”http://www.flickr.com/services/rest/?method=flickr.pho-

tos.search&amp;api_key=

your_api_key&amp;text={tags.text}” />

Listing 13.14 Defining flickr HTTPService with a text parameter

Note that you’ll need to replace the term your_api_key with your own API key obtained from Flickr. You can request one from the Flickr API site I mentioned earlier.

When a request with this URL is sent to Flickr, we can expect an XML file in return. A sample re-sponse is shown in Listing 13.15.

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

<rsp stat=”ok”>

<photos page=”1” pages=”1483” perpage=”100” total=”148239”>

<photo id=”209424451” owner=”83276940@N00” secret=”0193620991” server=”62”

title=”Eastern Pondhawk” ispublic=”1” isfriend=”0” isfamily=”0” />

<photo id=”209413116” owner=”35237092727@N01” secret=”0e09c465f9” server=”81”

title=”Inspiration: Refresh Jacksonville” ispublic=”1” isfriend=”0” isfamily=”0” />

<photo id=”209406820” owner=”12915821@N00” secret=”515b9f22c1” server=”63” title=”HMS

Building a SlideShow ApplicationCHAPTER 13

Page 560: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

562 RIA WITH ADOBE FLEX AND JAVA

Belfast - Engine Room” ispublic=”1” isfriend=”0” isfamily=”0” />

<photo id=”209395531” owner=”64362703@N00” secret=”cd71e12304” server=”70”

title=”Jenson Button” ispublic=”1” isfriend=”0” isfamily=”0” />

</photos>

</rsp>

Listing 13.15 Flickr Web Service response

We’ll need to create an itemRenderer for the TileList that uses the XML data received from Flickr to retrieve and display images. Let’s create a new MXML component named FlickrThumb:

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

<mx:VBox xmlns:mx=”http://www.adobe.com/2006/mxml” width=”75” height=”75”

buttonMode=”true” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” horizontalCen-

ter=”0” verticalCenter=”0”>

<mx:Image

id=”image”

width=”75” height=”75”

source=”http://static.flickr.com/{data.server}/{data.id}_{data.secret}_s.jpg”

toolTip=”{data.title}”

verticalCenter=”0”

completeEffect=”Fade”

/>

</mx:VBox>

Listing 13.16 FlickrThumb.mxml

The string concatenation used is based on Flickr’s documentation at http://www.flickr.com/ser-vices/api/misc.urls.html. It shows that we can expect to load an image that is 75 pixels in width and 75 pixels in height using the constructed URL. You can find the attributes server, ID, and secret in Listing 13.15.

Now that all the necessary components are in place we can bind the appropriate HTTPService result to the TileList in our SlideShowCreator application by setting its dataProvider property to “{service.lastResult.rsp.photos.photo}”.

The “rsp.photos.photo” portion of this code is referencing the actual XML response from Flickr. Let’s also set the itemRenderer as FlickrThumb and the search button’s click event to invoke the HTTPService object’s send method. Run the SlideSlowCreator application to test our Flickr integra-tion and search through images to your heart’s content. The progam will output the screen (with-out photos) as in Figure 13.6.

Building a SlideShow ApplicationCHAPTER 13

Page 561: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 563

Editing SlideShow DataWe’ve finally got lots of Flickr images to look at, but users still can’t do anything with them. We need to create an XML object to hold our slideshow data as we manipulate it and a second one to hold the currently selected photo data. Then we can create methods to select, add, and remove slides based on their URL as shown in Listing 13.17:

<mx:Script>

<![CDATA[

[Bindable]

public var xml:XML = <slideshow><audio source=”” /></slideshow>;

[Bindable]

public var selected:XML = <slideshow><photo duration=”” source=””/>

</slideshow>;

private function selectSlide( source:String, duration:uint ):void {

var slide:XML = <slideshow><photo duration=”” source=””/>

</slideshow>;

slide.photo.@source = source;

slide.photo.@duration = duration;

selected = slide;

imgDuration.value = duration;

}

private function addSelectedSlide():void {

selected.photo.@source = image.source;

selected.photo.@duration = imgDuration.value

xml.appendChild( selected.photo );

}

private function removeSelectedSlide():void {

xml.photo = xml.photo.(@source!=selected.photo.@source);

}

]]>

</mx:Script>

Listing 13.17 Manipulating XML data in ActionScript

Once again, we’ve used E4X in this code to store and manipulate XML data. If this was a larger or more complex application it might have been necessary to create business objects instead, but since the slideshow data is ultimately represented as XML, this may be a better option.

Now we’ve got all the methods needed to create a slideshow. We just need to fill in some of the

Building a SlideShow ApplicationCHAPTER 13

Page 562: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

564 RIA WITH ADOBE FLEX AND JAVA

user interface required. Inside the Panel we’ll create a VBox containing an Image control and a Form with input for the photo source and duration. This should include a TextInput control called imgSource and a NumericStepper control named imgDuration. We can bind the Image control to “selected.photo.@source” to be sure it always shows the selected image. We’ve already referenced the NumericStepper imgDuration (just a text field with up and down arrows) in the code above so that it updates correctly. We’ll also need to invoke the selectSlide method inside of the TileList’s change event and the addSelectedSlide and removeSelectedSlide methods inside of button click events as shown in Listing 13.18.

<mx:Panel layout=”absolute” title=”SlideShow Creator” id=”creator” left=”10” right=”10”

bottom=”100” top=”50”>

<mx:TileList id=”gallery” change=”selectSlide(‘http://static.flickr.com/’ + event.

currentTarget.selectedItem.server + ‘/’ + event.currentTarget.selectedItem.id + ‘_’ +

event.currentTarget.selectedItem.secret + ‘_m.jpg’, 10);” dataProvider=”{service.las-

tResult.rsp.photos.photo}” itemRenderer=”FlickrThumb” right=”310” bottom=”5” top=”5”

left=”5”/>

<mx:VBox top=”5” width=”300” bottom=”5” horizontalAlign=”center”

verticalAlign=”bottom” right=”5”>

<mx:Image id=”image” source=”{selected.photo.@source}” width=”200”

height=”200”/>

<mx:Form width=”100%”>

<mx:FormItem label=”Source” horizontalAlign=”left”>

<mx:TextInput id=”imgSource”

text=”{selected.photo.@source}” width=”175”/>

</mx:FormItem>

<mx:FormItem label=”Duration”>

<mx:NumericStepper id=”imgDuration” value=”10”

minimum=”1” maximum=”300” />

</mx:FormItem>

</mx:Form>

<mx:HBox width=”100%” horizontalAlign=”right”>

<mx:Button label=”Remove Photo”

click=”removeSelectedSlide();” />

<mx:Button label=”Add Photo” click=”addSelectedSlide();” />

</mx:HBox>

</mx:VBox>

<mx:ControlBar horizontalAlign=”left” verticalAlign=”middle”>

<mx:Label text=”Tags”/>

<mx:TextInput id=”tags”/>

<mx:Button label=”Search” id=”search” click=”service.send();” />

</mx:ControlBar>

</mx:Panel>

Listing 13.18 Panel with Image selection and control

Building a SlideShow ApplicationCHAPTER 13

Page 563: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 565

Now that we’ve got some of the interface in place, users can add and remove photos to and from the slideshow XML object, but there’s still no feedback to show users the current list of slides. Fortu-nately, we’ve already created a Scroller component and Thumb component that can be reused and bound to the slideshow XML. Simply add the Scroller component we created earlier underneath the Panel and bind it to the XML object as shown in Listing 13.19.

<lib:Scroller id=”scroller” dataProvider=”{xml.photo}” change=”selectSlide( event.curren

tTarget.selectedItem.@source );” itemRenderer=”Thumb” bottom=”10” left=”10” right=”10”

height=”85”/>

Listing 13.19 Adding the Scroller component

Notice that we’ve also set the Scroller’s change event to invoke the selectSlide method. This is the only interactivity we need to define. Flex’s rich data binding will take care of updating the list when items are added or removed. Run your application and be sure that everything is in working order. You should be able to browse photos from Flickr, select photos to preview, adjust settings, and add photos to the slideshow list. You should also be able to select photos from the slideshow list and remove them. The last thing we need to include is the capability to add audio from a URL. Insert the code from Listing 13.20 into the ControlBar component already in the application.

<mx:HBox width=”100%” horizontalAlign=”right” verticalAlign=”middle”>

<mx:Label text=”Audio”/>

<mx:TextInput id=”soundURL”/>

<mx:Button label=”Add Sound” click=”xml.audio.@source = soundURL.text;” />

</mx:HBox>

Listing 13.20 HTTPService

In Listing 13.20 we’ve provided a TextInput control for the user to enter a URL and a button whose click event sets the appropriate value in our slideshow XML. Also note that we have surrounded these input fields with a VBox container that has a horizontalAlign value of right. This is provided so that the controls align themselves on the opposite side of the Flickr search controls.

For example, enter New York in the Tags text field and press the button Search Flickr. The Slide-ShowCreator will display the thumbs with the images of the photos of New York. Select the image that you’d like to include in your slideshow, and you’ll see the URL of the source file. Enter required duration. Press the button Add Photo and repeat this procedure for other photos.

Now that we’ve developed a way for users to create a slideshow, let’s include a way for them to pre-view it in the SlideShowCreator application.

Building a SlideShow ApplicationCHAPTER 13

Page 564: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

566 RIA WITH ADOBE FLEX AND JAVA

Figure 13.6 The SlideShowCreator window: Design mode

Developing the SlideShow PreviewTo preview slideshows, we can simply reuse our SlideShow component. We’ll need to create a new view state and include a way for users to navigate between both view states in the application. To create a new view state we can use the MXML State control as shown in Listing 13.21.

<mx:states>

<mx:State name=”preview” enterState=”preview.loadShow( xml );”

exitState=”preview.stopShow();”>

<mx:SetProperty target=”{creator}” name=”visible” value=”false”/>

<mx:SetProperty target=”{scroller}” name=”visible” value=”false”/>

<mx:AddChild>

<lib:SlideShow id=”preview” bottom=”10” left=”10” right=”10” top=”50” />

</mx:AddChild>

</mx:State>

</mx:states>

Listing 13.21 State control

In this section of the code we’ve defined a new state named preview. In the preview state we’re setting the Panel and Scroller controls to be hidden and creating a new SlideShow component on stage. We’ve also set the enterState and exitState events to play and stop the slideshow, respectively. Now all we need to do is provide the user with a way to navigate to the appropriate application state. We’ve left some room at the top of our application for an ApplicationControlBar and we’re

Building a SlideShow ApplicationCHAPTER 13

Page 565: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 567

going to use it to provide links to our two application states as shown in Listing 13.22.

<mx:ApplicationControlBar top=”5” left=”5” right=”5”>

<mx:LinkButton label=”Design” click=”currentState=’’;” />

<mx:LinkButton label=”Preview” click=”currentState=’preview’;” />

</mx:ApplicationControlBar>

Listing 13.22 State control

In the click event of the LinkButton for our Design view we’ve set the application view state back to the default view by assigning currentState equal to a blank string. Run the application to create a new slideshow and preview it using our SlideShow component. If you have any trouble running your application, reference the full SlideShowCreator.mxml source shown in Listing 13.23.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:lib=”com.theriabook.

controls.*” xmlns=”*” layout=”absolute” >

<mx:states>

<mx:State name=”preview” enterState=”preview.loadShow( xml );”

exitState=”preview.stopShow();”>

<mx:SetProperty target=”{creator}” name=”visible” value=”false”/>

<mx:SetProperty target=”{scroller}” name=”visible” value=”false”/>

<mx:AddChild>

<SlideShow id=”preview” bottom=”10” left=”10” right=”10” top=”50” />

</mx:AddChild>

</mx:State>

</mx:states>

<mx:Script>

<![CDATA[

[Bindable]

public var xml:XML = <slideshow><audio source=”” /></slideshow>;

[Bindable]

public var selected:XML = <slideshow><photo duration=”” source=””/>

</slideshow>;

private function selectSlide( source:String, duration:uint ):void {

var slide:XML = <slideshow><photo duration=”” source=”” />

</slideshow>;

slide.photo.@source = source;

slide.photo.@duration = duration;

selected = slide;

imgDuration.value = duration;

}

Building a SlideShow ApplicationCHAPTER 13

Page 566: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

568 RIA WITH ADOBE FLEX AND JAVA

private function addSelectedSlide():void {

selected.photo.@source = image.source;

selected.photo.@duration = imgDuration.value

xml.appendChild( selected.photo );

}

private function removeSelectedSlide():void {

xml.photo = xml.photo.(@source!=selected.photo.@source);

}

]]>

</mx:Script>

<mx:HTTPService id=”service” url=”http://www.flickr.com/services/rest/?method=flickr.

photos.search&amp;api_key=fa5d101b8564317c248aa429302468ee&amp;text={tags.text}” />

<mx:ApplicationControlBar top=”5” left=”5” right=”5”>

<mx:LinkButton label=”Design” click=”currentState=’’;” />

<mx:LinkButton label=”Preview” click=”currentState=’preview’;” />

</mx:ApplicationControlBar>

<mx:Panel layout=”absolute” title=”SlideShow Creator” id=”creator” left=”10”

right=”10” bottom=”100” top=”50”>

<mx:TileList id=”gallery” change=”selectSlide(‘http://static.flickr.com/’ + event.

currentTarget.selectedItem.server + ‘/’ + event.currentTarget.selectedItem.id + ‘_’ +

event.currentTarget.selectedItem.secret + ‘_o.jpg’, 10);” dataProvider=”{service.las-

tResult.rsp.photos.photo}” itemRenderer=”FlickrThumb” right=”310” bottom=”5” top=”5”

left=”5”/>

<mx:VBox top=”5” width=”300” bottom=”5” horizontalAlign=”center”

verticalAlign=”bottom” right=”5”>

<mx:Image id=”image” source=”{selected.photo.@source}” width=”200”

height=”200” />

<mx:Form width=”100%”>

<mx:FormItem label=”Source” horizontalAlign=”left”>

<mx:TextInput id=”imgSource”

text=”{selected.photo.@source}” width=”175”/>

</mx:FormItem>

<mx:FormItem label=”Duration”>

<mx:NumericStepper id=”imgDuration” value=”10”

minimum=”1” maximum=”300” />

</mx:FormItem>

</mx:Form>

<mx:HBox width=”100%” horizontalAlign=”right”>

<mx:Button label=”Remove Photo”

click=”removeSelectedSlide();” />

<mx:Button label=”Add Photo” click=”addSelectedSlide();” />

</mx:HBox>

Building a SlideShow ApplicationCHAPTER 13

Page 567: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 569

</mx:VBox>

<mx:ControlBar horizontalAlign=”left” verticalAlign=”middle”>

<mx:Label text=”Tags”/>

<mx:TextInput id=”tags”/>

<mx:Button label=”Search Flickr” id=”search”

click=”service.send();” />

<mx:HBox width=”100%” horizontalAlign=”right”

verticalAlign=”middle”>

<mx:Label text=”Audio URL”/>

<mx:TextInput id=”soundURL”/>

<mx:Button label=”Add Audio” click=”xml.audio.@source =

soundURL.text;” />

</mx:HBox>

</mx:ControlBar>

</mx:Panel>

<lib:Scroller id=”scroller” dataProvider=”{xml.photo}” change=”selectSlide( event.

currentTarget.selectedItem.@source, event.currentTarget.selectedItem.@duration );”

itemRenderer=” com.theriabook.controls.SlideThumb “ bottom=”10” left=”10” right=”10”

height=”85”/>

</mx:Application>

Listing 13.23 SlideShowCreator.mxml

When our application is running in preview mode, the SlideShow component should automatically load and let users interact with it just as they would in the SlideShowPlayer application. See Figure 13.7.

When all slides are selected and an MP3 audio file is ready, click on preview and enjoy the show!

Figure 13.7 SlideShowCreator Preview state

Building a SlideShow ApplicationCHAPTER 13

Page 568: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

570 RIA WITH ADOBE FLEX AND JAVA

SummaryIn this chapter we’ve developed an application that can be used to create and preview custom slideshows using the Flickr API. Hopefully we’ve gotten you well on your way to creating a very rich and useful application in Flex with relatively little effort. We’ve also had another chance to review several important Flex 2 topics including:

• Developing components for reuse• Animating with ActionScript 3• Using Timer events• Integrating with Web Services• Using E4X with databinding• Managing view states

Flex 2 makes desktop-like applications a reality for the Web. We hope you can expand on the ideas presented in this chapter and create your own media applications targeted for the Web.

Building a SlideShow ApplicationCHAPTER 13

Page 569: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 573

Developing Custom Charts

14CHAPTER

Page 570: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

574 RIA WITH ADOBE FLEX AND JAVA

This line chart is rendered by the Flash Player and it reacts to your mouse movements, displaying the date and time under the mouse cursor; it has a zoom feature; and you can change the time pe-riod by dragging the timeline above the chart. This Flash chart is smart enough to interact with the rest of the non-Flash Web page components. Click on one of these little flags and the correspond-ing news item is highlighted on the right. Interactive charts will give your end users a lot more control than static ones, and will make them happy.

In this chapter, we’ll show you how you can program similarly interactive charts by creating your own and extending standard Flex Charting components. We assume the reader is familiar with the basics of Flex Charting. To get a feeling for what Flex Charting is about, check out the following URL that demos various samples of charts in action and shows the MXML source code for each: http://flexapps.macromedia.com/flex15/chartexplorer/explorer.mxml?versionChecked=true.

To simplify digesting this material, we’ll build the following five applications adding, complexity as we go:

• A program that draws a rectangular area right within the line chart component• A line chart with a hundred data points• A line chart with two data series and a movable vertical line• The line chart described above with a tool tip displaying data point values from two series• All of the above plus a chart zooming feature

While these applications won’t have all the functionality Google Finance offers, it’ll definitely give you a little push in the right direction.

How to Develop a Chart from ScratchFlex provides a large number of pre-built charts and flexible ways to customize them, in some cases you might find that developing your own charts has its benefits. It gives you the flexibility to imple-ment the functionality, ranging from simply highlighting areas of the chart to providing superior interactivity and presentation.

In our first example, we’ll try to override the standard painting of a chart component. Let’s start with a simple example that will only use ActionScript. Suppose you want to implement limits – each limit representing some rectangular region on the chart filled with some color. This can be useful in highlighting some business-specific range of values. For example, if a stock price drops below some resistance value, the line chart should be displayed in a red area. We’ll create two AS3 classes:

• A CartesianLimit that’s the data model of the limit• An ExtendedCartesianChart that’s responsible for the line chart and the limit-area painting

Please note that in this example, we’re painting this area right on the same graphic object. Later in

In Chapter 5 we used a <mx:PieChart> component in a sample portfolio application. Flex Charting lets you not only offer better visualization of your data, but also create interactive dashboard-type applications in which the charts can respond to a user’s actions. For example, the user’s click on a pie slice in the portfolio application would repopulate the data grid with the news about selected stock.

Check out the Google Finance application at http://finance.google.com/finance. Just enter a stock symbol (e.g., ADBE) and enjoy working with a highly interactive line chart representing the stock performance over a selected period of time.

Figure 14.1 Google Finance with interactive charting

Developing Custom ChartsCHAPTER 14

Developing Custom Charts

Page 571: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 575

This line chart is rendered by the Flash Player and it reacts to your mouse movements, displaying the date and time under the mouse cursor; it has a zoom feature; and you can change the time pe-riod by dragging the timeline above the chart. This Flash chart is smart enough to interact with the rest of the non-Flash Web page components. Click on one of these little flags and the correspond-ing news item is highlighted on the right. Interactive charts will give your end users a lot more control than static ones, and will make them happy.

In this chapter, we’ll show you how you can program similarly interactive charts by creating your own and extending standard Flex Charting components. We assume the reader is familiar with the basics of Flex Charting. To get a feeling for what Flex Charting is about, check out the following URL that demos various samples of charts in action and shows the MXML source code for each: http://flexapps.macromedia.com/flex15/chartexplorer/explorer.mxml?versionChecked=true.

To simplify digesting this material, we’ll build the following five applications adding, complexity as we go:

• A program that draws a rectangular area right within the line chart component• A line chart with a hundred data points• A line chart with two data series and a movable vertical line• The line chart described above with a tool tip displaying data point values from two series• All of the above plus a chart zooming feature

While these applications won’t have all the functionality Google Finance offers, it’ll definitely give you a little push in the right direction.

How to Develop a Chart from ScratchFlex provides a large number of pre-built charts and flexible ways to customize them, in some cases you might find that developing your own charts has its benefits. It gives you the flexibility to imple-ment the functionality, ranging from simply highlighting areas of the chart to providing superior interactivity and presentation.

In our first example, we’ll try to override the standard painting of a chart component. Let’s start with a simple example that will only use ActionScript. Suppose you want to implement limits – each limit representing some rectangular region on the chart filled with some color. This can be useful in highlighting some business-specific range of values. For example, if a stock price drops below some resistance value, the line chart should be displayed in a red area. We’ll create two AS3 classes:

• A CartesianLimit that’s the data model of the limit• An ExtendedCartesianChart that’s responsible for the line chart and the limit-area painting

Please note that in this example, we’re painting this area right on the same graphic object. Later in

In Chapter 5 we used a <mx:PieChart> component in a sample portfolio application. Flex Charting lets you not only offer better visualization of your data, but also create interactive dashboard-type applications in which the charts can respond to a user’s actions. For example, the user’s click on a pie slice in the portfolio application would repopulate the data grid with the news about selected stock.

Check out the Google Finance application at http://finance.google.com/finance. Just enter a stock symbol (e.g., ADBE) and enjoy working with a highly interactive line chart representing the stock performance over a selected period of time.

Figure 14.1 Google Finance with interactive charting

Developing Custom ChartsCHAPTER 14

Developing Custom Charts

Page 572: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

576 RIA WITH ADOBE FLEX AND JAVA

this chapter we’ll use a different technique (programmatic skins) and create additional objects to be put on the chart.

To begin with, let’s create a CartesianLimit class that will hold all the values required to paint the limits area:

Package com.theriabooks.limits {

import mx.graphics.IFill;

import mx.graphics.SolidColor;

import mx.graphics.IStroke;

import mx.charts.chartClasses.CartesianChart;

import flash.geom.Point;

import flash.geom.Rectangle;

public class CartesianLimit {

public var minX: Object;

public var maxX: Object;

public var minY: Object;

public var maxY: Object;

public var fillStyle:IFill = new SolidColor(0xFF0000);

public var lineStyle:IStroke;

public function paint(chart: CartesianChart): void {

var pt0 : Point = chart.dataToLocal(minX,minY);

var pt1 : Point = chart.dataToLocal(maxX,maxY);

var x: Number = Math.min(pt0.x,pt1.x);

var y: Number = Math.min(pt0.y,pt1.y);

var width: Number = Math.abs(pt0.x-pt1.x);

var height: Number = Math.abs(pt0.y-pt1.y);

if(lineStyle != null)

lineStyle.apply(chart.graphics);

else

chart.graphics.lineStyle();

if(fillStyle != null)

fillStyle.begin(chart.graphics, new Rectangle(x,y,width,height));

chart.graphics.drawRect(x,y,width,height);

if(fillStyle != null)

fillStyle.end(chart.graphics);

Developing Custom ChartsCHAPTER 14

Page 573: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 577

}

}

}

Listing 14.1 The ActionScript class CartesianLimit.as

This class lets you store the information about the region coordinates, fill, and the line style used to render this region. The method paint() here computes the coordinates of the rectangle based on the provided minimum and maximum values and paints the area according to the fill and line style. This is not a callback as Java programmers might assume; we’ll call it from the class Extended-CartesianChart. This method has one argument of the CartesianChart type, which is a base Flex class of all standard two-dimensional rectangular charts.

The user-defined ActionScript classes can be used as MXML tags as in Listing 14.4.

Object-oriented purists may not like the fact that we didn’t define the properties (minX, maxX, et al) of the private CartesianLimit class with public getters and setters. If we went that route, we’d have to clutter our code with a bunch of the following statements (one per each private property):

[Inspectable]

private var _minX: Object;

public function get minX() : Object {

return _minX;

}

public function set minX(value:Object) : void {

_minX=value;

}

Listing 14.2 Private properties with getters and setters

But if these getters and setters don’t include any additional processing, but are just passing the data from the outside world to private variables and back, we can get away without getters and setters. You don’t have to abide by the JavaBean specification to make your code visible to tools like Flex Builder.

To help Flex Builder provide code hints, you can mark the private properties of the ActionScript class with a metatag [Inspectable]. For public properties this wouldn’t be required. Flex Builder’s MXML editor can offer you code hints as shown below:

Developing Custom ChartsCHAPTER 14

Page 574: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

578 RIA WITH ADOBE FLEX AND JAVA

Figure 14.2 Flex Builder code hinting for the user-defined properties

To create our custom chart, we need to extend the CartesianChart class (a base class for all rect-angular 2D charts), and override its callback method updateDisplayList. Flex calls the updateDis-playList() method when the component is added to a container using the addChild() method, and when the component’s invalidate DisplayList() method is called. Don’t miss the fact that the meth-od updateList() first calls its peer from the superclass to provide the default drawing of the chart, and then paints the limits area on top of it.

We’ve also added a new public property limits to this class:

package com.theriabook.limits {

import mx.charts.chartClasses.CartesianChart;

public class ExtendedCartesianChart extends CartesianChart {

[Inspectable(category=”Data”,arrayType=”com.theriabook.limits.CartesianLimit”)]

public var limits: Array;

protected override function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

if(limits != null)

for(var i:int =0;i<limits.length;i++)

limits[i].paint(this);

}

}

}

Listing 14.3 The ActionScript class ExtendedCartesianChart

Finally, let’s create an MXML application to test our chart:

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” xmlns:

Developing Custom ChartsCHAPTER 14

Page 575: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 579

ns1=”com.theriabooks.limits.*”>

<mx:Script>

[Bindable]

public var results:Object = [

{Expense: “Taxes”, Amount: 2000},

{Expense: “Rent”, Amount: 1500},

{Expense: “Bills”, Amount: 100},

{Expense: “Car”, Amount: 450},

{Expense: “Gas”, Amount: 100},

{Expense: “Food”, Amount: 200}

];

</mx:Script>

<ns1:ExtendedCartesianChart x=”82” y=”182” id=”linechart1”

dataProvider=”{results}” showDataTips=”true” width=”300” height=”200”>

<ns1:fill>

<mx:SolidColor color=”0xffffff”/>

</ns1:fill>

<ns1:horizontalAxis>

<mx:CategoryAxis padding=”0” dataProvider=”{results}”

categoryField=”Expense”/>

</ns1:horizontalAxis>

<ns1:series>

<mx:Array>

<mx:LineSeries form=”curve” yField=”Amount” name=”Apple” >

</mx:LineSeries>

</mx:Array>

</ns1:series>

<ns1:limits>

<mx:Array>

<ns1:CartesianLimit minX=”Rent” maxX=”Food” minY=”400” maxY=”800”/>

</mx:Array>

</ns1:limits>

</ns1:ExtendedCartesianChart>

</mx:Application>

Listing 14.4 The mxml application Limits.mxml

We put the tag <mx:Array> inside the <ns1:limits> just to make the code more generic as the ap-plication may need more than one <ns1:CartesianLimit> area.

Developing Custom ChartsCHAPTER 14

Page 576: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

580 RIA WITH ADOBE FLEX AND JAVA

Run the application and it’ll display the chart with a red rectangle:

Figure 14.3 Adding a rectangle with specified limits to the chart

Working with Larger Data SetsIt often happens that you need to display a relatively large data set inside a chart. Let’s try to plot 100 data points as a line chart. This time we’ll use MXML. The data values for each point will be generated randomly for a hundred days starting January 1, 2006. All these data will be put in the array that will be used as a data provider for our line chart.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”absolute” backgroundColor=”0xFFFFFF”>

<mx:Script>

<![CDATA[

private function createData(count:uint) : Array {

var date: Date = new Date(2006,0); // starting from the new year day

Developing Custom ChartsCHAPTER 14

Page 577: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 581

var day : uint = 1000*60*60*24; //msec in one day

var retval: Array = new Array();

for(var i:int = 0; i< count;i++,

// Add one day

date.setMilliseconds(date.getMilliseconds()+day))

// Set the random data for the current day

retval[i] = { Date: date.toDateString(), Value: Math.random()*100} ;

return retval;

}

private function parseDate(date: String) : Date {

return new Date(date);

}

[Bindable]

private var dataSet:Array = createData(100);

]]>

</mx:Script>

<mx:LineChart x=”127” y=”72” width=”400” height=”300” id=”linechart1”

dataProvider=”{dataSet}” showDataTips=”true”>

<mx:seriesFilters><mx:Array/></mx:seriesFilters>

<mx:horizontalAxis>

<mx:DateTimeAxis dataUnits=”days” parseFunction=”parseDate”/>

</mx:horizontalAxis>

<mx:series>

<mx:LineSeries displayName=”Series 1” xField=”Date” yField=”Value”>

<mx:lineStroke>

<mx:Stroke weight=”1” color=”0xff0000” />

</mx:lineStroke>

</mx:LineSeries>

</mx:series>

</mx:LineChart>

</mx:Application>

Listing 14.5 The line chart with 100 data points: LineChart100.mxml

Developing Custom ChartsCHAPTER 14

Page 578: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

582 RIA WITH ADOBE FLEX AND JAVA

This code will produce a line chart that may look like this:

Figure 14.4 The line chart with 100 data points

Unfortunately, as we start moving the cursor over the chart, we recognize that there’s an issue – the tool tip is shown only when we put our cursor over the actual data point (shown as a little circle). Even with 100 points it makes extracting the data from the chart difficult. And what if we want to plot multiple series and compare values at the same date?

Adding a Vertical LineIdeally we’d like to show the vertical line moving horizontally over the chart, following your current mouse position. We’d also like to add data tips showing the data for multiple stocks (assuming that each line represents some stock prices) at the selected point in time.

Figure 14.5 A two-stock chart with a vertical line

Developing Custom ChartsCHAPTER 14

Page 579: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 583

Let’s subclass our chart and implement this functionality.We’ll split the implementation into multiple classes for demonstration purposes. The first version of our program will display a line chart with two data series emulating the data on two stocks. We’ll also display the vertical line in the chart as shown in Figure 14.5.

At this point, we are going to create two ActionScript classes:

• ExtendedLineChart: This class extends the LineChart and is responsible for drawing the line chart

• VLineLayer: This class extends the ProgrammaticSkin class and its purpose is to draw the vertical line on top of the ExtendableLine Chart.

Non-Typical Use of SkinningSkinning in Flex can be implemented by rendering graphics (a jpg file). But skins can also be ren-dered programmatically as an ActionScript class.

Originally, Flex skinning was created to change the look-and-feel of the visual parts of the same component easily (for example, a popular media player WinAmp offers hundreds of skins). But this time we’ll use the skinning capabilities of Flex just to draw the line on top of the chart. We aren’t planning to change the skin of the thin vertical line, but rather use this technique to put an object on top of the chart and control it (we’re going to move this line along the horizontal axis).

Drawing a line is a fairly simple task – we’ll use a new VlineLayer class inherited from the Program-maticSkin class. This class will draw the line (it’ll play the role of a skin) and add it as a child to our chart.

package com.theriabook.charts.line1{

import mx.skins.ProgrammaticSkin;

import flash.geom.Rectangle;

public class VLineLayer extends ProgrammaticSkin {

protected override function updateDisplayList(

unscaledWidth:Number, unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

var rect: Rectangle = (parent as ExtendedLineChart).viewBounds;

graphics.clear();

Developing Custom ChartsCHAPTER 14

Page 580: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

584 RIA WITH ADOBE FLEX AND JAVA

if(rect.x<=parent.mouseX && (rect.x+rect.width)>=parent.mouseX &&

rect.y<=parent.mouseY && (rect.y+rect.height)>=parent.mouseY) {

graphics.lineStyle(1,0x808080);

graphics.moveTo(parent.mouseX, rect.y);

graphics.lineTo(parent.mouseX, rect.y+rect.height);

}

}

}

}

Listing 14.6 The VlineLayer class that draws a line

Flex designers declared the CartesianChart’s property dataRegion that defines the coordinates of the actual data rectangle as protected. Since we need this information to be accessible from the outside of this inheritance hierarchy, we’ve defined in our subclass a viewBounds public property that will return the value of the dataRegion to any class that may need it:

public function get viewBounds() : Rectangle {

return super.dataRegion;

}

And the complete code for the ExtendedLineChart will look like:

package com.theriabook.charts.line1{

import mx.charts.LineChart;

import flash.events.MouseEvent;

import flash.geom.Rectangle;

public class ExtendedLineChart extends LineChart {

private var skin: VLineLayer;

public function ExtendedLineChart() {

addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);

}

protected override function updateDisplayList(

unscaledWidth:Number, unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

skin.width = width;

skin.height = height;

}

protected override function createChildren() : void {

super.createChildren();

if(skin == null)

addChild(skin = new VLineLayer());

}

public function get viewBounds() : Rectangle {

return super.dataRegion;

}

private function onMouseMove(event: MouseEvent) : void {

skin.invalidateDisplayList();

}

}

}

Listing 14.7 The ActionScript class ExtendedLineChart

Let’s modify our MXML application from Listing 14.5 to support a two-line series:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”absolute” xmlns:ns1=”com.theriabook.charts.line1.*”>

<mx:Script>

<![CDATA[

private function createData(count:uint) : Array {

var date: Date = new Date(2006,0);

var day : uint = 1000*60*60*24;

var retval: Array = new Array();

for(var i:int = 0; i< count;i++,

date.setMilliseconds(date.getMilliseconds()+day))

retval[i] = { Date: date.toDateString(),

ABCD: 50+Math.random()*10, XYZT:Math.random()*100} ;

return retval;

}

private function parseDate(date: String) : Date {

return new Date(date);

}

[Bindable]

private var dataSet:Array = createData(100);

]]>

</mx:Script>

<ns1:ExtendedLineChart x=”127” y=”72” width=”400” height=”300” id=”linechart1”

dataProvider=”{dataSet}” showDataTips=”false”>

Developing Custom ChartsCHAPTER 14

Page 581: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 585

if(rect.x<=parent.mouseX && (rect.x+rect.width)>=parent.mouseX &&

rect.y<=parent.mouseY && (rect.y+rect.height)>=parent.mouseY) {

graphics.lineStyle(1,0x808080);

graphics.moveTo(parent.mouseX, rect.y);

graphics.lineTo(parent.mouseX, rect.y+rect.height);

}

}

}

}

Listing 14.6 The VlineLayer class that draws a line

Flex designers declared the CartesianChart’s property dataRegion that defines the coordinates of the actual data rectangle as protected. Since we need this information to be accessible from the outside of this inheritance hierarchy, we’ve defined in our subclass a viewBounds public property that will return the value of the dataRegion to any class that may need it:

public function get viewBounds() : Rectangle {

return super.dataRegion;

}

And the complete code for the ExtendedLineChart will look like:

package com.theriabook.charts.line1{

import mx.charts.LineChart;

import flash.events.MouseEvent;

import flash.geom.Rectangle;

public class ExtendedLineChart extends LineChart {

private var skin: VLineLayer;

public function ExtendedLineChart() {

addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);

}

protected override function updateDisplayList(

unscaledWidth:Number, unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

skin.width = width;

skin.height = height;

}

protected override function createChildren() : void {

super.createChildren();

if(skin == null)

addChild(skin = new VLineLayer());

}

public function get viewBounds() : Rectangle {

return super.dataRegion;

}

private function onMouseMove(event: MouseEvent) : void {

skin.invalidateDisplayList();

}

}

}

Listing 14.7 The ActionScript class ExtendedLineChart

Let’s modify our MXML application from Listing 14.5 to support a two-line series:

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”absolute” xmlns:ns1=”com.theriabook.charts.line1.*”>

<mx:Script>

<![CDATA[

private function createData(count:uint) : Array {

var date: Date = new Date(2006,0);

var day : uint = 1000*60*60*24;

var retval: Array = new Array();

for(var i:int = 0; i< count;i++,

date.setMilliseconds(date.getMilliseconds()+day))

retval[i] = { Date: date.toDateString(),

ABCD: 50+Math.random()*10, XYZT:Math.random()*100} ;

return retval;

}

private function parseDate(date: String) : Date {

return new Date(date);

}

[Bindable]

private var dataSet:Array = createData(100);

]]>

</mx:Script>

<ns1:ExtendedLineChart x=”127” y=”72” width=”400” height=”300” id=”linechart1”

dataProvider=”{dataSet}” showDataTips=”false”>

Developing Custom ChartsCHAPTER 14

Page 582: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

586 RIA WITH ADOBE FLEX AND JAVA

<ns1:seriesFilters>

<mx:Array/>

</ns1:seriesFilters>

<ns1:horizontalAxis>

<mx:DateTimeAxis dataUnits=”days” parseFunction=”parseDate”/>

</ns1:horizontalAxis>

<ns1:series>

<mx:LineSeries displayName=”ABCD” xField=”Date” yField=”ABCD”>

<mx:lineStroke>

<mx:Stroke weight=”1” color=”0x00ff” />

</mx:lineStroke>

</mx:LineSeries>

<mx:LineSeries displayName=”XYZT” xField=”Date” yField=”XYZT”>

<mx:lineStroke>

<mx:Stroke weight=”1” color=”0xff0000” />

</mx:lineStroke>

</mx:LineSeries>

</ns1:series>

<ns1:fill>

<mx:SolidColor color=”0xFFFFFF” />

</ns1:fill>

</ns1:ExtendedLineChart>

</mx:Application>

Listing 14.8 LineChartVLine1.mxml

Run this program and you’ll see a chart that looks like Figure 14.5.

This class draws a movable vertical line, but we also need a tool tip that can show the data of mul-tiple series based on the position of the vertical line. The standard tool tip window can only show the information about one series, so we’ll create a new class to implement it (we’ll omit some prop-erties declarations to make the code more readable):

public class ToolTip extends UIComponent {

private var _borderWidth : uint = 1;

private var _borderColor : uint = 0xff;

private var _backColor : uint = 0xffff00;

private var _backAlpha: Number = 0.5;

private var textField : TextField = new TextField();

protected override function createChildren():void {

Developing Custom ChartsCHAPTER 14

Page 583: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 587

textField.background = false;

textField.x=2;

textField.y=2;

textField.selectable = false;

addChild(textField);

}

protected override function measure():void {

super.measure();

// add eight pixels around the text field in the tool tip to

// just to look prettier

measuredWidth = measuredMinWidth = textField.textWidth +2;

measuredHeight= measuredMinHeight= textField.textHeight+2;

}

protected override function updateDisplayList(

unscaledWidth:Number, unscaledHeight:Number):void {

textField.width=unscaledWidth-4;

textField.height=unscaledHeight-4;

graphics.clear();

graphics.lineStyle(_borderWidth, _borderColor);

graphics.beginFill(_backColor, _backAlpha);

graphics.drawRect(0,0,unscaledWidth, unscaledHeight);

}

public function get text() :String {

return textField.text;

}

public function set text(text:String) : void {

textField.text = text;

invalidateSize();

validateNow();

}

}

Listing 14.9 The ActionScript class ToolTip

We’ll display the text in the tool tip using a text contol. To do this, we override the createChildren method of the UIComponent, which sets no background, margins, makes it non-editable, and adds the text as a child component.

In the measure callback we’ll add a couple of pixels margin around the text.

Developing Custom ChartsCHAPTER 14

Page 584: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

588 RIA WITH ADOBE FLEX AND JAVA

The next step is to implement a function that can take an x-coordinate and return the information about the nearest data point for each series. FlexCharts support two functions for converting data points into the actual screen coordinates and vice versa – localToData and dataToLocal. But local-ToData requires both x and y coordinates, and we don’t know the y coordinate of the point at the current x since all we know is the x coordinate of the vertical line. The solution is to create a list of all actual coordinates for all the data points and then use it to determine the closest data points at a given x coordinate. Our assumptions are:

• All values are provided in ascending order (sorted by date) .• Each data series has the same number of data points and the x coordinate of each data series

is the same for a given date.

Now we can implement a simple PointCache class to “remember” the closest data points at each x coordinate. The method getPointIndexByX will return the index of the data point inside the data series.

public class PointCache {

private var points : Array ;

private var chart : ExtendedLineChart;

public function PointCache(chart: ExtendedLineChart) {

this.chart = chart;

}

public function reset() : void {

points = null;

}

public function getPointIndexByX(x: int, index:int=0) : int {

if(points == null)

init();

if(!chart.viewBounds.contains(chart.mouseX,chart.mouseY))

return -1;

var row: Array = points[index] as Array;

for(var i:int = 0;i< row.length;i++) {

if(row[i].x > x) {

if(i == 0) return 0;

// deltas are distances btwn the line and the closest ponts

// on the left and on the right

var delta0 : Number = x-row[i-1];

var delta1: Number = row[i]-x;

return delta0<delta1 ? i-1 : i; }

Developing Custom ChartsCHAPTER 14

Page 585: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 589

}

return row.length-1;

}

private function init():void {

points = new Array(chart.series.length);

if(points.length == 0) return ;

var xField: String = chart.series[0].xField, yField: String;

for(var j:int = 0;j<chart.series.length;j++) {

yField = chart.series[j].yField;

var dp: ICollectionView = chart.series[j].dataProvider

as ICollectionView;

var elem: Array = new Array(dp.length);

for(var i:int = 0;i<dp.length;i++)

elem[i] = chart.dataToLocal(dp[i][xField],

dp[i][yField]);

points[j] = elem;

}

}

}

Listing 14.10 The ActionScript class PointCache

It’s relatively easy to modify this class to support an arbitrary order of points by sorting the points based on the x coordinate.

Finally, we can put together a new ExtendedLineChart class. Since we want to show in the tool tip window the date, the name, and the value for the element on this date (the x coordinate) for all se-ries, we’ll have to implement a custom tool tip getToolTipText formating function that can extract the information from all the series and present it nicely:

public class ExtendedLineChart extends LineChart {

private var _cache: PointCache ;

private var _layer: VLineLayer ;

private var _dataTip: ToolTip = new ToolTip();

private var tipDateFormatter: DateFormatter;

private var tipNumberFormatter: NumberFormatter;

public function ExtendedLineChart() {

_cache = new PointCache(this);

_layer = new VLineLayer();

addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);

Developing Custom ChartsCHAPTER 14

Page 586: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

590 RIA WITH ADOBE FLEX AND JAVA

tipDateFormatter = new DateFormatter();

tipDateFormatter.formatString = “MMM DD, YYYY”;

tipNumberFormatter = new NumberFormatter();

tipNumberFormatter.precision = 2;

}

public function get viewBounds() : Rectangle {

return super.dataRegion;

}

// calculate and update position of the tool tip

protected override function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

_layer.width = width;

_layer.height = height;

var index: int = _cache.getPointIndexByX(mouseX);

if(index>=0) {

_dataTip.visible = true;

_dataTip.text = getToolTipText(index);

_dataTip.width = _dataTip.measuredWidth;

_dataTip.height= _dataTip.measuredHeight;

// move the text box two pixels to the right

// and one box hight up, so the mouse pointer

// is below the tool tip box

_dataTip.x = mouseX+2;

_dataTip.y = mouseY-_dataTip.height;

}

else

_dataTip.visible = false;

}

private function getToolTipText(index: int): String {

var sb:String =”” ;

for(var i:int=0;i<series.length;i++) {

if(i==0)

sb +=

tipDateFormatter.format(series[0].dataProvider[index][series[0].xField])+”\n”;

var elem: LineSeries = this.series[i];

Developing Custom ChartsCHAPTER 14

Page 587: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 591

var dp: ICollectionView = elem.dataProvider as ICollectionView;

var pt:Object = dp[index];

sb += elem.displayName + “: “;

sb += tipNumberFormatter.format(pt[elem.yField]);

sb += “\n”;

}

return sb;

}

protected override function createChildren() : void {

super.createChildren();

if(!contains(_dataTip))

addChild(_dataTip);

if(!contains(_layer))

addChild(_layer);

}

private function onMouseMove(event: MouseEvent) : void {

invalidateDisplayList();

}

public override function invalidateProperties():void {

if(_cache!=null)

_cache.reset();

super.invalidateProperties();

}

public override function invalidateSize():void {

if(_cache!=null)

_cache.reset();

super.invalidateSize();

}

}

Listing 14.11 A new version of the ExtendedLineChart

Once you run the application, you can see the chart shown in Figure 14.6 with the tool tip contain-ing the information on two data points that represent the line series for ABCD and XYZT, located close to the vertical line. It doesn’t really matter now if your mouse pointer is located by the data point: the vertical line (aka skin ) position guarantees that the proper data will be displayed.

Developing Custom ChartsCHAPTER 14

Page 588: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

592 RIA WITH ADOBE FLEX AND JAVA

Figure 14.6 A tool tip with the data about two series: ABCD and XYZT

Adding Scrolling and ZoomingIt’s often desirable to let the user zoom in on the data and scroll zoomed charts. With Flex Charts this can be done easily by adjusting the minimum and maximum values of the x axis (the date range in our case).

First, we’ll implement a very simple zoom function in the ExtendedLineChart class, which will in-crease or decrease the range of the x axis by some predefined value. Note that when we change the axis’s range, we also have to reset the point cache.

public function zoom(delta: int ) : void {

var axis: DateTimeAxis = DateTimeAxis(horizontalAxis);

var newmin : Date= maxDate(axisMin,addDays(axis.minimum, -delta));

var newmax : Date= minDate(axisMax,addDays(axis.maximum, delta));

if(diffDays(newmax,newmin) >= MIN_RANGE) {

axis.minimum = newmin;

axis.maximum = newmax;

_cache.reset();

this.invalidateDisplayList();

}

}

private function get axisMin() : Date {

return DateTimeAxis(horizontalAxis).

parseFunction(series[0].dataProvider[0][series[0].xField]);

}

Developing Custom ChartsCHAPTER 14

Page 589: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 593

private function get axisMax() : Date {

var dp: ICollectionView = series[0].dataProvider;

return DateTimeAxis(horizontalAxis).

parseFunction(dp[dp.length-1][series[0].xField]);

}

// utility functions to work with dates

private static function addDays(date: Date, days:int): Date {

return new Date(date.getTime()+days*MSEC_IN_DAY);

}

private static function minDate(date1: Date, date2: Date):Date {

return date1.getTime()<date2.getTime() ? date1 : date2;

}

private static function maxDate(date1: Date, date2: Date):Date {

return date1.getTime()>date2.getTime() ? date1 : date2;

}

private static function diffDays(date1 : Date, date2: Date): Number {

return (getDate(date1).getTime()-getDate(date2).getTime())/MSEC_IN_DAY;

}

Listing 14.12 Zooming and date processing

The function zoom() ensures that the axis range won’t go outside the underlying data values and will always be at least MIN_RANGE days.

Now we can simply add two buttons to our MXML file to zoom the chart in and out. Usually you’d provide a more sophisticated mechanism for zooming the chart, but we’ll keep this example simple for demonstration purposes.

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

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

xmlns:ns1=”com.theriabook.charts.zoom.*” layout=”absolute”>

<mx:Script>

<![CDATA[

private function createData(count:uint) : Array {

var date: Date = new Date(2006,0);

var day : uint = 1000*60*60*24;

var retval: Array = new Array();

for(var i:int = 0; i< count;i++, date.setMilliseconds(date.getMilliseconds()+day))

retval[i] = { Date: date.toDateString(), ABCD: 50+Math.random()*10,

Developing Custom ChartsCHAPTER 14

Page 590: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

594 RIA WITH ADOBE FLEX AND JAVA

XYZT: 75+Math.random()*50} ;

retval[0].ABCD = 0;

retval[retval.length-1].ABCD=0;

return retval;

}

private function parseDate(date: String) : Date {

return new Date(date);

}

[Bindable]

private var dataSet:Array = createData(100);

]]>

</mx:Script>

<mx:VBox x=”33” y=”24” height=”445” width=”606”>

<ns1:ExtendedLineChart width=”600” height=”400” id=”myChart”

dataProvider=”{dataSet}” showDataTips=”false” >

<ns1:seriesFilters>

<mx:Array/>

</ns1:seriesFilters>

<ns1:horizontalAxis>

<mx:DateTimeAxis dataUnits=”days” parseFunction=”parseDate”/>

</ns1:horizontalAxis>

<ns1:series>

<mx:LineSeries displayName=”ABCD” xField=”Date” yField=”ABCD”>

<mx:lineStroke>

<mx:Stroke weight=”1” color=”0x00ff” />

</mx:lineStroke>

</mx:LineSeries>

<mx:LineSeries displayName=”XYZT” xField=”Date” yField=”XYZT”>

<mx:lineStroke>

<mx:Stroke weight=”1” color=”0xff0000” />

</mx:lineStroke>

</mx:LineSeries>

</ns1:series>

<ns1:fill>

<mx:SolidColor color=”0xFFFFFF” />

</ns1:fill>

</ns1:ExtendedLineChart>

<mx:HBox width=”100%”>

<mx:Button label=”Zoom In” id=”zoomIn” fontWeight=”bold” click=”myChart.zoom(-2)”/>

<mx:Button label=”Zoom Out” id=”zoomOut” fontWeight=”bold” click=”myChart.zoom(2)”/>

</mx:HBox>

Developing Custom ChartsCHAPTER 14

Page 591: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 595

</mx:VBox>

</mx:Application>

Listing 14.13 The final application LineChartVLineZoom.mxml

Figure 14.7 Running LineChartVLineZoom.mxml before zooming in

Press the Zoom In button several times, and the Line chart will change as shown below. The zoom-ing ration is controlled by the value you’re passing to the zoom() function under the zoom but-tons.

<mx:Button label=”Zoom In” id=”zoomIn” fontWeight=”bold” click=”myChart.zoom(-2)”/>

<mx:Button label=”Zoom Out” id=”zoomOut” fontWeight=”bold”

click=”myChart.zoom(2)”/>

Developing Custom ChartsCHAPTER 14

Page 592: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

596 RIA WITH ADOBE FLEX AND JAVA

Figure 14.8 Running LineChartVLineZoom.mxml after zooming in

After you zoom in to the chart, you can’t see the entire time interval. For example, you don’t see the January data on Figure 14.8. Hence, we need to implement horizontal scrolling so the user can drag the mouse to the left or right.

For this scrolling we’ll introduce the member variables _ancorX and _ancorY in the Extended-LineChart class that will store the last mouse position and define the mouse down and mouse up event handlers that will toggle the isDragging flag.

Our new onMouseMove event handler will compute the required change based on the new and old mouse location and adjust the axis’s minimum and maximum accordingly. The final version of the ExtendedLineChart class is shown in Listing 14.14.

public class ExtendedLineChart extends LineChart {

private static const MIN_RANGE: int= 7;

private static const MSEC_IN_DAY:int = 24*60*60*1000;

private var isDragging : Boolean = false;

private var _ancorX: int;

private var _ancorY: int;

private var _cache: PointCache ;

private var _layer: VLineLayer ;

Developing Custom ChartsCHAPTER 14

Page 593: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 597

private var _dataTip: ToolTip = new ToolTip();

private var tipDateFormatter: DateFormatter;

private var tipNumberFormatter: NumberFormatter;

public function ExtendedLineChart() {

_cache = new PointCache(this);

_layer = new VLineLayer();

addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);

tipDateFormatter = new DateFormatter();

tipDateFormatter.formatString = “MMM DD, YYYY”;

tipNumberFormatter = new NumberFormatter();

tipNumberFormatter.precision = 2;

addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);

addEventListener(MouseEvent.MOUSE_UP, onMouseUp);

addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);

}

private function onMouseDown(evt: MouseEvent) :void {

isDragging = true;

_ancorX = mouseX;

_ancorY = mouseY;

invalidateDisplayList() ;

}

private function onMouseUp(evt:MouseEvent) :void {

isDragging = false;

invalidateDisplayList() ;

}

private function onMouseMove(event: MouseEvent) : void {

if(!isDragging) {

invalidateDisplayList();

return;

}

var axis: DateTimeAxis = DateTimeAxis(horizontalAxis);

var min : Date = axisMin;

var max : Date = axisMax;

var vWidth : int = dataToLocal(axis.maximum).x -dataToLocal(axis.minimum).x;

var change: int = (axis.maximum.getTime()-axis.minimum.getTime())*-(mouseX-

_ancorX)/vWidth;

Developing Custom ChartsCHAPTER 14

Page 594: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

598 RIA WITH ADOBE FLEX AND JAVA

var newmin: Date = new Date(axis.minimum.getTime()+change);

var newmax: Date = new Date(axis.maximum.getTime()+change);

if(newmin.getTime()<min.getTime()) {

newmax.setTime(newmax.getTime()+min.getTime()-newmin.getTime());

newmin = min;

} else

if(newmax.getTime()>max.getTime()) {

newmin.setTime(newmin.getTime()+max.getTime()-newmax.getTime());

newmax = max;

}

_ancorX = mouseX;

_ancorY = mouseY;

axis.minimum = newmin;

axis.maximum = newmax;

_cache.reset();

this.invalidateDisplayList();

}

public function zoom(delta: int ) : void {

var axis: DateTimeAxis = DateTimeAxis(horizontalAxis);

var newmin : Date= maxDate(axisMin,addDays(axis.minimum, -delta));

var newmax : Date= minDate(axisMax,addDays(axis.maximum, delta));

if(diffDays(newmax,newmin) >= MIN_RANGE) {

axis.minimum = newmin;

axis.maximum = newmax;

_cache.reset();

this.invalidateDisplayList();

}

}

private function get axisMin() : Date {

return DateTimeAxis(horizontalAxis).parseFunction(series[0].dataProvider[0][seri

es[0].xField]);

}

private function get axisMax() : Date {

var dp: ICollectionView = series[0].dataProvider;

return DateTimeAxis(horizontalAxis).parseFunction(dp[dp.length-

1][series[0].xField]);

}

private static function addDays(date: Date, days:int): Date {

Developing Custom ChartsCHAPTER 14

Page 595: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 599

return new Date(date.getTime()+days*MSEC_IN_DAY);

}

private static function minDate(date1: Date, date2: Date):Date {

return date1.getTime()<date2.getTime() ? date1 : date2;

}

private static function maxDate(date1: Date, date2: Date):Date {

return date1.getTime()>date2.getTime() ? date1 : date2;

}

private static function diffDays(date1 : Date, date2: Date): Number {

return (getDate(date1).getTime()-getDate(date2).getTime())/MSEC_IN_DAY;

}

private static function getDate(date : Date) : Date {

return new Date(date.getFullYear(), date.getMonth(), date.getDate());

}

private static function toDateString(date:Date) : String {

var d: DateFormatter = new DateFormatter();

d.formatString = “MM/DD/YY JJ:NN:SS”;

return d.format(date);

}

public function get viewBounds() : Rectangle {

return super.dataRegion;

}

protected override function updateDisplayList(unscaledWidth:Number,

unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);

_layer.width = width;

_layer.height = height;

var index: int = _cache.getPointIndexByX(mouseX);

_dataTip.visible = index>=0 ;

if(_dataTip.visible) {

_dataTip.text = getToolTipText(index);

_dataTip.width = _dataTip.measuredWidth;

_dataTip.height= _dataTip.measuredHeight;

_dataTip.x = mouseX+2;

_dataTip.y = mouseY-_dataTip.height;

Developing Custom ChartsCHAPTER 14

Page 596: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

600 RIA WITH ADOBE FLEX AND JAVA

}

}

private function getToolTipText(index: int): String {

var sb:String =”” ;

for(var i:int=0;i<series.length;i++) {

if(i==0)

sb +=

tipDateFormatter.format(series[0].dataProvider[index][series[0].xField])+”\n”;

var elem: LineSeries = this.series[i];

var dp: ICollectionView = elem.dataProvider as ICollectionView;

var pt:Object = dp[index];

sb += elem.displayName + “: “;

sb += tipNumberFormatter.format(pt[elem.yField]);

sb += “\n”;

}

return sb;

}

protected override function createChildren() : void {

super.createChildren();

if(!contains(_dataTip))

addChild(_dataTip);

if(!contains(_layer))

addChild(_layer);

}

public override function invalidateProperties():void {

if(_cache!=null)

_cache.reset();

super.invalidateProperties();

}

public override function invalidateSize():void {

if(_cache!=null)

_cache.reset();

super.invalidateDisplayList();

}

}

Listing 14.14 The final version of the class ExtendedLineChart

Developing Custom ChartsCHAPTER 14

Page 597: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 601

SummaryIn this chapter we gradually built an application that should give you a good start in developing your own application that can look as good or even better than Google Finance. We’ve hard-coded the symbols of imaginary stocks ABCD and XYZT in our MXML application to keep the reader fo-cused on the charting aspects of Flex, but if you’ve read this far, it shouldn’t be too difficult to add some external data feed that will serve as the provider for this chart, as shown in Chapter 5. We’d like to thank the folks at GreenPoint, Inc., (http://gpoint.com/) for their valuable input to this chapter.

Developing Custom ChartsCHAPTER 14

Page 598: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 603

Integration with External Applications

15CHAPTER

Page 599: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

604 RIA WITH ADOBE FLEX AND JAVA

This subject of this chapter assumes a broad use of technologies besides the ones provided by Adobe, let alone Adobe Flex. Whenever you integrate two systems you need intimate familiarity with both. Obviously we had to pick one external application that the reader will be familiar with so that, besides the educational value, our solutions would have immediate practical use. We picked Microsoft Excel.

Hence the composition of our chapter includes references to LocalConnection and External API intervened with JavaScript, VBA, and XSLT to name just a few. The ride might feel hard, but it is worth it: by the end of the chapter we’ll have presented a series of generic solutions that you can use off-the-shelf.

All examples are thoroughly commented, although we permitted ourselves less detail on the Mi-crosoft subjects.

Using Excel as a counter-party example, we’ll address the needs of Flex applications to commu-nicate with the standalone native applications running on a user’s computer. We’ll also show the integration of Flex and a spreadsheet in a single HTML page. The Flex-to-Flex communications, ir-respective of the mode the Flex applications are running in (embedded inside an ActiveX container or run by a standalone Flash Player), will get on our radar as well.

Overview of Flash External Interfacing MethodsHistorically, releases of Flash have been consistently introducing more and more methods of inter-facing with external applications.

First, developers got access to a bunch of methods like FSCommand, CallFrame, CallLabel, and SetVariable. These methods, exposed by a Flash ActiveX plug-in, allow one-way control of the Flash from a hosting container.

Macromedia Flash 6.0 added SharedObject to a set of available ActionScript classes. The Shared

Integration with External ApplicationsCHAPTER 15

Integration with External Applications

Page 600: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 605

Object class is used to read and store limited amounts of data on a user’s computer. Besides its primary function, SharedObject allows a data exchange between two Flash movies running on the same computer (a second type of communication).

Flash 6.0 also provides a LocalConnection class. LocalConnection objects can communicate only among SWF files that are running on the same client computer, but they can be running in differ-ent applications – again, we’re talking about Flash-to-Flash communication. Note that the Local-Connection API and security model underwent several significant changes with further releases.

Finally, release 8.0 adds ExternalInterface to the developers’ arsenal. The External API enables straightforward two-way communication between ActionScript and the host application, i.e., you can call ActionScript functions from the Flash Player container and invoke container functions from ActionScript as well.

In this chapter we’ll discuss only communications using ExternalInterface and LocalConnection. Even though the FSCommand & Co. methods are fully supported by the current Flash Player (9.0 when this was written), it’s recommended to avoid them for integrating applications. The External API is more straightforward, flexible, and generally applicable.

Using External APIWe start with the simplest External API example possible – communications between Flex SWF embedded in HTML and JavaScript running inside the Web page.

Calling an external JavaScript function is trivial: import flash.external.ExternalInterface;

var retVal:Object = ExternalInterface.call( “javaScriptFunction”, arg1, arg2, …,

argN );

This will call a JavaScript function named “javaScriptFunction” declared somewhere on the HTML page with arguments supplied as the rest of the parameters and will get the result of the function execution as a retVal variable. You can use a wide range of types for arguments and the return value: strings, numbers, Boolean values, instances of Date class, Object, Array, and any combination thereof like Array of Objects or Object with Array properties. User-defined types are not supported.

If you’re just starting Flex application development, everything should look so natural to you that it doesn’t even deserve further explanation. However, if you’re a seasoned Flash developer, you may notice a number of improvements. Unlike FSCommand that accepts one and only one argument of the String type, you can use any number of parameters with an ExternalInterface.call. Another im-provement over the “old approach” is that dealing with return values is absolutely transparent: you just get the value returned by JavaScript function as a result of its operation. In dark ancient days you’d have to cope with dynamic properties and either Object.watch or onEnterFrame events.

Integration with External ApplicationsCHAPTER 15

Page 601: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

606 RIA WITH ADOBE FLEX AND JAVA

The code snippet above has a serious problem: if you accidentally run your SWF in a hosting appli-cation that has no External API support (like a standalone Flash Player), you’ll get an error message. When you have to develop a Flex application that works in different containers and not all of them support the External API, you definitely need a way to detect the availability of the External API. To do this, it’s necessary to check a static property available to the ExternalInterface class: import flash.external.ExternalInterface;

if ( ExternalInterface.available )

ExternalInterface.call( “javaScriptFunctionName”, arg1, arg2, …, argN );

To declare an ActionScript function as callable from JavaScript you have to per-

form two steps. First, you have to declare a callback function in your Action-

Script class:

private function myCallback(arg1:type1, arg2:type2, …, argN:typeN):Type {

return something;

}

Second, you have to register your function using ExternalInterface.addCallback:

import flash.external.ExternalInterface;

if ( ExternalInterface.available )

ExternalInterface.addCallback( “myFunction”, myCallback );

The first parameter to ExternalInterface.addCallback is the name of the function as it will be visible to JavaScript. The second parameter is the function closure to invoke. Notice that you may expose your ActionScript function to JavaScript under a different name, but for the sake of clarity, using the same name is recommended.

ExternalInterface.addCallback supports different kinds of functions as a second parameter: a method closure referencing an instance method, a static class method, even an in-line anonymous function as in the example below:

import flash.external.ExternalInterface;

import mx.controls.Alert;

if ( ExternalInterface.available )

ExternalInterface.addCallback( “myFunction”, function():void{ Alert.

show(“Called fromJavaScript”); } );

When you pass an instance method closure as an argument, the callback is directed at the method

Integration with External ApplicationsCHAPTER 15

Page 602: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 607

of a particular object instance, so you can access other instance methods or properties of this ob-ject. The set of supported arguments’ types is exactly the same as for arguments passed with Ex-ternalInterface.call.

Typically, callback functions are registered when an application is created, i.e., in the creationCom-plete event handler’s chain. However, you can register a callback at any time during the application lifecycle, or even replace one callback with another if necessary. However, there’s no method to re-move a callback completely. As a workaround, replace already registered callbacks with a function that doesn’t do anything.

Believe it or not, this is all you have to know about the External API to start using it. The only im-portant point left uncovered is the security aspect. When you’re accessing a Flex function from JavaScript running on an HTML page, or when you call a JavaScript function from Flex, stringent security restrictions are applied. According to the default security policy both HTML and SWF files must originate from the same domain. In most cases this requirement is quite easy to follow.

If you want to relax the restrictions associated with the ExternalInterface.call method, then you have to tweak the allowScriptAccess parameter of the HTML <OBJECT> tag or the attribute with same name in the HTML <EMBED> tag. Changing this setting from the sameDomain default value to liberate always lets you execute JavaScript functions regardless of whether the JavaScript source is in the same domain as Flex swf. If you set the aforementioned property to never, you effectively cause any ExternalInterface.call invocation to fail with a security error.

To relax the security policy that’s applied to JavaScript calling a Flex function, you must use one of the ActionScript methods, Security.allowDomain or Security.allowInsecureDomain. Supply the list of domain names you want to grant access to as parameters:

Security.allowDomain(“www.host-a.com”, “www.host-b.net”);

Or use the wildcard “*” to grant access to the JavaScript code loaded from any host:

Security.allowDomain(“*”);

The latest option is to edit your global Flash Player Security Settings and add certain domains to trusted sites. You must do this if you run this chapter’s examples from your local file system. To do this visit the Global Security Settings panel at http://www.macromedia.com/support/documentation/en/flash-player/help/settings_manager04.html and add directory with examples to the list of trusted sites.

We’re done with the theory so let’s proceed with a small working example. What we need is just a simple Flex application and a small HTML page.

Here’s the code for Flex MXML:

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

Integration with External ApplicationsCHAPTER 15

Page 603: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

608 RIA WITH ADOBE FLEX AND JAVA

<!-- File ./FxJavaScript.mxml -->

<!-- Flex / JavaScript communications example -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” creationCo

mplete=”onCreationComplete()”>

<mx:Script>

<![CDATA[

import mx.controls.Alert;

import flash.external.ExternalInterface;

private var counter:int = 0;

private function onCreationComplete():void {

if (!ExternalInterface.available)

Alert.show( “No ExternalInterface available for container” );

else

ExternalInterface.addCallback( “asFunction”, callActionScript );

}

private function callActionScript(newMessage:String):void {

input.text = newMessage;

}

private function callJavaScript():void {

if (!ExternalInterface.available) return;

counter++;

var retVal:Object = ExternalInterface.call(“jsFunction”,

input.text, counter, new Date(), counter % 2 == 0);

var newMessage:String = retVal as String;

if (newMessage != null) input.text = newMessage;

}

]]>

</mx:Script>

<mx:VBox width=”100%” paddingLeft=”10” paddingRight=”10” paddingTop=”10” padding-

Bottom=”10”>

<mx:TextInput id=”input” width=”100%”/>

<mx:Button id=”button” width=”100%” label=”Call JavaScript” click=”callJavaScrip

t()”/>

</mx:VBox>

</mx:Application>

Listing 15.1 An External API sample

Integration with External ApplicationsCHAPTER 15

Page 604: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 609

We start with an onCreationComplete event handler. Here we first check whether the External API is available and, if so, register an callActionScript instance method closure under the name “asFunc-tion” so JavaScript code can invoke this method like:

flexObject.asFunction(“String argument”);

Now take a look at the callActionScript method itself. As long as we use an instance method closure, we can refer to other instance variables or methods. In the example above we just assign the given text to the TextInput control.

The second method is used to invoke a jsFunction JavaScript function from a Flex applica-tion in response to the button click. Again, we start with an External API availability check. You may choose a different strategy for your application and just disable the button after the initial check. We’re passing several parameters of different types to the JavaScript func-tion: the number of invocations as Number, user-entered text as String, current timestamp as Date, and even/odd invocation flag as Boolean. Finally, the method handlers return a value from the JavaScript function. If it returns a String then we update the text of TextIn-put control.

Next, we have to create an HTML file that embeds the Flex SWF:

<html lang=”en”>

<!-- File ./html-template/FxJavaScript.template.html -->

<!-- Flex / JavaScript communications example -->

<head>

<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8” />

<title>Basic External API Example</title>

<style type=”text/css”>

#fxJavaScript { width: 15em; height: 80px; }

#html_controls {

width: 15em; margin-top: 1em; padding: 1em;

color: white; border: solid 1px white;

font-family: Arial

}

body>#html_controls {

width: 13em;

}

body {

background-color: #869CA7;

}

</style>

<script language=”JavaScript”>

function jsFunction(string, number, date, boolean) {

alert(

Integration with External ApplicationsCHAPTER 15

Page 605: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

610 RIA WITH ADOBE FLEX AND JAVA

[“String = “, string, “, “, typeof string, “\n”,

“Number = “, number, “, “, typeof number, “\n”,

“Date = “, date, “, “, typeof date, “\n”,

“Boolean = “, boolean, “, “, typeof boolean, “\n”

].join(“”)

);

return string ? string.toUpperCase() : string;

}

function callActionScript () {

var fxControl = document.fxJavaScript || window.fxJavaScript;

var value = document.getElementById(“txtMessage”).value;

fxControl.asFunction( value );

}

</script>

</head>

<body scroll=”no”>

<object classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000”

id=”fxJavaScript”

codebase=”http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab”>

<param name=”movie” value=”FxJavaScript.swf” />

<param name=”quality” value=”high” />

<param name=”bgcolor” value=”#869CA7” />

<param name=”allowScriptAccess” value=”sameDomain” />

<embed src=”FxJavaScript.swf”

name=”fxJavaScript”

id=”fxJavaScript”

align=”middle”

bgcolor=”#869CA7”

quality=”high”

play=”true”

loop=”false”

quality=”high”

allowScriptAccess=”sameDomain”

type=”application/x-shockwave-flash”

pluginspage=”http://www.adobe.com/go/getflashplayer”>

</embed>

</object>

<div id=”html_controls”>

Send message to Flex<br/>

<input type=”text” id=”txtMessage” /><button

onclick=”callActionScript ()”>Send</button>

</div>

</body>

Integration with External ApplicationsCHAPTER 15

Page 606: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 611

</html>

Listing 15.2 The HTML page for the sample External API application

To save the book space, we’ve removed the boilerplate code generated by Flex Builder that handles automatic Flash plug-in installation, disabled JavaScript, and browser history navigation. So to run this example you must have Flash Player 9 installed and JavaScript enabled.

The HTML file embeds the Flex application via both the <OBJECT> and <EMBED> tags to support a wide range of browsers and provide a minimal set of controls to invoke an ActionScript function from JavaScript. The most interesting part is a pair of JavaScript functions defined in the <SCRIPT> tag.

The first one is a jsFunction that’s invoked from our Flex application. This function just dumps supplied parameters using the built-in window.alert method and returns the string parameter to ActionScript transformed into upper case. While executing this example, you may see that the JavaScript function receives parameters of the same type (String, Number, Date, Boolean) as on the ActionScript side.

Figure 15.1 External API notification received by JavaScript

Note that the name of this function must exactly match the first parameter of the ExternalInter-face.call (Listing 15.1) and must be defined in the global scope1. If you mistype the name either in the Flex application or in the HTML, the External API silently returns an undefined value to

Integration with External ApplicationsCHAPTER 15

Page 607: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

612 RIA WITH ADOBE FLEX AND JAVA

ActionScript without any errors! If you supply more parameters to the ExternalInterface.call, then the JavaScript function expects the rest of them will be ignored. If the argument list is shorter than expected, the remaining ones will have undefined values.

The second function – callActionScript – shows how easy it is to invoke an ActionScript function exposed via the External API:

fxControl.asFunction( value );

The only tricky part here is to get a reference to fxControl (Flex ActiveX or a plug-in). We used common JavaScript “or” shorthand to get this reference either from the window object (Internet Explorer) or from the document (Gecko-based browsers or Safari):

var fxControl = document.fxJavaScript || window.fxJavaScript;

When we invoke asFunction the content of the TextInput control inside the Flex application is up-dated accordingly.

Unlike JavaScript, ActionScript has optional strong typing. In fact, the Flex Builder compiler uses strong typing by default. So unless the function closure passed to ExternalInterface.addCallback allows a variable number of arguments, supplying more or less arguments than expected will re-sult in an error. To test this, you can modify the fxControl.asFunction(...) from Listing 15.2 to pass either no arguments at all or, say, two arguments – either scenario will cause an error. Supplying arguments of a non-expected type forces the implicit type conversion on the ActionScript side. If the type conversion fails, you’ll see an error again. But wait, if it succeeds, it’s even worse: there’s a good chance of falling into the trap of hard-to-find bugs.

For example, consider an ActionScript callback that defines Number and String parameters. How-ever, JavaScript code passes them in the wrong order: first String, then Number. This call will suc-ceed with the first parameter containing the result of String-to-Number conversion (zero, if the String contains non-numeric characters) and the second parameter presenting Number as String (Number.toString()), but the final result will hardly be correct.

Interesting enough, all built-in JavaScript global functions as well as methods of the window object are also available to the ExternalInterface. In other words, you can use methods like alert, confirm, navigate, open, and many others directly from Flex applications. For example:

private function openAboutWindow():void {

if ( !ExternalInterface.available) return;

var confirmed:Boolean = ExternalInterface.call(“confirm”,

“This will open \”About\” page in new browser window.\nPress \”OK\” to con-

tinue.”

);

Integration with External ApplicationsCHAPTER 15

Page 608: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 613

if ( !confirmed ) return;

ExternalInterface.call(“open”,

“http://myhost/about.html”, “_blank” );

};

This remains true for JavaScript global functions like eval:

private function applyHtmlStyle():void {

if ( !ExternalInterface.available) return;

ExternalInterface.call(“eval”,

‘document.getElementById(“’ + ExternalInterface.objectID + ‘”).style.border

= “solid 3px red”;’

);

}

Bear in mind, however, that many JavaScript programmers say eval is B.A.D. – Broken As Designed. The ratio of flexibility versus readability of code using eval is also disputable; the performance lost is proven by practice: JavaScript will have to switch back to interpreter mode when executing a statement passed to eval.

Two-Way Flex – OWC Spreadsheet IntegrationIn the last section we discussed the basics: what ExternalInterface is and how it enables bidirec-tional communications between JavaScript inside the HTML page and the embedded Flex applica-tion.

Now we’ll consider a more complex example of integration with external applications. The solution involves two ActiveX objects embedded in an HTML page with instant as well as on-demand data synchronization between the objects.

One of these ActiveX objects is the Flash Player control, executing a Flex application. The second ActiveX is a spreadsheet from Microsoft Office Web Components (OWC). You may think of the spreadsheet as a miniature Microsoft Excel application running inside the HTML page. We hope you find our choice of the external application quite natural; data entry or analytical applications with no Excel integration are hard to find. Be it saving data in native Excel format or CSV, accepting data entered in Excel, or dynamically creating Excel charts based on the database queries – we see it everywhere.

Later in this chapter we’ll show the integration of Flex with a real Microsoft Office Excel applica-tion, but for now Spreadsheet ActiveX will do just fine, given the large set of common functionality between Excel and the OWC Spreadsheet.

Before diving into coding, let’s briefly outline the design of the complete solution.

Integration with External ApplicationsCHAPTER 15

Page 609: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

614 RIA WITH ADOBE FLEX AND JAVA

First, we need a Flex application to play with. We’ll build a simple Master/Detail screen with Da-taGrid and Form exposing the basic set of data entry operations: Create-Read-Update-Delete, commonly referred to as CRUD. Every operation will be notifying the hosting environment about the data changes using the ExternalInterface.call method.

Second, we need a Spreadsheet ActiveX control embedded in the same HTML page. As with any third-party ActiveX component we can script it using methods of a Dispatch interface also known as an automation interface. Thankfully, Spreadsheet’s objects have a rich API with all the necessary methods to read/write data and change tracking events.

Finally, we need some mediator that orchestrates communications between both parties. This task is carried out by a set of JavaScript functions/event handlers inside the HTML page.

As far as ActiveX components are used, the example can run only under Internet Explorer on the Microsoft Windows platform (any modern version like Windows 2000 or Windows XP). See Figure 15.2 to get an impression of what the final result looks like.

Figure 15.2 Flex + Spreadsheet OWC example running

After the page is loaded and the initial data for the Flex application is available, the user can edit the records using either the Flex application or a Spreadsheet OWC control. Any changes like altering the cell value in the Spreadsheet or inserting/deleting records in the Flex application are immedi-

Integration with External ApplicationsCHAPTER 15

Page 610: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 615

ately synchronized between two controls. Moreover, the “undo” functionality is fully supported: if the user modifies a record in the Flex application, it can revert changes in the Spreadsheet OWC and both controls will be updated accordingly.

Flex Application for OWC Spreadsheet IntegrationOkay, we start with a Flex application. After all, Flex serves the data in our scenario so it has an up-per hand, besides the fact that we already know everything to start on the Flex part immediately:

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

<!-- File ./FxSpreadsheet.mxml -->

<!-- Flex / Spreadsheet OWC communications example -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml”

layout=”absolute” creationComplete=” onCreationComplete()”>

<mx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

[Bindable]

private var employees:ArrayCollection = new ArrayCollection();

[Bindable]

public var bhiOptions:Array = [{label: “No”, data:false}, {label:”Yes”, data:

true}];

private var notify:Boolean;

private function onCreationComplete():void {

notify = ExternalInterface.available;

if (notify) {

ExternalInterface.addCallback(“xlImportRows”, xlImportRows);

ExternalInterface.addCallback(“xlResend”, xlResend);

fxMetadata();

}

loadData();

}

public function xlResend():void {

fxMetadata()();

fxRowsUpdated(0, employees.toArray(), true);

}

public function xlImportRows(offset:int, cells:Array, reset:Boolean):void {

if (reset) {

if (employees) employees.removeAll();

Integration with External ApplicationsCHAPTER 15

Page 611: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

616 RIA WITH ADOBE FLEX AND JAVA

else employees = new ArrayCollection();

cells.forEach( function(el:Object,idx:int,array:Array):void {

var employee:Object = {};

for (var name:String in el) employee[name] = el[name];

employees.addItem(employee);

});

if (employees.length)

dataGrid.selectedIndex = 0;

}

else

cells.forEach( function(el:Object,idx:int,array:Array):void {

var pos:int = idx + offset;

var employee:Object = employees.getItemAt(pos);

for (var name:String in el)

employee[name] = el[name];

employees.setItemAt(employee, pos);

});

}

private function addRow():void {

if (frmName.text != “”) {

var item:Object = employee(

frmName.text, frmBirthday.selectedDate, frmPhone.text,

frmYos.value, frmHealthInsurance.selectedItem.data, frmSalary.value

);

var idx:int = dataGrid.selectedIndex;

var nextIdx:int = idx >= 0 ? idx + 1 : 0;

employees.addItemAt( item, nextIdx );

dataGrid.selectedIndex = nextIdx;

fxRowsInserted(idx, [item]);

}

}

private function updateRow():void {

var idx:int = dataGrid.selectedIndex;

if (idx >= 0) {

var item:Object = employee(

frmName.text, frmBirthday.selectedDate, frmPhone.text,

frmYos.value, frmHealthInsurance.selectedItem.data, frmSalary.value

);

employees.setItemAt(item, idx);

employees.itemUpdated( item );

fxRowsUpdated(idx, [item], false);

}

Integration with External ApplicationsCHAPTER 15

Page 612: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 617

}

private function deleteRow():void {

var idx:int = dataGrid.selectedIndex;

if (idx >= 0) {

employees.removeItemAt(idx);

if ( !employees.length ) {

frmName.text = frmPhone.text = “”;

frmBirthday.selectedDate = new Date;

frmYos.value = 1; frmSalary.value = 0;

}

else

dataGrid.selectedItem = employees.getItemAt(Math.min(idx, employees.length

- 1));

fxRowsRemoved(idx, 1);

}

}

private function applyResult(data:Array):void {

employees = new ArrayCollection(data);

fxRowsUpdated(0, data, true);

}

private function fxRowsInserted(offset:int, content:Array):void {

if ( notify ) ExternalInterface.call(“fxRowsInserted”, offset, content);

}

private function fxRowsUpdated(offset:int, content:Array, reset:Boolean):void {

if ( notify) ExternalInterface.call(“fxRowsUpdated”, offset, content, reset);

}

private function fxRowsRemoved(offset:int, size:int):void {

if ( notify ) ExternalInterface.call(“fxRowsRemoved”, offset, size);

}

private function fxMetadata():void {

if ( notify ) ExternalInterface.call(“fxMetadata”, [

{name:”name”, title:”Full name”, type:”string”},

{name:”birthday”, title:”Birthday”, type:”date”},

{name:”phone”, title:”Phone”, type:”string”},

{name:”yos”, title:”Years of srv.”, type:”number”},

{name:”healthInsurance”, title:”Health insurance”, type:”boolean”},

{name:”salary”, title:”Salary”, type:”number”}

]);

Integration with External ApplicationsCHAPTER 15

Page 613: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

618 RIA WITH ADOBE FLEX AND JAVA

}

private function loadData():void {

applyResult([

employee(“Yakov Fain”, d(“1960/04/18”), “732-456-4432”,

yos(“1999/05/20”), true, 95000.0),

employee(“Anatole Tartakovsky”, d(“1962/03/03”), “561-860-2376”,

yos(“1991/03/01”), true, 85000.0),

employee(“Victor Rasputnis”, d(“1963/02/06”), “718-445-7622”,

yos(“1993/07/02”), true, 85900.0),

employee(“Melissa Espinoza”, d(“1950/12/14”), “508-555-2319”,

yos(“1996/04/18”), true, 36490.0),

employee(“Kathleen Poitras”, d(“1966/09/29”), “617-555-3920”,

yos(“1999/05/29”), false, 46200.0),

employee(“Joseph Barker”, d(“1980/02/14”), “617-555-8021”,

yos(“2001/09/10”), false, 27290.0),

employee(“Shih Lin”, d(“1980/12/12”), “617-555-5921”,

yos(“2001/11/11”), false, 33890.0),

employee(“Barbara Blaikie”, d(“1954/11/14”), “617-555-9345”,

yos(“2001/11/20”), true, 54900.0)

]);

}

private static function employee(name:String, birthday:Date, phone:String,

yos:int, healthInsurance:Boolean, salary:Number):Object {

return {

name:name, birthday:birthday, phone:phone,

yos:yos, healthInsurance: healthInsurance, salary:salary

};

}

private static function d(s:String):Date {return new Date( Date.parse(s) );}

private static const THIS_YEAR:int = new Date().getFullYear();

private static function yos(startDate:String):int {return THIS_YEAR - d(startDate).

getFullYear();}

private static function formatDate(itm:Object, col:Object):String {

var d:Date = itm.birthday;

return d ? [d.getFullYear(), pad(d.getMonth() + 1), pad(d.getDate())].join(“-”)

: “”;

}

private function formatHealthInsurance(itm:Object, col:Object):String {

return bhiOptions[itm.healthInsurance * 1].label;

}

Integration with External ApplicationsCHAPTER 15

Page 614: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 619

private static function pad(v:int):String { return (v < 10 ? “0” : “”) + v; }

]]>

</mx:Script>

<mx:VBox width=”100%” height=”100%”>

<mx:DataGrid id=”dataGrid” width=”100%” height=”100%” dataProvider=”{employees}”

editable=”false” sortableColumns=”false”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”name” headerText=”Name”/>

<mx:DataGridColumn labelFunction=”formatDate” headerText=”Birthday”/>

<mx:DataGridColumn dataField=”phone” headerText=”Phone”/>

<mx:DataGridColumn dataField=”yos” headerText=”Years of Srv.” textAlign=”

right”/>

<mx:DataGridColumn labelFunction=”formatHealthInsurance”

headerText=”Health insurance” textAlign=”center”/>

<mx:DataGridColumn dataField=”salary” headerText=”Salary” textAlign=”right

”/>

</mx:Array>

</mx:columns>

</mx:DataGrid>

<mx:Form width=”100%” height=”100%” borderStyle=”solid” borderColor=”#ffffff”>

<mx:FormItem label=”Name”>

<mx:TextInput id=”frmName” width=”200” text=”{dataGrid.selectedItem.

name}”/>

</mx:FormItem>

<mx:FormItem label=”Birthday”>

<mx:DateField id=”frmBirthday” selectedDate=”{dataGrid.selectedItem.

birthday}”/>

</mx:FormItem>

<mx:FormItem label=”Phone”>

<mx:TextInput id=”frmPhone” width=”200” text=”{dataGrid.selectedItem.

phone}”/>

</mx:FormItem>

<mx:FormItem label=”Years of service”>

<mx:NumericStepper id=”frmYos” value=”{dataGrid.selectedItem.yos}”

maxChars=”3” minimum=”1” maximum=”99”/>

</mx:FormItem>

<mx:FormItem label=”Health insurance”>

<mx:ComboBox id=”frmHealthInsurance”

selectedIndex=”{dataGrid.selectedItem.healthInsurance}”

Integration with External ApplicationsCHAPTER 15

Page 615: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

620 RIA WITH ADOBE FLEX AND JAVA

dataProvider=”{bhiOptions}”/>

</mx:FormItem>

<mx:FormItem label=”Salary”>

<mx:NumericStepper id=”frmSalary” value=”{dataGrid.selectedItem.salary}”

maxChars=”10” minimum=”0” maximum=”999999”/>

</mx:FormItem>

<mx:FormItem>

<mx:HBox>

<mx:Button label=”Update” click=”updateRow()”/>

<mx:Button label=”Add” click=”addRow()”/>

<mx:Button label=”Delete” click=”deleteRow()”/>

</mx:HBox>

</mx:FormItem>

</mx:Form>

</mx:VBox>

</mx:Application>

Listing 15.3 Flex application for OWC integration sample

Rather than going through a compete code walkthrough, let’s just focus on the particular key meth-ods. The first one, fxMetadata, notifies the hosting container about the metadata used. As long as we strive to keep the JavaScript on the HTML page as generic as possible, neither hard-coded nor “well-known” names are valid options. fxMetadata notification enables the JavaScript code to be used with a wide range of Flex applications. The metadata format used in our example is sufficient to describe the order of fields, user-friendly field names, and types of fields. The last feature is very important. Both Microsoft Excel and Microsoft Spreadsheet OWC support type-specific formating options, and we plan to exploit this functionality.

Next comes the trio of methods fxRowsInserted, fxRowsUpdated, and fxRowsRemoved. As their names suggest, these are data change notifications to the hosting container. Note that the no-tification for the complete data (re)load is fxRowsUpdated with the Boolean flag reset equals true. All data notifications use the offset parameter to define the positions where changes take place.

In this example we match the records in the Flex application and the rows in the Spreadsheet OWC by position, i.e., a record in the Flex application corresponds to a row in the Spreadsheet if they both have the same index. Other types of “equality” are possible as well, for example, matching a data record and the Spreadsheet row by key attributes. However, anything more complex than a simple index would require lengthy code examples that won’t fit in a book chapter.

Finally, we have a pair of xlImportRows and xlResend methods as ActionScript callbacks exposed via the ExternalInterface.addCallback. The purpose of the first is fairly obvious: this is data change notification in the opposite direction from the Spreadsheet OWC to the Flex application. The sec-ond method deals with possible timing issues related to loading the SWF file by the Flash Player

Integration with External ApplicationsCHAPTER 15

Page 616: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 621

control. If HTML JavaScript was unable to get the pair of initial fxMetadata/fxRowsUpdated notifi-cations, it can query the Flex application to resend them.

HTML Template Embedding Our ActiveX ComponentsIt’s time to write an HTML page that will carry our Flex application and the OWC Spreadsheet. Here’s our first draft. It doesn’t contain the OWC Spreadsheet, only the FxSpreadsheet.swf is em-bedded so far:

<html lang=”en”>

<!-- File ./html-template/FxSpreadsheet.template.html -->

<!-- Flex / Spreadsheet OWC communications example -->

<head>

<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8” />

<title>Flex &lt;-&gt; Spreadsheet OWC example</title>

<style type=”text/css”>

body { overflow: hidden; background: #869CA7 }

#fxGrid { float:left; width: 50%; height: 100%; margin-right: 1em; }

#xlBox { width: 99%; height: 100% }

#xlOWC { width: 100%; height: 100%; border: 1px solid rgb(183,186,188) }

* { font-family: Verdana, Arial; font-size: 10pt; color: white }

</style>

<script language=”JavaScript” type=”text/javascript”>

/* CODE TO DETECT CURRENT BROWSER / VERSION */

var fxMetadata, fxRowsUpdated, fxRowsInserted, fxRowsRemoved;

fxMetadata = fxRowsUpdated = fxRowsInserted = fxRowsRemoved = function() {};

/* MAIN JAVASCRIPT CODE */

</script>

</head>

<body scroll=”no”>

<object classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000”

id=”fxGrid” width=”100%” height=”100%”

codebase=”http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab”>

<param name=”movie” value=”FxSpreadsheet.swf” />

<param name=”quality” value=”high” />

<param name=”bgcolor” value=”#869ca7” />

<param name=”allowScriptAccess” value=”sameDomain” />

<embed src=”FxSpreadsheet.swf” name=”fxGrid” quality=”high” bgcolor=”#869ca7”

width=”100%” height=”100%” align=”middle”

play=”true”

loop=”false”

quality=”high”

allowScriptAccess=”sameDomain”

type=”application/x-shockwave-flash”

Integration with External ApplicationsCHAPTER 15

Page 617: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

622 RIA WITH ADOBE FLEX AND JAVA

pluginspage=”http://www.macromedia.com/go/getflashplayer”>

</embed>

</object>

<div id=”xlBox”>

<!-- CODE TO WRITE SPREADSHEET ACTIVEX -->

</div>

</body>

</html>

Listing 15.4 The draft of an HTML page for OWC integration sample

Now we have to include the Spreadsheet OWC control in the HTML page. This control is available only as Microsoft Windows ActiveX, which leaves users of other platforms and browsers out in the cold. In any event, we feel obligated to relieve these users of JavaScript errors or browser crashes. Concrete fallback strategies may vary, but in the simplest form we have it here, it’s just a message about the unsupported environment. More complex JavaScript may first analyze the browser and OS and then suggest more specific steps.

Here’s the first block of JavaScript code that detects the version of the browser and OS. It’s based on a conditional compilation, manifested by an @cc_on statement, an IE-specific feature. As long as the logic based on @cc_on is nested inside the /**/ comments, all other browsers simply treat it as a comment, nothing more:

<script language=”JavaScript” type=”text/javascript”>

var ie_win32 = 0;

/* MS-only conditional compilation for JScript */

/* Besides the fact that this is the most simple */

/* guaranted way to detect IE, it provides OS info */

/*@cc_on @*/

/*@if (@_win32)

ie_win32 = 1;

/*@end @*/

</script>

Listing 15.5 The browser-detection script

As a net result, the variable ie_win32 gets sets to 1 if and only if the script is running under Micro-soft Internet Explorer 5+ on Windows 95/NT 4.0 or higher. Browsers other than Microsoft Internet Explorer 5+ see the assignment as commented-out code. If the browser isn’t IE our page will display the “complaint” and do nothing (see Listing 15.6 below):

if (!ie_win32) {document.write(wrong_env); return;}

Once IE is confirmed, our JavaScript must be robust enough to detect the availability of the Micro-

Integration with External ApplicationsCHAPTER 15

Page 618: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 623

soft Office Web Components and to select the most recent version2. Currently, there are four ver-sions of Microsoft Office Web Components: Office 2000, XP, XP SP3, and 2003 as shown, along with the corresponding CLSID in Table 15.1.

Microsoft OWC Version (Build) Microsoft Office Version CLSID of Spreadsheet OWC

09 Microsoft Office 2000 0002E510-0000-0000-C000-000000000046

10 (prior 4109) Microsoft Office XP 0002E551-0000-0000-C000-000000000046

10 (4109 and above) Microsoft Office XP SP3 0002E541-0000-0000-C000-000000000046

11 Microsoft Office 2003 0002E559-0000-0000-C000-000000000046

Table 15.1 A summary of OWC versions

And whether you’re surprised with this abundance of versions or not, you have to understand and accept it to make your code run on all OWC-enabled systems.

The first version of Office Web Components released to the public was OWC 9. To say that it was released along with Microsoft Office 2000 would be an understatement because ordering the Office 2000 CD was the only way to get it. Aside from this questionable distribution model, the OWC9 API has been anything but practical. For example, a Spreadsheet component from that release lacks content change notification events, so essentially when used with OWC9 our example will be a one-way road from Flex to OWC.

The second big release was OWC coming with Microsoft Office XP (2002). The API was seriously reworked to match the object hierarchy and functionality of the corresponding Office applications. With this single release Microsoft contrived to make several important changes:

• It changed the CLSID of all Office Web Components after the release of Service Pack 3, see Table 15.1.

• It enhanced OWC licensing. Originally, to use OWC 10 Components in editable mode (as with version 9), you needed a Microsoft Office product or license file installed on your PC. The new licensing model introduced the notion of a “shared license” for business entities that have a spe-cial agreement with Microsoft. This opened the door for using OWC Components in an interac-tive mode within organizations’ intranets even when a specific client computer has no Microsoft Office product or license file installed. For a full description refer to Microsoft’s Knowledge Base article #555094 (http://support.microsoft.com/?scid=kb;en-us;555094&spid=2488&sid=714).

• The recent version 11 of Office Web Components has no dramatic changes comparable to previous versions. However, the API has been extended according to the enhancements in Mi-crosoft Office 2003 (mostly XML-related stuff) and the set of CLSIDs used was changed again.

Now that we’ve completed this version-hell walkthrough, we’re ready to write the JavaScript code

Integration with External ApplicationsCHAPTER 15

Page 619: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

624 RIA WITH ADOBE FLEX AND JAVA

that attempts to embed the most recent version of the Spreadsheet OWC Component or, alterna-tively, degrades the HTML page when no OWC is available, the IE security settings prohibit running ActiveX, or a combination of the browser/OS isn’t supported:

<script language=”JavaScript” type=”text/javascript”>

(function(){

var wrong_env =

“<p>This sample requires Microsoft Internet Explorer “ +

“browser, running on Windows platform.Also you need “ +

“Microsoft Office Web Components (OWC) installed.</p>”

if (!ie_win32) {document.write(wrong_env); return;}

/* The very first entry in versions is */

/* version-independend ProgID (vi-ProgID) */

/* However, it seems to always cause *

/* errors, so we try versions from */

/* higher to lower and lastly vi-ProgID */

var versions = [‘’,9,10,11];

var version_clsids = {

“09.0.0.0000”: “0002E510”,

“10.0.0.0000”: “0002E551”, /*PRE-SP3*/

“10.0.0.4109”: “0002E541”, /*SP3 and above, probably need to check 6619 build*/

“11.0.0.0000”: “0002E559”

};

var version_ids = [];

for (var key in version_clsids) version_ids.push(key);

version_ids.sort();

var clsidPrefix;

var versionInfo = “&lt;unknown&gt;”;

LOOP_VERSIONS:

for (var i = versions.length - 1; i >=0; i--) {

var owc = null;

var ver = versions[i];

var progid = “OWC” + ver + “.Spreadsheet” + (ver ? “.” + ver : “”);

try { owc = new ActiveXObject(progid) } catch(e) {}

if (owc) {

versionInfo = (owc.majorVersion < 10 ? “0” : “”) + owc.version;

if (!ver) ver = parseInt(versionInfo);

for (var i = version_ids.length - 1; i >= 0; i--) {

var lastVersion = version_ids [i];

var guess = version_clsids[lastVersion];

if ( versionInfo >= lastVersion ) {

Integration with External ApplicationsCHAPTER 15

Page 620: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 625

clsidPrefix = guess;

break LOOP_VERSIONS;

}

}

}

}

if ( clsidPrefix )

document.write(

‘<object id=”xlOWC” classid=”CLSID:’ + clsidPrefix + ‘-0000-0000-C000-

000000000046” ‘ +

‘title=”Microsoft Office Web Component / Excel Spreadsheet v’ + versionInfo +

‘”>’ +

wrong_env + ‘</object>’

);

else

document.write(

“<p>Unable to resolve Spreadsheet component CLSID for “ +

“Microsoft Office Web Components (OWC) version “ + versionInfo + “</p>”

);

})();

</script><noscript>

<p>Unable to detect OWC version: JavaScipt disabled.<br/>

This sample requires Microsoft Internet Explorer browser, running on Windows platform.

Also you need Microsoft Office Web Components (OWC) installed.</p></noscript>

Listing 15.6 Inserting the Spreadsheet ActiveX

The code traverses a list of available versions in reverse order (from tale to head, from higher ver-sion to lower) trying to instantiate a Spreadsheet component via the JScript-specific global func-tion ActiveXObject. If object is created, we get its exact version and check it against the registry of CLSID prefixes (suffixes are the same for all versions). If a match is found, we write the <OBJECT> tag in place. If we fail for any reason (the wrong environment, no OWC installed), the problem de-scription is provided.

Note that the complete scripting part of Listing 15.6 is enveloped in a single anonymous function call:

(function(){

/* CODE HERE */

})();

Here’s how you can read this: an expression that returns an anonymous function and immediately executes it. Quite simple but a powerful technique. First, all variables declared in the function are, naturally, encapsulated in the scope of the function. Therefore global scope isn’t polluted; there’s

Integration with External ApplicationsCHAPTER 15

Page 621: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

626 RIA WITH ADOBE FLEX AND JAVA

no chance to override global variables accidentally with the same names. Second, we may exit from the function early with a return statement – in traditional “global” script we’d rather use nested if/else statements.

Once we complete embedding the OWC SpreadSheet and the Flex application shown in the previ-ous section, we get a page that results in this:

Figure 15.3 Flex and Spreadsheet OWC embedded

Making Flex and Spreadsheet TalkWe’ve got a pair of ActiveX controls separated by a 10-pixels spacer. We have to add a substantial dose of JavaScript glue to make them “talk” to each other. A truncated version of HTML with the “glue” added is presented in Listing 15.7. For purposes of clarity, we skipped the content covered earlier and separated the initialization procedure into several blocks. We’re going to discuss these blocks one by one.

<html lang=”en”>

<!-- File ./html-template/FxSpreadsheet.template.html -->

<!-- Flex / Spreadsheet OWC communications example -->

<head>

<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8” />

<title>Flex &lt;-&gt; Spreadsheet OWC example</title>

<style type=”text/css”>

Integration with External ApplicationsCHAPTER 15

Page 622: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 627

<!-- STYLES AS IN EXAMPLE ABOVE -->

</style>

<script language=”JavaScript” type=”text/javascript”>

var ie_win32 = 0;

/*@cc_on @*/

/*@if (@_win32)

ie_win32 = 1;

/*@end @*/

var missedNotification = false; // Changed to true if Flex loads data quicker then

page initialized

/* JavaScript callbacks invoked from Flex */

var fxMetadata, fxRowsUpdated, fxRowsInserted, fxRowsRemoved;

/* JavaScript callbacks invoked from Excel */

var xlOWCEvents;

(function(){

var MISSED = function(){missedNotification = true; };

fxMetadata = MISSED; /* function(meta) {} */

fxRowsUpdated = MISSED; /* function(offset, content, reset) {} */

fxRowsInserted = MISSED; /* function(offset, content) {}; */

fxRowsRemoved = MISSED; /* function(offset, count) {}; */

var NOP = function(){};

xlOWCEvents = { sheetChange: NOP, beforeContextMenu: NOP, commandBeforeExecute:

NOP };

})();

window.onload = function() {

var xlOWC = document.getElementById(“xlOWC”)

if (!xlOWC) return; // No OWC component available

var fxGrid = window[“fxGrid”];

if (!fxGrid) return; // No Flex component available

var COLUMNS_XL = [];

var COLUMNS_FX = {};

var COLUMNS_COUNT = COLUMNS_XL.length;

var ROW_COUNT;

var xlVersion = parseInt(xlOWC.version);

var XLConstants = xlOWC.Constants;

var xlSheet = xlVersion >= 10 ? xlOWC.activeSheet : xlOWC;

Integration with External ApplicationsCHAPTER 15

Page 623: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

628 RIA WITH ADOBE FLEX AND JAVA

if ( xlVersion >= 10) {

/* Some small UI tweaks */

var titleBar = xlOWC.titleBar;

with (titleBar) { visible = true; caption = “FlexGrid Data”; interior.color =

“#F0F0F0”; }

with (titleBar.font) { color = “#000000”; size = 10; bold = true; name =

“Verdana”; }

xlOWC.activeWindow.enableResize = false;

}

function setupFxMetadata() { /* CODE WILL BE DISCUSSED BELOW */ }

function setupFxDataNotifications() { /* CODE WILL BE DISCUSSED BELOW */ }

function setupXlDataNotifications() { /* CODE WILL BE DISCUSSED BELOW */ }

function setupXlContextMenu() { /* CODE WILL BE DISCUSSED BELOW */ }

setupFxMetadata();

setupFxDataNotifications();

setupXlDataNotifications();

setupXlContextMenu();

/* If Flex application did not send data yet, then returns */

/* Otherwise query for current data */

if (missedNotification) fxGrid.xlResend();

};

</script>

<script language=’JavaScript’ for=’xlOWC’ event=’sheetChange(sheet,range)’>

xlOWCEvents.sheetChange(sheet,range);

</script>

<script language=’JavaScript’ for=’xlOWC’ event=’beforeContextMenu(x, y,

menu,cancel)’>

xlOWCEvents.beforeContextMenu(x, y, menu, cancel);

</script>

<script language=’JavaScript’ for=’xlOWC’ event=’commandBeforeExecute(cmd,cancel)’>

xlOWCEvents.commandBeforeExecute(cmd,cancel);

</script>

</head>

<body scroll=”no”>

<!-- FLASH OBJECT/EMBED -->

<!-- DIV WITH SCRIPT TO INSERT SPREADSHEET -->

</body>

</html>

Listing 15.7 The final HTML page for the OWC integration sample

Integration with External ApplicationsCHAPTER 15

Page 624: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 629

The page is being initialized in two phases: when HTML starts loading and after the complete page content is available (see the window.onload event handler in Listing 15.7). During the first phase we provide a fallback implementation for both Flex notifications and Spreadhsheet OWC event handlers (packaged as xlOWCEvents object methods). You may be surprised to see that both groups of methods contain no real functionality. Let’s assure the reader that the content of these methods is going to be dynamically reassigned later. For now, we keep them at bay so that no harm is done if they are invoked in the unsupported browser or at the wrong time.

To understand the kind of problems we’re trying to avoid, imagine that our HTML page with an OWC example is opened in Firefox. The Spreadsheet control is unavailable in this environment, but the Flex application can run successfully (as long as the Flash plug-in is installed). The very first notification from Flex would cause a JavaScript error if the corresponding handler attempted to access a non-existing control. Sure, it’s possible to add the necessary check to every notification handler. However, this is code duplication.

The second sort of problem we solve with “empty” notification handlers are timing-related. The SWF file is loaded asynchronously with the rest of the page and the fxMetadata notification is fired immediately from the onCreationComplete event handler (see Listing 15.3). Hence, the HTML page may get this notification earlier when the Spreadsheet ActiveX control is inserted and/or fully initialized. That’s why notification handlers in this phase contain no functionality to update the Spreadsheet OWC; they all set the flag missedNotification to requery notifications from Flex when initialization is complete.

The second phase of the page initialization is executed when the window.onload event happens. It starts by looking for the xlOWC HTML element that we attempted to dynamically instantiate in Listing 15.6. If no object is returned for this ID, there’s no sense in taking further action – HTML is running in the unsupported environment. Otherwise initialization continues and the reference to the second ActiveX control, Flash Player running the Flex application, is stored as well.

Next, the window.onload handler declares several metadata-related variables. Briefly, these vari-ables describe the structure of the records coming from the Flex application and the associated rules to convert field values back and forth between Flex and the Spreadsheet (we’ll make full use of these variables in the fxMetadata notification handler a bit later). As long as we need a bidirectional communication channel, two mapping structures are required:

• The number-indexed COLUMNS_XL array defines the mapping between the Spreadsheet column numbers and ActionScript field names. The position in this array corresponds to the index of the column.

• The associative COLUMNS_FX array defines the mapping between the ActionScript field name and the column index. The key in this array is the name of field.

The variables COLUMN_COUNT and ROW_COUNT hold the number of available fields (columns) and records (rows) correspondingly. Strictly speaking, ROW_COUNT relates to the data rather than to metadata.

Integration with External ApplicationsCHAPTER 15

Page 625: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

630 RIA WITH ADOBE FLEX AND JAVA

To discuss the next variable – xlSheet – we at least need a recap of the Microsoft Excel/Spreadsheet OWC object model, which we’re going to provide right here in a “Shakespeare on the eggshell” format.

The root of the object hierarchy is the Application object. For Spreadsheet OWC it’s the control itself. Every Application contains a collection of Workbooks, every Workbook, in turn, holds a col-lection of Worksheets. Many objects in the hierarchy have a notion of “active” sub-elements. Active means “currently visible to the user and ready to accept the user’s input.” So an Application has an ActiveWorkbook, and a Workbook has an ActiveSheet. Transitively, Application has an ActiveSheet property as well.

When a Spreadsheet control is initialized, it pre-creates one active Workbook with three Worksheets; the first Worksheet is active. It’s the reference to this very worksheet that’s stored as an xlSheet vari-able during the initialization time.

Remember, there are a number of differences between existing OWC versions. To keep track of the currently used OWC version we defined the variable xlVersion. The version differences come into play immediately. In fact, everything we’ve said about the object model is true for the Microsoft Ex-cel application and Spreadsheet OWC of versions 10/11, but doesn’t stand for Spreadsheet version 9, which uses a completely different API. There are no such things like Workbook, Worksheet, and the collections thereof in OWC9. The Spreadsheet is a mix of Application with a sole active Work-book that has one and only one (active) Worksheet. However, the combination of the Microsoft COM late-binding mechanism, the JavaScript “duck typing,” and the similarities between Spread-sheet and Worksheet APIs let us treat Spreadsheet as a Worksheet in certain cases: at least OWC9 functionality is enough to implement Flex-to-Spreadsheet communications fully; when it comes to anything beside this, our script degrades gracefully.

XLConstants provides a shortcut to all constants used in the Spreadsheet OWC methods. In script-ing languages like JScript or VBScript there’s no way to refer the constants declared in ActiveX-type libraries by name. Hence, every OWC control exposes a set of constants as its own complex prop-erty.

Just before returning from the window.onload event handler, when all setup and preparation is done, a check for lost notifications from the Flex application is done. If there are any, the Flex ap-plication is asked to resend notifications for both metadata and available data entries (see fxGrid.xlResend() invocation).

Handling Flex Metadata NotificationAs a reminder, one of our primary design goals is to make the JavaScript code generic and reusable while fully using Excel formating capabilities. To that extent we externalize the metadata. This part of the code, which also fulfils our promise of overwriting the Flex metadata notification function, is presented in Listing 15.8:

Integration with External ApplicationsCHAPTER 15

Page 626: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 631

function setupFxMetadata() {

function AS_IS(v) { return v; }

function variantDate2jsDate (v) { return null == v ? null : new Date(v); }

XL_FORMATTERS = {

date: variantDate2jsDate, time: variantDate2jsDate, dateTime:

variantDate2jsDate,

boolean: function(v) { return v == null ? null : !!v; },

string: AS_IS, number: AS_IS

};

function fmtDate(v) { return [v.getFullYear(), v.getMonth() + 1, v.getDate()].

join(“-”); }

function fmtTime(v) { return [v.getHours(), v.getMinutes(), v.getSeconds()].

join(“:”); }

FX_FORMATTERS = {

date: fmtDate, time: fmtTime, dateTime: function(v) { return fmtDate(v) + “ “ +

fmtTime(v); },

boolean: AS_IS, string: AS_IS, number: AS_IS

};

fxMetadata = function(meta) {

meta = meta || [];

ROW_COUNT = 0;

fxRowsUpdated(0, [], true);

COLUMNS_FX = {};

COLUMNS_COUNT = meta.length;

COLUMNS_XL = new Array(COLUMNS_COUNT);

var xlHeadings;

if (xlVersion >= 10) {

var xlWnd = xlOWC.activeWindow;

xlWnd.resetHeadings();

xlHeadings = xlWnd.columnHeadings;

}

else {

var FAKE_HEADING = {};

xlHeadings = function(idx) { return FAKE_HEADING; }

}

for (var i = 0; i < COLUMNS_COUNT; i++) {

var field = meta[i];

COLUMNS_XL[i] = field.name;

field.idx = i + 1;

field.xl = XL_FORMATTERS[field.type];

Integration with External ApplicationsCHAPTER 15

Page 627: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

632 RIA WITH ADOBE FLEX AND JAVA

field.fx = FX_FORMATTERS[field.type];

COLUMNS_FX[field.name] = field;

xlHeadings(i + 1).caption = field.title;

}

}

}

Listing 15.8 Initializing metadata functions

When our JavaScript is notified of metadata changes (the function fxMetadata), it must do several things. First, it’s necessary to reset all existing variables that are related to the metadata. We also need to drop the existing content. Otherwise the user will be confused to see a logical mismatch between column headings and cell content. Afterwards, the handler re-creates JavaScript metadata variables according to parameters from ActionScript.

We use the active Window instance to overwrite a default text of column headings with user-friend-ly field titles (compare the Spreadsheet column heading texts in Figures 15.2 and 15.3). Note that though the Window object is present in both Excel and Spreadsheet OWC 10/11, the column/row headings functionality is unique to the Spreadsheet component. As you can see from the work-around applied, Spreadsheet OWC 9 has no such functionality.

The remaining part of the code in Listing 15.8 deals with type formats. Excel is smart enough to auto-format a cell based on the values entered. So if we transform the original value to the Excel-friendly form, the necessary formatting will be applied automatically.

Be aware that the primary scripting language of Microsoft Office Applications is Visual Basic. It could either be VBA inside Office applications or VBScript for Office Web Components. In fact, the very first Visual Basic customer was the Microsoft Excel team!

Many Visual Basic features may look strange from the JavaScript perspective. One of them, for ex-ample, is that indexing in Visual Basic starts from 1 (one) whereas in JavaScript or ActionScript it starts from 0 (zero). So be prepared to see “row + 1” statements here and there in example code.

The second issue is that the presentation of certain types differs between Visual Basic and JScript. The most problematic one is Date. Visual Basic accepts a date as a variant of the VT_DATE type. Un-der the hood, it’s a floating-point value, counting days since midnight December 30, 1899. Hours and minutes are represented as fractional days. In the ECMAScript family of languages like JavaS-cript, ActionScript, or JScript, a date is represented as an object of the Date type and its internal representation is the number of milliseconds since January 1, 1970. There’s enough differences to get upset!

That said, Excel is smart enough to parse the date from a string in several formats3, for example, “2006-06-04 14:30:30” is recognizable as a date/time value. The set of FX_FORMATTERS for date/time types uses this feature. The values of other JavaScript types can be safely passed to Excel as is.

Integration with External ApplicationsCHAPTER 15

Page 628: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 633

While moving the data in the opposite direction, we need to convert VT_DATE to JavaScript. Thank-fully, it’s just a Date constructor call with VT_DATE as an argument (see the variantDate2jsDate function in Listing 15.8), a neat undocumented feature of JScript. For fields defined as Boolean we need to ensure that the value entered in the Spreadsheet is indeed Boolean. To that end we apply the good old JavaScript “negation of negation” idiom to convert the value entered to the Boolean type:

var numberF = 0, numberT = 255;

var stringF = “”, stringT = “ABC”;

var objectF = null, objectT = new Date();

var isNumberF = !!numberF; // false;

var isNumberT = !!numberT; // true;

var isStringF = !!stringF; // false;

var isStringT = !!stringT; // true;

var isObjectF = !!objectF; // false;

var isObjectT = !!objectT; // true;

You may have noticed that we are updating the metadata field objects that come from ActionScript. Such updates don’t affect original objects in ActionScript due to the marshaling used by the Exter-nal API protocol (we’ll discuss this later).

Synchronizing Spreadsheet OWC with Flex Data ChangesTo discuss the part of the code that handles data change notifications coming from the Flex appli-cation, we have to digress a level deeper in the Microsoft Excel/Spreadsheet OWC objects tree. The code operates on the content of the Worksheet, which is made up of cells. There’s no Cell object in the tree per se, but there’s a Range object. Range is a lightweight object that provides access to any number of cells depending on the range bounds. So you can think of a cell as just a Range with the top-left corner equal to the bottom-right one. Similarly, a column is a Range with the left bound equal to the right bound, the top bound is equal to one, and the bottom bound is equal to the maximum number of rows allowed (in recent Excel versions it’s 2^16). By the same analogy, a row is a Range with the top bound equal to the bottom bound, the left equal to one and the right equal to the maximum number of columns allowed (up to 2^8).

The number of methods to get the Range object in the Excel/Spreadsheet OWC far outweighs the number of ways to skin a cat. Depending on your needs, it’s possible to construct a Range from a Worksheet supplying the pair of top/left and bottom/right cells; it’s possible to get a Range by the name of the cells (for example, “A1:B2”); there’s a notion of an active cell and current selection; the result of the intersection/union operations on ranges is, obviously, Range as well. For a complete list see the Excel VBA documentation available with the Office Web Components.

A Range is a very efficient abstraction when it comes to data operations or formatting. If you read a value from the Range, the value returned is taken from the top-level cell. When you assign an indi-

Integration with External ApplicationsCHAPTER 15

Page 629: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

634 RIA WITH ADOBE FLEX AND JAVA

vidual value to the Range or modify its visual property like FillStyle it affects all cells in the Range.

The code we’re going to present in Listing 15.9 reacts to data changes on the Flex side and modifies the Spreadsheet accordingly. In essence, it sequentially assigns values to individual cells. This approach has a serious associated performance penalty. The cost of multiple as-signments and in general the cost of multiple calls across COM object boundaries is inherent-ly high. To make things worse, with JScript and VBScript we’re limited to the COM IDispatch interface.

We’ll address the performance improvements later, when instead of individual value assignments we’ll use another feature of Range – the support of tabular data assignments such as pasting HTML content from the clipboard or copying values from ADODB.Recordset. In these cases Range forces data to be split up into individual values, one per cell.

function setupFxDataNotifications() {

function xlCopyCells(offset, content) {

for (var i = content.length - 1; i >= 0; i--) {

var obj = content[i];

var row = i + 1 + offset;

for (var name in COLUMNS_FX) {

var col = COLUMNS_FX[name];

xlSheet.cells(row, col.idx).value = col.fx(obj[name]);

}

}

}

function xlTransaction(fn) {

xlOWC.enableEvents = false;

xlOWC.beginUndo();

try { fn(); }

finally {

xlOWC.endUndo();

xlOWC.enableEvents = true;

}

}

fxRowsUpdated = function(offset, content, reset) {

xlTransaction(function() {

var cells = xlSheet.cells;

if (reset) { cells.clear(); offset = 0; }

xlCopyCells(offset, content);

var count = content.length;

if (reset) ROW_COUNT = count;

Integration with External ApplicationsCHAPTER 15

Page 630: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 635

if ( !count ) return;

var range = xlSheet.range(

xlSheet.cells(offset + 1, 1),

xlSheet.cells(offset + count, COLUMNS_COUNT)

);

range.select();

if (reset && xlVersion >= 10) range.columns.autoFit();

});

};

fxRowsInserted = function(offset, content) {

var count = content.length;

if (!count) return;

xlTransaction(function(){

var row = offset + 2;

while (count--) xlSheet.rows(row).insert();

xlCopyCells(offset + 1, content);

var range = xlSheet.range(

xlSheet.cells(row, 1),

xlSheet.cells(row + content.length - 1, COLUMNS_COUNT)

);

range.select();

});

};

fxRowsRemoved = function(offset, count) {

if (!count) return;

xlTransaction(function(){

var row = offset + 1;

while (count--) xlSheet.rows(row)[“delete”]();

var range = xlSheet.range(

xlSheet.cells(row, 1),

xlSheet.cells(row, COLUMNS_COUNT)

);

range.select();

});

};

}

Listing 15.9 Handling data changes in a Flex application

Methods xlCopyCells and xlTransaction are used by Flex data change notification handlers. The ra-tionale behind xlCopyCells is clear. Given a zero-based offset and content as an array of objects, the

Integration with External ApplicationsCHAPTER 15

Page 631: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

636 RIA WITH ADOBE FLEX AND JAVA

method assigns a value of every property to corresponding cells. This is where the FX_COLUMNS associative array comes in handy. It provides a way to both order properties and get correct format-ing function to convert values as necessary.

The xlTransaction method is a pure function decorator. First of all, it disables notification events from Spreadsheet OWC when we’re modifying cells. Otherwise, there’s a risk of either getting into infinite recursion or issuing an unnecessary update to the Flex application. Second, it lets us enlist a set of operations for a single undo “command,” or otherwise users might be surprised with the number of clicks needed to undo the changes on the Spreadsheet side of a single atomic record insertion/modification/deletion done on the side of the Flex application.

On a side note, beginUndo/endUndo functionality isn’t unique to Spreadsheet OWC. Similar function-ality exists in Excel. But it’s implemented in a different way as the Application.OnUndo(“MenuText,” “ProcedureName”) method.

The Flex notification handlers fxRowsUpdated, fxRowsInserted, and fxRowsRemoved are quite easy to follow. All of them pass processing blocks as function closures to xlTransaction. For the insertion/deletion of rows we first get the complete row from an existing Range via the xlSheet.rows(idx) call. Both insert and delete methods have optional parameters that define how to shift the remaining cells. However, if Range is a complete column/row we can safely skip this parameter. Note how we use “key notation” inside fxRowsRemoved to avoid using dot notation against the delete, which is a JavaScript keyword.

So far we’ve enabled the Spreadsheet population with data from the Flex application. Users can edit data in Flex and changes are automatically propagated to Spreadsheet.

In the next section we’ll look at Spreadsheet-to-Flex data updates.

Synchronizing Flex with Spreadsheet Data ChangesThe code that will send data changed in Spreadsheet back to Flex is presented in Listing 15.10:4

function setupXlDataNotifications() {

xlOWCEvents.sheetChange = function(sheet, range) {

if (!ROW_COUNT || (xlSheet != sheet)) return;

xlOWC.enableEvents = false;

try {

var r1 = range.row;

var c1 = range.column;

var rc = range.rows.count;

var cc = range.columns.count;

if (r1 > ROW_COUNT || c1 > COLUMNS_COUNT) return;

var rows = Math.min(rc, ROW_COUNT - r1 + 1);

Integration with External ApplicationsCHAPTER 15

Page 632: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 637

var cols = Math.min(cc, COLUMNS_COUNT - c1 + 1);

var content = new Array(rows);

for (var i = 0; i < rows ; i++) {

var row = content[i] = {}

for (var j = 0, col = c1 - 1; j < cols; j++, col++) {

var prop = COLUMNS_XL[col];

row[prop] = COLUMNS_FX[prop].xl( range.cells(i+1, j+1).value );

}

}

fxGrid.xlImportRows(r1 - 1, content, false)

}

finally { xlOWC.enableEvents = true; }

};

}

Listing 15.10 Handling data changes in Spreadsheet OWC

The xlOWCEvents.sheetChange method gets two parameters from the corresponding event: the reference to the Worksheet where the event occurred and the range of affected cells. Potentially, this range can be large enough to freeze the program forever. Imagine a use case where a user se-lects several columns and presses the Delete key. The Range parameter for such an event contains 2^16 rows. Clearly, processing all of them would take an unacceptable amount of time. So the first thing the event handler above does is restrict the processing area to some reasonable region. In our case it’s an intersection of the parameter range with (1,1) : (ROW_COUNT, COLUMN_COUNT) area. After getting correct boundaries the only thing left is to construct the payload for the Flex xlImportRows method.

Let’s stress that the above method supports fine-grained change notifications. If the user updates a value only in one cell, then exactly one property of a single object will be updated. If the user selects a range of cells and updates them at once (as a rule via copying clipboard data or deleting values), then the update package will contain only the rows affected with every row containing only affected properties. The xlImportRows method on the Flex side knows about this feature and behaves accordingly.

The definition of the xlOWCEvents.sheetChange method on its own, however, isn’t sufficient. This is where the following strange script block comes into play:

<script language=”JavaScript” for=”xlOWC” event=”sheetChange(sheet,range)”>

xlOWCEvents.sheetChange(sheet,range);

</script>

This is an MSIE-specific way to attach event handlers. A script block must contain a FOR attribute that specifies an ID of the source HTML element and EVENT attribute that describes an event sig-nature. Variable names used inside an event signature may be used in a script block to access event

Integration with External ApplicationsCHAPTER 15

Page 633: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

638 RIA WITH ADOBE FLEX AND JAVA

parameters. If the browser can’t find an HTML control with the ID provided, or a control doesn’t expose the event with the name specified in the signature, then the script block is simply ignored. So a Spreadsheet OWC 9 that has no SheetChange event will be safely taken out of the game with no additional coding.

It’s worth mentioning that certain browsers have no clue what FOR and EVENT attributes are for (no pun indeed). But once they see a script block, they just execute it. Do you recall that initially (see Listing 15.7) we created xlOWCEvents with “empty” methods? Now you know what we did it for – to prevent users of non-Microsoft browsers from JavaScript errors during page loading.

Another important thing: a separate FOR/EVENT script block is the only valid way to assign an event handler to an ActiveX control. If you try to do the same thing via a DOM Level 0 event-han-dling mechanism like ay of those below:

xlOWC.SheetChange = function() {...};

xlOWC.sheetChange = function() {...};

xlOWC.sheetchange = function() {...};

xlOWC.onsheetchange = function() {...};

xlOWC.onSheetChange = function() {...};

you end up with a new property of the Function type in an OBJECT HTML element (a so-called “expando” property) instead of an event handler. If you try an MSIE variation of the DOM Level 2 mechanism:

xlOWC.attachEvent( “SheetChange”, function() { ... } )

you can attach an event handler, but it will cause Spreadsheet OWC/Internet Explorer to crash sooner or later. Try to attach an empty event handler to the SheetChange event this way and edit 8-15 cells. You’ll be forwarded to Microsoft’s support center by a gentle quality feedback agent. It’s hard to say for sure, but attachEvent seems to have issues with certain ActiveX threading models.

The remaining block enhances our example with a custom Spreadsheet menu, the ability to save Spreadsheet content to the local file system, and several other miscellaneous functions:

function setupXlContextMenu() {

function copy_vb_array(variant) {

var vba = new VBArray(variant);

var low = vba.lbound(1), high = vba.ubound(1);

var res = new Array(high - low + 1);

for (var i = low, idx = 0; i <= high; i++) {

var itm = vba.getItem(i);

res[idx++] = typeof itm == ‘unknown’ ? copy_vb_array(itm) : itm ;

}

return res;

Integration with External ApplicationsCHAPTER 15

Page 634: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 639

}

function clone_array(src) {

var size = src.length;

var copy = new Array(size);

for (var i = 0; i < size; i++) {

var itm = src[i];

copy[i] = itm && itm.prototype == Array ? clone_array(itm) : itm;

}

return copy;

}

var xlFileTypes = {};

xlFileTypes[XLConstants.ssExportXMLSpreadsheet]=”Excel”;

xlFileTypes[XLConstants.ssExportHTML]=”HTML”;

function saveFile(format) {

var fileName = window.prompt(“Enter complete path for “ + xlFileTypes[format] + “

file”, “”);

if (!fileName) return;

/* Saving may cause exceptions due to browser */

/* security or if operation canceled by user */

try { xlOWC[“export”]( fileName, XLConstants.ssExportActionNone, format); }

catch(e) {}

}

var customCommands = {

cmdSaveXls: function(){saveFile(XLConstants.ssExportXMLSpreadsheet)},

cmdSaveHtml: function(){saveFile(XLConstants.ssExportHTML )},

cmdSendData: function() {

var region = xlSheet.cells(1, 1).currentRegion;

if (!region) return;

var size = region.row - 1 + region.rows.count;

var items = new Array(size);

for (var i = 0; i < size; i++) {

var row = i + 1; var item = {};

for (var j = 0; j < COLUMNS_COUNT; j++) {

var col = j + 1;

var prop = COLUMNS_XL[j];

item[prop] = COLUMNS_FX[prop].xl( xlSheet.cells(row,col).value );

}

items[i] = item;

}

fxGrid.xlImportRows(0, items, true);

ROW_COUNT = items.length;

Integration with External ApplicationsCHAPTER 15

Page 635: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

640 RIA WITH ADOBE FLEX AND JAVA

} ,

cmdClearFormats: function(){xlOWC.selection.clearFormats()},

cmdClearValues: function(){xlOWC.selection.clearContents()},

cmdClear: function() {

xlOWC.beginUndo();

try {

var r = xlOWC.selection;

r.clearFormats();

r.clearContents();

}

finally { xlOWC.endUndo(); }

}

};

xlOWCEvents.beforeContextMenu = function(x, y, menu, cancel) {

// Check for menu roll-outs from toolbar buttons.

// Try to remove it and see what happens with

// sorting (A-Z / Z-A) commands.

if ( y < 40 ) return;

// First copy original menu from

// OLE VARIANT array to JS array

var ctxMenu = copy_vb_array(menu.value);

var cmClearSubMenu = [

[“&All”, “cmdClear”], [“&Formats”, “cmdClearFormats”], [“&Values”,

“cmdClearValues”]

];

// Place “Clear” within other edit commands

ctxMenu.splice(5, 0, [“Clea&r”, cmClearSubMenu]);

// At the very end of menu add Save / Send to Flex

ctxMenu.push(

null /* separator */, [“Save...”, “cmdSaveXls”], [“Save as HTML...”,

“cmdSaveHtml”],

null /* separator */, [“Send data to FlexGrid”, “cmdSendData”]

);

// Menu and Cancel arguments are ByRef in VB

// In script (both JS and VBS) it is object

// with single property “value”

menu.value = xlVersion >= 11 ? ctxMenu : clone_array(ctxMenu);

};

xlOWCEvents.commandBeforeExecute = function(cmd, cancel) {

Integration with External ApplicationsCHAPTER 15

Page 636: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 641

var handler = customCommands[cmd];

if ( !handler ) return;

/* Disable default processing of command */

cancel.value = true;

handler();

};

}

Listing 15.11 A custom context menu for Spreadsheet OWC

To add our own context menu commands we need to handle at least two events: BeforeContext-Menu and CommandBeforeExecute. BeforeContextMenu alters or replaces the default menu sup-plied by a parameter, while CommandBeforeExecute will help us perform a concrete action when the user selects the menu item.

Note that the menu parameter of BeforeContextMenu comes as a special ByRef type introduced by Spreadsheet OWC to support in/out parameters (unlike Visual Basic neither the JScript nor VB-Script support parameter is passed by reference). Variables of ByRef type have a single property – value.

Altering the context menu is really a tricky part. When you read the menu’s value you get a Visual Basic Array (VT_ARRAY). Every item of this array is either a [Text, CommandIdentifier] pair or a [Text, Submenu] pair to accommodate the arbitrary nesting of submenus. In a sense you are getting an array of arrays. Before you can do anything else you have to convert the original menu to the JavaScript Array – see the recursive copy_vb_array function that does the trick. Handling recursion here is quite easy given that the JavaScript typeof fairly reports “unknown” (and undocumented, one might add) for VT_ARRAY.

Next we can extend the menu with its own items using convenient JavaScript Array methods like splice and push. There are two options for command identifiers: integer codes and strings. Integer codes are reserved by the Spreadsheet OWC so you can only use the ones defined in XLConstants to override the behavior of the existing commands or add existing commands if they’re not avail-able by default. String identifiers are at your disposal to implement custom commands. To insert separator, use null as the menu item.

Remember we converted menu’s value from the VB array to the JavaScript array for modifications? Funny enough, there’s no need to convert the modified array back to a VB array before assign-ing it to a menu.value. There’s another “subtle” problem specific to OWC 10. If a corresponding JavaScript array length has been changed after creation (as a result of assigning an element with the index higher then or equal to the length, invoking push or splice on an array, etc.), the menu is reported as “invalid!” The only workaround is to deep clone the already-built array, supplying the exact length to the constructor at every step.

The functionality of the xlOWCEvents.commandBeforeExecute is simple enough. It attempts a look-

Integration with External ApplicationsCHAPTER 15

Page 637: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

642 RIA WITH ADOBE FLEX AND JAVA

up via the registry of custom commands and executes one, if found. In this case we have to cancel the default menu processing.

There are three commands of particular interest (see object customCommands in Listing 15.11). Two of them – cmdSaveXls and cmdSaveHtml – save the content of the Spreadsheet OWC Compo-nent in a corresponding Excel XML and HTML format. Saving a file to the local file system is subject to security restrictions. So be prepared. These functions may or may not work on a particular client computer depending on its security settings.

The third command, cmdSendData, sends a complete data set back to the Flex application. In the current implementation this is the only way to add rows directly in the Spreadsheet while the xlOW-CEvents.sheetChange event handler ignores any sheet content beyond ROW_COUNT. The xlSheet.cells(1, 1).currentRegion lets us get a continuous region from the first cell to anything bounded by an empty row and empty column. Values from this region are packed as parameters to fxGrid.xlIm-portRows() the same way it was done in xlOWCEvent.sheetChange() (see Listing 15.10).

Note that we don’t disable Spreadsheet events when executing “Clear” commands unlike the xl-Transaction for Flex data notifications (see Listing 15.9). Now when a SheetChange event is fired as a result of a “Clear” command execution, the Flex application content will be cleared as well.

Figure 15.4 The custom context menu for Spreadsheet OWC

Integration with External ApplicationsCHAPTER 15

Page 638: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 643

We’re done. Looking back at Listings 15.3 and 15.7 through 15.11, you may have the feeling that this was all rather complex. However, the complexity is entirely associated with the enormous Spread-sheet OWC API, VB/JScript type conversions, plain OWC bugs, and undocumented JScript features. On the bright side, all the code related to the Flex External API was straightforward and easy to follow.

While being complex, OWC Spreadsheet/Flex integration presents a nice option for hybrid HTML/Flex solutions. On this note we’d like to mention another OWC control – PivotTable, which offers functionality unmatched in Flex. Integration with PivotTable, however, is beyond the scope of this chapter.

One-Way Flex-Excel Integration via the System ClipboardSo far we haven’t mentioned the system clipboard – a solution that lets Flex exchange data with na-tive applications. Flex lets you put text data on the system clipboard via the System.setClipboard() method. (Unlike typical native clipboard APIs Flex supports only one clipboard format – text. An-other limitation, due to security considerations, is that you can’t get the clipboard contents.)

Listing 15.12 presents a slightly modified version of the Flex application used in our previous ex-ample. We removed code related to External notifications and, for simplicity, dropped the fxMeta-data() function in favor of a static constant array or metadata. What we’ve added here is a “Copy to clipboard” button with the action handler:

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

<!-- File ./FxClipboard.mxml -->

<!-- Flex / Native application data exchange via system clipboard -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”

creationComplete=”init()”>

<mx:Script>

<![CDATA[

import mx.controls.Alert;

import mx.collections.IViewCursor;

import mx.collections.ArrayCollection;

[Bindable]

private var employees:ArrayCollection = new ArrayCollection();

[Bindable]

public var bhiOptions:Array = [{label: “No”, data:false}, {label:”Yes”, data:

true}];

private function init():void { loadData(); }

private function copyToClipboard():void {

var nsExcel:Namespace = new Namespace(“urn:schemas-microsoft-com:office:excel”);

Integration with External ApplicationsCHAPTER 15

Page 639: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

644 RIA WITH ADOBE FLEX AND JAVA

var html:XML =

<html

xmlns:x=”urn:schemas-microsoft-com:office:excel”

xmlns:o=”urn:schemas-microsoft-com:office:office”>

<body><table></table></body>

</html>;

var table:XML = html.body[0].table[0];

for (var cursor:IViewCursor = employees.createCursor(); !cursor.afterLast;

cursor.moveNext() ) {

var el:Object = cursor.current;

var row:XML = <tr/>;

for (var idx:int = 0, cnt:int = META.length; idx < cnt; idx++) {

var prop:Object = META[idx];

var name:String = prop.name;

var value:Object = el[name];

var cell:XML = <td nowrap=”nowrap”/>;

switch (prop.type) {

case “string”:

case “number”: break;

case “boolean”:

cell.@nsExcel::bool = value;

break;

case “date”:

var d:Date = value as Date;

value = d != null ?

[d.getFullYear(), pad(d.getMonth() + 1), pad(d.getDate())].join(“-

”) : “”;

break;

default:

value = “”;

}

cell.appendChild( value );

row.appendChild( cell );

}

table.appendChild( row );

};

System.setClipboard( html );

if (ExternalInterface.available) {

var retVal:Object = ExternalInterface.call( “fxCopyContent” );

if (retVal) Alert.show(retVal.message + “: “ + retVal.rows + “x” + retVal.

cols);

}

Integration with External ApplicationsCHAPTER 15

Page 640: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 645

}

private function addRow():void {

if (frmName.text != “”) {

var item:Object = employee(

frmName.text, frmBirthday.selectedDate, frmPhone.text,

frmYos.value, frmHealthInsurance.selectedItem.data, frmSalary.value);

var idx:int = dataGrid.selectedIndex;

var nextIdx:int = idx >= 0 ? idx + 1 : 0;

employees.addItemAt( item, nextIdx );

dataGrid.selectedIndex = nextIdx;

}

}

private function updateRow():void {

var idx:int = dataGrid.selectedIndex;

if (idx >= 0) {

var item:Object = employee(

frmName.text, frmBirthday.selectedDate, frmPhone.text,

frmYos.value, frmHealthInsurance.selectedItem.data, frmSalary.value);

employees.setItemAt(item, idx);

employees.itemUpdated( item );

}

}

private function deleteRow():void {

var idx:int = dataGrid.selectedIndex;

if (idx >= 0) {

employees.removeItemAt(idx);

if ( !employees.length ) {

frmName.text = frmPhone.text = “”; frmBirthday.selectedDate = new Date;

frmYos.value = 1; frmSalary.value = 0;

}

else

dataGrid.selectedItem = employees.getItemAt(Math.min(idx, employees.length

- 1));

}

}

private static const META:Array = [

{name:”name”, title:”Full name”, type:”string”},

{name:”birthday”, title:”Birthday”, type:”date”},

{name:”phone”, title:”Phone”, type:”string”},

{name:”yos”, title:”Years of srv.”, type:”number”},

Integration with External ApplicationsCHAPTER 15

Page 641: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

646 RIA WITH ADOBE FLEX AND JAVA

{name:”healthInsurance”, title:”Health insurance”, type:”boolean”},

{name:”salary”, title:”Salary”, type:”number”}

];

private function applyResult(data:Array):void { employees = new

ArrayCollection(data); }

private function loadData():void {

applyResult([

employee(“Yakov Fain”, d(“1960/04/18”), “732-456-4432”,

yos(“1999/05/20”), true, 95000.0),

employee(“Anatole Tartakovsky”, d(“1962/03/03”), “561-860-2376”,

yos(“1991/03/01”), true, 85000.0),

employee(“Victor Rasputnis”, d(“1963/02/06”), “718-445-7622”,

yos(“1993/07/02”), true, 85900.0),

employee(“Melissa Espinoza”, d(“1950/12/14”), “508-555-2319”,

yos(“1996/04/18”), true, 36490.0),

employee(“Kathleen Poitras”, d(“1966/09/29”), “617-555-3920”,

yos(“1999/05/29”), false, 46200.0),

employee(“Joseph Barker”, d(“1980/02/14”), “617-555-8021”,

yos(“2001/09/10”), false, 27290.0),

employee(“Shih Lin”, d(“1980/12/12”), “617-555-5921”,

yos(“2001/11/11”), false, 33890.0),

employee(“Barbara Blaikie”, d(“1954/11/14”), “617-555-9345”,

yos(“2001/11/20”), true, 54900.0)

]);

}

private static function employee(name:String, birthday:Date, phone:String,

yos:int, healthInsurance:Boolean,

salary:Number):Object {

return { name:name, birthday:birthday, phone:phone,

yos:yos, healthInsurance: healthInsurance, salary:salary };

}

private static function d(s:String):Date {return new Date( Date.parse(s) );}

private static const THIS_YEAR:int = new Date().getFullYear();

private static function yos(startDate:String):int {return THIS_YEAR - d(startDate).

getFullYear();}

private static function formatDate(itm:Object, col:Object):String {

var d:Date = itm.birthday;

return d ? [d.getFullYear(), pad(d.getMonth() + 1), pad(d.getDate())].join(“-”)

Integration with External ApplicationsCHAPTER 15

Page 642: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 647

: “”;

}

private function formatHealthInsurance(itm:Object, col:Object):String {

return bhiOptions[itm.healthInsurance * 1].label;

}

private static function pad(v:int):String { return (v < 10 ? “0” : “”) + v; }

]]>

</mx:Script>

<mx:VBox width=”100%” height=”100%”>

<mx:DataGrid id=”dataGrid” width=”100%” height=”100%” dataProvider=”{employees}”

editable=”false” sortableColumns=”false”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”name” headerText=”Name”/>

<mx:DataGridColumn labelFunction=”formatDate” headerText=”Birthday”/>

<mx:DataGridColumn dataField=”phone” headerText=”Phone”/>

<mx:DataGridColumn dataField=”yos” headerText=”Years of Srv.” textAlign=”r

ight”/>

<mx:DataGridColumn labelFunction=”formatHealthInsurance”

headerText=”Health insurance” textAlign=”center”/>

<mx:DataGridColumn dataField=”salary” headerText=”Salary” textAlign=”right

”/>

</mx:Array>

</mx:columns>

</mx:DataGrid>

<mx:Form width=”100%” height=”100%” borderStyle=”solid” borderColor=”#ffffff”>

<mx:FormItem label=”Name”>

<mx:TextInput id=”frmName” width=”200” text=”{dataGrid.selectedItem.

name}”/>

</mx:FormItem>

<mx:FormItem label=”Birthday”>

<mx:DateField id=”frmBirthday” selectedDate=”{dataGrid.selectedItem.

birthday}”/>

</mx:FormItem>

<mx:FormItem label=”Phone”>

<mx:TextInput id=”frmPhone” width=”200” text=”{dataGrid.selectedItem.

phone}”/>

</mx:FormItem>

Integration with External ApplicationsCHAPTER 15

Page 643: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

648 RIA WITH ADOBE FLEX AND JAVA

<mx:FormItem label=”Years of service”>

<mx:NumericStepper id=”frmYos” value=”{dataGrid.selectedItem.yos}”

maxChars=”3” minimum=”1” maximum=”99”/>

</mx:FormItem>

<mx:FormItem label=”Health insurance”>

<mx:ComboBox id=”frmHealthInsurance” selectedIndex=”{dataGrid.selectedItem.

healthInsurance}”

dataProvider=”{bhiOptions}”/>

</mx:FormItem>

<mx:FormItem label=”Salary”>

<mx:NumericStepper id=”frmSalary” value=”{dataGrid.selectedItem.salary}”

maxChars=”10” minimum=”0” maximum=”999999”/>

</mx:FormItem>

<mx:FormItem>

<mx:HBox>

<mx:Button label=”Update” click=”updateRow()”/>

<mx:Button label=”Add” click=”addRow()”/>

<mx:Button label=”Delete” click=”deleteRow()”/>

<mx:VRule height=”20”/>

<mx:Button label=”Copy to clipboard” click=”copyToClipboard()”/>

</mx:HBox>

</mx:FormItem>

</mx:Form>

</mx:VBox>

</mx:Application>

Listing 15.12 A Flex application exporting data to the clipboard

The Flex application above puts HTML with employee data on the clipboard. Run the application, invoke “Copy to clipboard,” open any text editor, and “paste.” You’ll see formatted HTML source, generated by our Flex application, as shown in Figure 15.5.

Integration with External ApplicationsCHAPTER 15

Page 644: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 649

Figure 15.5 HTML source put on the clipboard

Of all the options, such as fixed width, CSV, and tab separated, we’ve resorted to HTML for a reason. You may notice that we generated a bit of unusual HTML. Some HTML elements and attributes mention an Office Excel namespace. If you recall our Spreadsheet OWC integration example, you can draw parallels with the handling of certain data types. To prove your guess you should open Excel and paste the clipboard content there. Bingo! The cells are populated from the HTML table. Note that this feature is unique to Excel. Neither Word nor Frontpage does the same. Excel is liberal enough with the type of clipboard data: even when the type is plain text, Excel tries to figure out whether the content is HTML.

To take advantage of type-specific Excel formating, we repeated the trick with date type. To force True/False formating of Boolean values, however, we had to explicitly define Excel-specific attri-butes on the TD element.

Flex Embedded in Microsoft ExcelAlthough the solution is quite workable, it requires a manual Paste by the user. Could we somehow automate the Paste? Well, to automate an application it has to support automation. Traditional Microsoft mechanisms like DDE (Dynamic Data Exchange) or OLE are out of our reach when we’re talking about Flex-Excel integration. However, if we embed Flash ActiveX into an Excel worksheet, then we can get by with VBA language.

Integration with External ApplicationsCHAPTER 15

Page 645: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

650 RIA WITH ADOBE FLEX AND JAVA

Figure 15.6 presents the embedding results. The same Flex application FxClipboard is running as ActiveX inside the Excel worksheet FxClipboard.xls. Whenever you modify the data in a Flex application, the up-dates are instantly propagated to the cells of the worksheet. This intimacy between an embedded Flex application and Excel will come in handy later when we establish Flex-to-Flex talk via LocalConnection. For now, there’s work ahead. We need to build communication accommodating FxClipboard.xls.

Figure 15.6 FxClipboard running as ActiveX inside an Excel spreadsheet

Straight ahead. Although it’s possible to activate the “View/Toolbars/Control Toolbox” toolbar in Excel and then insert “Shockwave Flash Object” from the set of additional ActiveX controls, we choose the more challenging option of embedding a Flash Player ActiveX control dynamically. Ob-viously, we need a bit of VBA code here.

The code in Listing 15.13 opts to VBA early binding, i.e., uses variables of concrete types from the ActiveX type library rather than a generic Object type. Accordingly, it’s necessary to add the type library reference to all used libraries. We need to add just an explicit reference to “Shockwave Flash Object” while references to Office and Excel libraries always add automatically on the first use of a VBA editor for a given Workbook.

Microsoft users learned the danger of VBA macros loaded from untrustworthy sources the hard way. So, by default Excel has a very pessimistic security policy: “Don’t execute macros at all.” This is

Integration with External ApplicationsCHAPTER 15

Page 646: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 651

way too restrictive for our example though, so to execute it you’d have to lower the security settings to at least medium, see Figure 15.7. (Medium requires confirmation of the macros every time the document is opened.)

Figure 15.7 Activating an Excel security setting dialog

When all prerequisites are met, go to the VBA editor (F11) and add the following to the Workbook module:

‘ File ./FxClipboard.xls

‘ Object module ThisWorkbook

Private fxProxy As FlexProxy

Private Sub Workbook_Open()

Set fxProxy = New FlexProxy

fxProxy.Initialize 1, “fxObject”

End Sub

Listing 15.13 FxClipboard workbook module

We’ve just declared an event handler for a workbook Open event. The event handler instantiates a helper FlexProxy class that does all the work. Now use the Insert/Class module to add a class mod-ule, rename it FlexProxy, and add the following from Listing 15.14:

‘ File ./FxClipboard.xls

‘ Class module FlexProxy

Option Explicit

Private WithEvents pSheet As Excel.Worksheet

Integration with External ApplicationsCHAPTER 15

Page 647: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

652 RIA WITH ADOBE FLEX AND JAVA

Private WithEvents pFlexObject As ShockwaveFlashObjects.ShockwaveFlash

Private pOleControl As Excel.oleObject

Public Sub Initialize(ByVal sheetID As Variant, ByVal controlID As String)

Set pSheet = Worksheets(sheetID)

Dim oleObject As Excel.oleObject

Dim found As Boolean

found = False

For Each oleObject In pSheet.OLEObjects

If oleObject.Name = controlID Then

found = True

Exit For

End If

Next oleObject

If Not found Then

Set oleObject = pSheet.OLEObjects.Add( _

ClassType:=”ShockwaveFlash.ShockwaveFlash.9”, _

Width:=365, Height:=270, Left:=10, Top:=10 _

)

oleObject.Name = controlID

End If

With oleObject

Call .BringToFront

Call .Activate

End With

Set pOleControl = oleObject

Set pFlexObject = oleObject.Object

With pFlexObject

.Loop = False

.Menu = True

.Playing = False

.EmbedMovie = False

.AllowNetworking = “all”

.AllowScriptAccess = “always”

If Not found Then

.LoadMovie 0, “../bin/FxClipboard.swf”

End If

End With

Call PositionOleControl

Integration with External ApplicationsCHAPTER 15

Page 648: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 653

End Sub

Private Sub pFlexObject_FlashCall(ByVal request As String)

If Not Application.EnableEvents Then Exit Sub

Application.EnableEvents = False

If InStr(1, request, “<invoke name=””fxCopyContent”””) = 1 Then

Call UpdateRows

Dim content As Range

Set content = pSheet.UsedRange

Dim rows As Integer, cols As Integer

If content Is Nothing Then

rows = 0

cols = 0

Else

rows = content.rows.count

cols = content.Columns.count

End If

pFlexObject.SetReturnValue _

“<object>” & _

“<property id=’message’><string>” & “Data copied to Excel” & “</

string></property>” & _

“<property id=’rows’><number>” & rows & “</number></property>” & _

“<property id=’cols’><number>” & cols & “</number></property>” & _

“</object>”

Else

End If

ExitSub:

Application.EnableEvents = True

End Sub

Private Sub UpdateRows()

Dim Target As Excel.Range

Set Target = pSheet.Cells(1, 1)

Call pSheet.Cells.Clear

‘-------------------------------

Target.PasteSpecial xlPasteAll

‘-------------------------------

Call pSheet.Activate

Dim newSelection As Range

Integration with External ApplicationsCHAPTER 15

Page 649: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

654 RIA WITH ADOBE FLEX AND JAVA

Set newSelection = pSheet.UsedRange

Call newSelection.CurrentRegion.Columns.AutoFit

Call newSelection.Select

Call PositionOleControl

Set newSelection = Nothing

End Sub

Private Sub PositionOleControl()

‘Move Flash ActiveX away from used worksheet region

Dim allCells As Range

Set allCells = pSheet.Cells(1, 1).CurrentRegion

pOleControl.Left = 10

If allCells Is Nothing Then

pOleControl.Top = 10

Else

pOleControl.Top = allCells.Top + allCells.Height + 10

End If

Set allCells = Nothing

End Sub

Listing 15.14 FlexProxy class module

The public Initialize method is the main initialization routine for FlexProxy. In VBA it’s impossible to use a parameterized constructor, so we’ve moved all the setup code here. We check the avail-ability of a Flash Player control by iterating over the OLEObjects collection that holds every ActiveX object available on the worksheet. If a control isn’t found, we create and add this object to the collection using the OLEObjects.Add method. Visual Basic supports both named and positional parameters. A lot of OLEObjects.Add arguments are irrelevant in our use case. The only important one is the ClassType that defines the ProgID of the ActiveX object to embed; all the rest define the control position. Accordingly, we resort to named parameters as the simplest option.

When reopening the workbook with the embedded Flash Player, a corresponding SWF movie (com-piled from FxClipboard.mxml, see Listing 15.2) starts playing automatically, so it’s necessary to call LoadMovie explicitly in case we just added the Flash Player control. The path to FxClipboard.swf is relative to the Excel document. Adjust it as necessary (see Listing 15.14, method Initialize).

The next method, pFlexObject_FlashCall, is of particular interest. It deals with handling notifica-tions of the External API, but this time in a desktop application.

The FlexProxy class module defines an instance variable pFlexObject type of ShockwaveFlashOb-

Integration with External ApplicationsCHAPTER 15

Page 650: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 655

jects.Shockwave.Flash. The variable is declared using a “With Events” statement that enables attaching event handlers to the object. The event handler must be a procedure with the name VariableName_EventName. This procedure’s signature must match the order and types of event parameters. In the case of the Flex External API there’s only the Request parameter of the String type. But, in fact, it’s a serialized form of a complex XML document. The format of this document deserves a close look and we dedicate the following section to it.

XML Format of External APIAlthough the External API-based communications in the Flex-ActiveX container and Flex-Web browser are similar, there’s one important difference. When Flex ActionScript communicates with a browser’s JavaScript, the functions are called directly; the formatting details of the function call requests and responses are encapsulated behind the ExternalInterface.call(). However, when the Ex-ternal API is facilitating communication with an ActiveX container application, we have to under-stand the specific XML messages sent by the Flash Player in place of function calls and return values. Importantly, Flash Player expects the container application to send its function calls (flashObject.CallFunction) and return values (flashObject.SetReturnValue) in the same XML format.

There are two parts to the XML format used by the External API: the first part is used to represent function calls and the second one defines the formatting of individual values. The second part is used to marshal function parameters and return values.

The following XML fragment shows an example of an XML-formatted function call:

<invoke name=”functionName” returntype=”xml”>

<arguments>

... (individual argument values)

</arguments>

</invoke>

The root node is the <invoke> node. It has two attributes: name, containing the name of the func-tion to call, and returntype, which has to be the XML for the ActiveX container other than the HTML page5. If the function call has parameters, the <invoke> node contains the <arguments> child node with individual parameter values formatted, as we’ll explain a bit later. Our trivial non-parameter notification Request looks like this:

<invoke name=”fxCopyContent” returntype=”xml”><arguments/></invoke>

Now you can see the naïve approach used to check the method name in the pFlexObject_FlashCall from Listing 15.14:

If InStr(1, request, “<invoke name=””fxCopyContent”””) = 1 Then

Call UpdateRows

. . . .

Integration with External ApplicationsCHAPTER 15

Page 651: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

656 RIA WITH ADOBE FLEX AND JAVA

If the request prologue looks like a fxCopyContent notification, the event handler delegates the pro-cessing to the UpdateRows method that just pastes the content of the clipboard into the worksheet starting with the cell (1,1).

To set a return value from an ActiveX container you must use the SetReturnValue method on a Flash object. This method can be invoked only in the CallFunction event handler. If an invocation of the SetReturnValue is omitted, ActionScript receives undefined as a return value from the Exter-anlInterface.call. Otherwise the ActionScript object parsed from the XML is returned. For example, we reply with a message about the successful operation and the number of rows/columns copied. The encoded XML form of the return value is the following:

<object>

<property id=”message”><string>Data copied to Excel</string></property>

<property id=”rows”><number>8</number></property>

<property id=”cols”><number>6</number></property>

“</object>”

On the ActionScript side this is transformed to an Object with the fields message, rows, and cols.

To illustrate support for all Action Script classes both as <arguments> in the CallFunction method/FlashCall event and as a result returned with the SetReturnValue, the complete formating scheme, shown in Table 15.2, includes data type information in addition to the actual values:

Integration with External ApplicationsCHAPTER 15

Page 652: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 657

ActionScript class/value XML Format Comments

Null <null/>

Boolean true <true/>

Boolean false <false/>

Number, int, uint <number>3.14</number> <number>-255</number>

String <string>Hello, Flex!</string>

Date <date>1151614800000</date> The encoded value is the number of mil-liseconds since January 1, 1970, the same result as Date.getTime() or Date.valueOf()

Array <array> <property id=”0”> <string>”ABC”</string> </property> <property id=”1”> <string>”XYZ”</string> </property> <property id=”2”> <number>128</number> </property> ... </array>

Elements can be any supported type; mixed types are supported and arbitrary nesting is allowed. The <property> node defines individual elements, and the ID attribute is the numeric, zero-based index.

Object <object> <property id=”name”> <string>Alex Whitney</string> </property> <property id=”salary”> <number>72000</number> </property> ... </object>

Individual properties are defined in the <property> element; the ID attribute is the property name (a string).

Custom object <null> or <object></object> Either a null or empty object. Any proper-ties of the custom objects are lost.

Table 15.2 External API formatting scheme

The set of supported types is rich enough to serialize the structure of almost any complexity and nesting except with two limitations. The first one is the lack of support for custom objects. The sec-ond serious restriction of the External API is that it doesn’t handle recursive structures correctly, or strictly speaking, it doesn’t track visited objects during marshaling at all:

var x:Object = {a:”The First”};

Integration with External ApplicationsCHAPTER 15

Page 653: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

658 RIA WITH ADOBE FLEX AND JAVA

var y:Object = {a:”The Second”};

x.b = y; y.b = x;

var retVal:Object = ExternalInterface.call( “someFunction”, x );

When the following ActionScript snippet is invoked, the External API XML serializer falls into infi-nite recursion that ends up with a StackOverflow exception.

Be aware that serialization to/from XML has its own associated cost in non-linear dependency so passing large objects via the External API can seriously slow your application down. As a general rule, always analyze what exactly is necessary on either side of the ExternalInterface invocation and avoid sending a complete graph of the objects if only a subset of the data is sufficient.

LiveLink: Standalone Flex - ExcelIn the following sections of the chapter we’ll integrate a standalone Flex application with a stand-alone Excel document. The LocalConnection class will play a main role in this effort, which enables one-way Flex-to-Flex communications (to be exact, we should say Flash-to-Flash communication). Ultimately, our communication scheme will look like “Flex Application-Flex Agent-Excel” where the FlexAgent, embedded inside the Excel spreadsheet, talks to Excel via the External API. Mean-while, the FlexApplication-Flex Agent communication link will be based on the LocalConnection.

LocalConnection 101LocalConnection has been available since Flash 6.0. There’s a saying that it takes two to lie: one to lie and one to listen. The same rule applies to LocalConnection: you need one SWF file that sends notifications and another one that listens to the notifications.

The first SWF file is commonly called the sending SWF file; it’s the application that invokes the method on the other application. The sending SWF file must create a LocalConnection object and call the send() method. The second SWF file is called the receiving SWF file; it’s the application whose method gets invoked. The receiving SWF must create another LocalConnection object and call the connect() method.

In its simplest form, a sending application uses LocalConnection as follows:

var LocalConnection sendingLC = new LocalConnection();

sendingLC.addEventListener(StatusEvent.STATUS, statusHandler );

sendingLC.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler

);

sendingLC.send( “MyConnection”, “remoteFunction”, arg1, arg2, ..., argN);

...

private function securityErrorHandler(ev:SecurityErrorEvent):void { /* Handle

Integration with External ApplicationsCHAPTER 15

Page 654: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 659

security errors */ }

private function statusHandler(ev:StatusEvent):void { /*Handle status errors like

failed calls to send */ }

A corresponding receiving application in its connect() call matches the name used in the send()above and provides the implementation for the remoteFunction and any other functions in-voked by sending applications:

var LocalConnection receivingLC = new LocalConnection();

receivingLC.client = this;

receivingLC.connect( “MyConnection” );

...

private function remoteFunction(arg1:type1, arg2: type2, ..., argN:typeN):void {

/* Function implementation */

}

Importantly, only one serving application is allowed per connection. In other words, only one ap-plication may successfully call LocalConnection.connect() with the given connection name at a time. If you attempt to connect() with the same name from the second LocalConnection, an Argu-mentError will be thrown.

The core driver of LocalConnection is defined via the client property. This property should reference the object that implements methods remotely called via send(), so in the code above we set it to this reference, pointing to receiving the application itself. If the receiving application doesn’t implement the “sent for” method (the method isn’t present, the function name is misspelled, the number or types of method parameters doesn’t match expectations of the send()), a runtime AsyncError will be thrown. Note that it’s the receiving application that will be getting the exception. Don’t attempt to catch the AsyncError with a regular try/catch block; since the remote function is invoked asyn-chronously, you may only assign a listener for the AsyncErrorEvent and follow up with the custom error-handling strategy.

The remote function in the code snippet above is declared void. This is not accidental; in fact, it’s a major limitation of LocalConnection: all communications are one-way; you may not get a reply from a receiving application like you would with the External API.

You may have noticed that we supplied listeners for Status and SecurityError events for the send-ing LocalConnection. Both events are fired as a result of the send() method. Status reports carries well the status of LocalConnection for every invocation of the send() – either status or error – see the property level of the event. For instance, you may get an error event when no listening applica-tion is running. A SecurityError event is raised when send() attempts to cross the security sandbox boundaries.

Integration with External ApplicationsCHAPTER 15

Page 655: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

660 RIA WITH ADOBE FLEX AND JAVA

LocalConnection is subject to certain security restrictions. By default, a connection is allowed only between SWF files from the same domain. Otherwise, the receiving application must enable the sending domain. Interestingly, these rules are intervened with the names you use with methods send() and connect() – plain as in the earlier example, qualified with a domain prefix, or global that starts with the underscore character.

In the simplest case when both applications originate from the same domain, LocalConnection communications are enabled with no additional effort. This happens, in particular, when you use just the name of the application without specifying a domain name.

The more complex case is when applications are loaded from different domains. Assuming both sending and receiving applications know the names in advance, the following must be done for LocalConnection communication:

1. A receiving application must enable the sending domain with an explicit receivingLC.allowDomain(“sender.domain.com”) or an indiscriminate receivingLC.allowDomain(*). Make no mistake, allowDomain is called on the LocalConnection object, not to be confused with Security.allowDomain(“domain”)!

2. A receiving application must connect() using a “plain” connection name: receivingLC.connect(“MyConnection”);

3. A sending application must use a connection name qualified with the domain prefix: send-ingLC.send(“sender.domain.com:MyConnection”, “remoteFunction”, args).

Instead of typing the full domain name prefix, you can use a connection name that starts with an underscore like “_MyConnection” on both sides. Either way, the receiving application must allow a domain with an allowDomain() call.

If you don’t specify an explicit domain prefix, or if you use a name beginning with “_”, LocalCon-nection adds the default prefix of the SWF origin domain to the name of the connection for both the send() and connect().This enables strict, yet flexibly configurable security and helps avoid name clashes between SWF files coming from different vendors (and, hence, from different domains). You may think of a qualified connection name as a name defined in a certain namespace. Note that you can’t define the explicit domain prefix with the connect() method of the receiving connec-tion. To understand how security and the namespacing mechanism work together, let’s consider examples of the three security options above.

In the simplest case, assume that both the sending and receiving SWF files are loaded from the www.theriabook.com site. The actual name of the connection in both cases is the “theriabook.com:MyConnection” and hence the matching receiving connection for LocalConnection.send will be found once the unqualified connection name is converted to the qualified one.

Now, imagine that the sending application is loaded from www.other-domain.com. In this case a default qualified name of the sending connection is “other-domain.com:MyConnection” and there’s no matching receiving connection. However, once you do receivingLC.allowDomain(“other-

Integration with External ApplicationsCHAPTER 15

Page 656: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 661

domain.com”) the LocalConnection API creates some internal matcher that connects the “theri-abook.com:MyConnection” name and “other-domain.com:MyConnectionName,” allowing other-domain.com.

Finally, if a connection name starts with an underscore, no domain prefixes are added on either side. So the matching connection can be found by default. However, the receiving security man-ager prevents incoming calls originating from other domains. So, again it’s necessary to explicitly allow the sender’s domain or, subject to your discretion, allow all with receivingConnection.allow-Domains(“*”).

There’s another LocalConnection API limitation that’s worth mentioning. The amount of data you can pass with LocalConnection.send is limited to 40KB in serialized form. If you exceed this limit, you’ll get an ArgumentError exception. The only way to overcome this limitation is to send chunks of data. Sadly, the serialization format isn’t documented, so it’s hard to imagine another algorithm to calculate the size of the chunk except by trial and error.

Typically, the receiving LocalConnection is automatically destroyed when its reference variable is garbage-collected. If the receiving LocalConnection is declared an application variable, the con-nection is destroyed when the SWF file is unloaded. You can also programmatically close the con-nection using an explicit close() call. Once the connection is closed, you may reconnect with the same connection name from any Flex application, including the original one.

Table 15.3 summarizes the functionality available for LocalConnection running in both sending and receiving modes:

Sending LocalConnection Receiving LocalConnection

sendingLC = new LocalConnection(); receivingLC = new LocalConnection();

sendingLC.addEventListener( StatusEvent.STATUS, statusHandler);

receivingLC.addEventListener( AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);

sendingLC.addEventListener( SecurityErrorEvent.SECURITY_ERROR securityErrorHandler);

receivingLC.allowDomain(“domain.com”); receivingLC.allowInsecureDomain(“domain.com”);

receivingLC.client = implObject;

receivingLC.connect(“ConnectionName”);

sendingLC.send( “domain.com:ConnectionName”, “function”, arg1, arg2, ..., argN);

receivingLC.close();

Table 15.3 LocalConnection API

Integration with External ApplicationsCHAPTER 15

Page 657: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

662 RIA WITH ADOBE FLEX AND JAVA

Flex Excel Agent ApplicationNow that we’ve looked at LocalConnection we can move closer to integrating Flex applications with the native Microsoft Excel.

As previously discussed, we need a Flex agent to interface with Excel via the External API. The code for the corresponding application – FxExcelAgent – is presented in Listing 15.15.

According to our plan, FxExcelAgent and a regular Flex application communicate with each other using the LocalConnection API. Actually, since LocalConnection provides only one-way commu-nication, we need two connections to enable data exchange in both directions: outConnection, dedicated to forwarding External API calls to a Flex application, and inConnection, promoting re-ceived notifications to Excel.

The initialization of the agent starts with an External API availability check. The existence of the External API is mandatory for FlexAgent; if it’s unavailable the agent is useless and no further ini-tialization is done.

The core function of the agent is in forwarding External API calls to the data application via outCon-nection, and promoting notifications received by inConnection to Excel with the External API. The only External API call that has no corresponding LocalConnection invocation is the fxQueryChan-nel() method. The method is used by FxAgent to construct the names of outbound/inbound Local-Connections. To keep the agent generic, we avoided hard-coded names. Instead, the agent queries the hosting Excel document to return the channel ID – the unique name of the bidirectional com-munication link composed by two LocalConnection objects. To compose names of outbound and inbound connections we add _Agent2App and _App2Agent suffixes to this ID correspondingly. If a channel ID starts with an underscore, then “global” connection names are created. In this case the outConnection object is modified to allow connections from any domain.

This approach is generic and configurable. You can switch which Flex application the Excel work-book connects to by altering just one workbook property. Further, you can compose other Excel documents using the workbook created in this example as a template: simply use the Excel com-mand “New from existing workbook…” instead of “New blank workbook.” Again, the only configu-ration effort for the “derived” workbook is to modify the channel ID.

After setting up connections, the agent forces the Flex application to resubmit both metadata and data. This resolves the race condition case when a Flex application has been started before an Excel document with embedded Flex agent is opened. It’s similar to the xlResend callback in the Spread-sheet OWC example.

Each and every step is logged into the List control. This helps to understand the order of events and what’s going on “under the hood.” Figure 15.8 shows a FxExcelAgent running inside an Excel spreadsheet:

Integration with External ApplicationsCHAPTER 15

Page 658: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 663

Figure 15.8 Excel spreadsheet with an embedded FxExcelAgent application

Here is the code for the FxExcelAgent:

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

<!-- File ./FxExcelAgent.mxml -->

<!-- Flex Agent running within Microsoft Excel -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”

creationComplete=”onCreationComplete()”>

<mx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

[Bindable]

private var messages:ArrayCollection = new ArrayCollection;

private var inConnection:LocalConnection;

private var outConnection:LocalConnection;

private var outConnectionName:String;

private function onCreationComplete():void {

message(INFO, “Application loaded”);

if (!ExternalInterface.available) {

message(ERROR, “Flex container does not support external interface”);

txtStatus.text = “Disabled: no external interface”;

return;

}

// Code here is executed only if there is ExternalInterface available

var channel:String = queryChannel();

var isOk:Boolean = !!channel;

if (isOk) outConnectionName = channel + “_Agent2App”;

isOk = isOk && openReceivingConnection(channel + “_App2Agent”);

isOk = isOk && openSendingConnection();

if (isOk) {

xlResend();

ExternalInterface.addCallback(“xlResend”, xlResend);

Integration with External ApplicationsCHAPTER 15

Page 659: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

664 RIA WITH ADOBE FLEX AND JAVA

ExternalInterface.addCallback(“xlImportRows”, xlImportRows);

}

}

private function queryChannel():String {

var channel:String;

try {

phase(“Querying channel ID...”);

channel = ExternalInterface.call(“fxQueryChannel”);

if ( !channel ) message(ERROR, “Hosting application fails to return chan

nel id”); else ok();

} catch(ex:Error) {

message(ERROR, ex.errorID + “:” + ex.message );

}

if (!channel) txtStatus.text = “Disabled: channel id unavailable”;

return channel;

}

private function openReceivingConnection(connectionName:String):Boolean {

inConnection = new LocalConnection();

inConnection.addEventListener(AsyncErrorEvent.ASYNC_ERROR,

asyncErrorHandler);

inConnection.client = this;

var isConnected:Boolean = false;

try {

if ( connectionName.indexOf(“_”) == 0 ) {

phase(“Updating security settings...”);

inConnection.allowDomain(“*”);

inConnection.allowInsecureDomain(“*”);

ok(); }

phase(“Connecting to \”” + connectionName + “\”...”);

inConnection.connect( connectionName );

isConnected = true;

ok();

message(INFO, “App->Agent connected via \”” + connectionName + “\””);

txtStatus.text = “Ready: listening to \”” + connectionName + “\””;

} catch (ex:ArgumentError) {

message(ERROR, ex.message);

} catch (ex:SecurityError) {

Integration with External ApplicationsCHAPTER 15

Page 660: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 665

message(ERROR, ex.message);

}

if (!isConnected) txtStatus.text = “Disabled: fail to establish local

connection”;

return isConnected;

}

private function openSendingConnection():Boolean {

try {

outConnection = new LocalConnection();

outConnection.addEventListener(StatusEvent.STATUS, statusHandler);

outConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR,

securityErrorHandler);

} catch (ex:Error) {

message(ERROR, “Error creating Agent->App connection”);

return false;

}

message(INFO, “Agent->App connection created”);

return true;

}

public function xlResend():void {

outConnection.send(outConnectionName, “lcResend”);

}

public function xlImportRows(offset:int, cells:Array, reset:Boolean):void {

outConnection.send(outConnectionName, “lcImportRows”, offset, cells, reset) ;

}

public function fxMetadata(metadata:Array):void {

if ( !checkConnected(“fxMetadata”) ) return;

message( META,

[“Remote query \”fxMetadata\”, “,

“fields-count=”, metadata.length].join(‘’) );

ExternalInterface.call(“fxMetadata”, metadata);

}

public function fxRowsInserted(offset:int, content:Array):void {

if ( !checkConnected(“fxRowsInserted”) ) return;

message( INSERT,

[“Remote query \”fxRowsInserted\”, “,

“offset=”, offset, “, count=”, content.length].join(‘’) );

Integration with External ApplicationsCHAPTER 15

Page 661: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

666 RIA WITH ADOBE FLEX AND JAVA

ExternalInterface.call(“fxRowsInserted”, offset, content);

}

public function fxRowsUpdated(offset:int, content:Array, reset:Boolean):void {

if ( !checkConnected(“fxRowsUpdated”) ) return;

message( reset ? RELOAD : UPDATE,

[“Remote query \”fxRowsUpdated\”, “,

“reset=”, reset, “, “,

“offset=”, offset, “, count=”, content.length].join(‘’) );

ExternalInterface.call(“fxRowsUpdated”, offset, content, reset);

}

public function fxRowsRemoved(offset:int, count:int):void {

if ( !checkConnected(“fxRowsRemoved”) ) return;

message( REMOVE,

[“Remote query \”fxRowsRemoved\”, “,

“offset=”, offset, “, count=”, count].join(‘’) );

ExternalInterface.call(“fxRowsRemoved”, offset, count);

}

private function securityErrorHandler(ev:SecurityErrorEvent):void {

message(ERROR, “Security error: “ + ev.text ); }

private function asyncErrorHandler(ev:AsyncErrorEvent):void { message( ER-

ROR, “Async error: “ + ev.text ); }

private function statusHandler(ev:StatusEvent):void {

var severity:Number = {error:ERROR, status: INFO, warnin:WARN}[ev.level];

if (!severity) severity = INFO;

message(severity, “Agent->App communication “ + ev.level + (ev.code ? “,

code = “ + ev.code : “”) );

}

private function checkConnected(query:String):Boolean {

if ( inConnection) return true;

message(WARN, “Agent not connected, remote query \”” + query + “\”

skipped.”);

return false;

}

private function phase(title:String):void { message(WAIT, title); }

private function ok():void {

var item:Object = messages.getItemAt(0);

item.icon = ICONS[OK];

item.label = item.label + “ Done.”;

Integration with External ApplicationsCHAPTER 15

Page 662: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 667

messages.itemUpdated(item);

lstMessages.selectedIndex = 0;

}

private function message(status:int, message:String):void {

messages.addItemAt({label:message,icon:ICONS[status]}, 0);

lstMessages.selectedIndex = 0;

}

/* Image resources */

[Embed(source=”assets/icons/wait.png”)]

private static const icoWait:String;

[Embed(source=”assets/icons/ok.png”)]

private static const icoOK:String;

[Embed(source=”assets/icons/info.png”)]

private static const icoInfo:String;

[Embed(source=”assets/icons/warn.png”)]

private static const icoWarn:String;

[Embed(source=”assets/icons/error.png”)]

private static const icoError:String;

[Embed(source=”assets/icons/metadata.png”)]

private static const icoMeta:String;

[Embed(source=”assets/icons/insert.png”)]

private static const icoInsert:String;

[Embed(source=”assets/icons/update.png”)]

private static const icoUpdate:String;

[Embed(source=”assets/icons/reload.png”)]

private static const icoReload:String;

[Embed(source=”assets/icons/remove.png”)]

private static const icoRemove:String;

private static const ICONS:Array = [

icoWait, icoOK, icoInfo, icoWarn, icoError,

icoMeta, icoInsert, icoUpdate, icoReload, icoRemove

];

private static const WAIT:int = 0;

private static const OK:int = 1;

private static const INFO:int = 2;

private static const WARN:int = 3;

private static const ERROR:int = 4;

private static const META:int = 5;

private static const INSERT:int = 6;

Integration with External ApplicationsCHAPTER 15

Page 663: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

668 RIA WITH ADOBE FLEX AND JAVA

private static const UPDATE:int = 7;

private static const RELOAD:int = 8;

private static const REMOVE:int = 9;

]]>

</mx:Script>

<mx:VBox width=”100%” height=”100%” verticalGap=”0”>

<mx:Text id=”txtTitle” color=”#FFFF80”

text=”FxAgent for Microsoft Excel” width=”100%” />

<mx:HRule width=”100%” strokeWidth=”1” strokeColor=”#FFFF80”/>

<mx:Text id=”txtStatus” text=”Starting...” width=”100%”></mx:Text>

<mx:List id=”lstMessages” dataProvider=”{messages}” editable=”false”

width=”100%” rowHeight=”18” liveScrolling=”true”

rowCount=”10”></mx:List>

</mx:VBox>

</mx:Application>

Listing 15.15 FxExcelAgent application

Remember that FxExcelAgent is designed to run embedded inside Excel. We’ll be looking at what needs to be done for that once we’re through with the Flex-to-Flex LocalConnection discussion.

Flex Standalone ApplicationLet’s build the sample standalone Flex application. Actually, we’re going to use the same applica-tion that was used in the Spreadsheet OWC integration example. The only change we had to make was to replace the notification mechanism based on the External API with one based on a Local-Connection. The complete application code is in Listing 15.16.

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

<!-- File ./FxExcelSample.mxml -->

<!-- Flex Application providing data to Microsoft Excel -->

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”

creationComplete=”onCreationComplete()”>

<mx:Script>

<![CDATA[

import mx.controls.Alert;

import mx.collections.ArrayCollection;

[Bindable]

private var employees:ArrayCollection = new ArrayCollection();

[Bindable]

public var bhiOptions:Array = [{label: “No”, data:false}, {label:”Yes”, data:

true}];

Integration with External ApplicationsCHAPTER 15

Page 664: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 669

private static const LISTEN_AGENT:String = “_FlexExcelDemo_Agent2App”;

private static const UPDATE_AGENT:String = “_FlexExcelDemo_App2Agent”;

private var outConnection:LocalConnection;

private var inConnection:LocalConnection;

private function onCreationComplete():void {

outConnection = new LocalConnection;

outConnection.addEventListener(StatusEvent.STATUS, statusHandler);

outConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR,

securityErrorHandler);

inConnection = new LocalConnection;

inConnection.client = this;

try {

if (LISTEN_AGENT.indexOf(“_”) === 0) {

inConnection.allowDomain(“*”);

inConnection.allowInsecureDomain(“*”);

}

inConnection.connect(LISTEN_AGENT);

} catch(ex:ArgumentError) {

Alert.show(ex.message, “Connection error”, Alert.OK);

} catch(ex:SecurityError) {

Alert.show(ex.message, “Security error”, Alert.OK);

}

fxMetadata();

loadData();

}

public function lcResend():void {

fxMetadata();

fxRowsUpdated(0, employees.toArray(), true);

}

public function IcImportRows(offset:int, cells:Array, reset:Boolean):void {

if (reset) {

if (employees) employees.removeAll();

else employees = new ArrayCollection();

cells.forEach( function(el:Object,idx:int,array:Array):void {

var employee:Object = {};

for (var name:String in el) employee[name] = el[name];

employees.addItem(employee);

});

Integration with External ApplicationsCHAPTER 15

Page 665: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

670 RIA WITH ADOBE FLEX AND JAVA

if (employees.length)

dataGrid.selectedIndex = 0;

}

else

cells.forEach( function(el:Object,idx:int,array:Array):void {

var pos:int = idx + offset;

var employee:Object = employees.getItemAt(pos);

for (var name:String in el)

employee[name] = el[name];

employees.setItemAt(employee, pos);

});

}

private function addRow():void {

if (frmName.text != “”) {

var item:Object = employee(

frmName.text, frmBirthday.selectedDate, frmPhone.text,

frmYos.value, frmHealthInsurance.selectedItem.data, frmSalary.value

);

var idx:int = dataGrid.selectedIndex;

var nextIdx:int = idx >= 0 ? idx + 1 : 0;

employees.addItemAt( item, nextIdx );

dataGrid.selectedIndex = nextIdx;

fxRowsInserted(idx, [item]);

}

}

private function updateRow():void {

var idx:int = dataGrid.selectedIndex;

if (idx >= 0) {

var item:Object = employee(

frmName.text, frmBirthday.selectedDate, frmPhone.text,

frmYos.value, frmHealthInsurance.selectedItem.data, frmSalary.value

);

employees.setItemAt(item, idx);

employees.itemUpdated( item );

fxRowsUpdated(idx, [item], false);

}

}

private function deleteRow():void {

var idx:int = dataGrid.selectedIndex;

if (idx >= 0) {

employees.removeItemAt(idx);

Integration with External ApplicationsCHAPTER 15

Page 666: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 671

if ( !employees.length ) {

frmName.text = frmPhone.text = “”;

frmBirthday.selectedDate = new Date;

frmYos.value = 1; frmSalary.value = 0;

}

else

dataGrid.selectedItem = employees.getItemAt(Math.min(idx, employees.

length - 1));

fxRowsRemoved(idx, 1);

}

}

private function applyResult(data:Array):void {

employees = new ArrayCollection(data);

fxRowsUpdated(0, data, true);

}

private function fxRowsInserted(offset:int, content:Array):void {

outConnection.send(UPDATE_AGENT, “fxRowsInserted”, offset, content);

}

private function fxRowsUpdated(offset:int, content:Array, reset:Boolean):void {

outConnection.send(UPDATE_AGENT, “fxRowsUpdated”, offset, content, reset);

}

private function fxRowsRemoved(offset:int, size:int):void {

outConnection.send(UPDATE_AGENT, “fxRowsRemoved”, offset, size);

}

private function fxMetadata():void {

outConnection.send(UPDATE_AGENT, “fxMetadata”, [

{name:”name”, title:”Full name”, type:”string”},

{name:”birthday”, title:”Birthday”, type:”date”},

{name:”phone”, title:”Phone”, type:”string”},

{name:”yos”, title:”Years of srv.”, type:”number”},

{name:”healthInsurance”, title:”Health insurance”, type:”boolean”},

{name:”salary”, title:”Salary”, type:”number”}

]);

}

private function loadData():void {

applyResult([

employee(“Yakov Fain”, d(“1960/04/18”), “732-456-4432”,

Integration with External ApplicationsCHAPTER 15

Page 667: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

672 RIA WITH ADOBE FLEX AND JAVA

yos(“1999/05/20”), true, 95000.0),

employee(“Anatole Tartakovsky”, d(“1962/03/03”), “561-860-2376”,

yos(“1991/03/01”), true, 85000.0),

employee(“Victor Rasputnis”, d(“1963/02/06”), “718-445-7622”,

yos(“1993/07/02”), true, 85900.0),

employee(“Melissa Espinoza”, d(“1950/12/14”), “508-555-2319”,

yos(“1996/04/18”), true, 36490.0),

employee(“Kathleen Poitras”, d(“1966/09/29”), “617-555-3920”,

yos(“1999/05/29”), false, 46200.0),

employee(“Joseph Barker”, d(“1980/02/14”), “617-555-8021”,

yos(“2001/09/10”), false, 27290.0),

employee(“Shih Lin”, d(“1980/12/12”), “617-555-5921”,

yos(“2001/11/11”), false, 33890.0),

employee(“Barbara Blaikie”, d(“1954/11/14”), “617-555-9345”,

yos(“2001/11/20”), true, 54900.0)

]);

}

private static function employee(name:String, birthday:Date, phone:String,

yos:int, healthInsurance:Boolean, salary:Number):Object {

return {

name:name, birthday:birthday, phone:phone,

yos:yos, healthInsurance: healthInsurance, salary:salary

};

}

private static function d(s:String):Date {return new Date( Date.parse(s) );}

private static const THIS_YEAR:int = new Date().getFullYear();

private static function yos(startDate:String):int {return THIS_YEAR - d(startDate).

getFullYear();}

private static function formatDate(itm:Object, col:Object):String {

var d:Date = itm.birthday;

return d ? [d.getFullYear(), pad(d.getMonth() + 1), pad(d.getDate())].join(“-”)

: “”;

}

private function formatHealthInsurance(itm:Object, col:Object):String {

return bhiOptions[itm.healthInsurance * 1].label;

}

private static function pad(v:int):String { return (v < 10 ? “0” : “”) + v; }

private function securityErrorHandler(event:SecurityErrorEvent):void { Alert.

Integration with External ApplicationsCHAPTER 15

Page 668: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 673

show(event.text, “Security error”, Alert.OK); }

private function statusHandler(event:StatusEvent):void {}

]]>

</mx:Script>

<mx:VBox width=”100%” height=”100%”>

<mx:DataGrid id=”dataGrid” width=”100%” height=”100%” dataProvider=”{employees}”

editable=”false” sortableColumns=”false”>

<mx:columns>

<mx:Array>

<mx:DataGridColumn dataField=”name” headerText=”Name”/>

<mx:DataGridColumn labelFunction=”formatDate” headerText=”Birthday”/>

<mx:DataGridColumn dataField=”phone” headerText=”Phone”/>

<mx:DataGridColumn dataField=”yos” headerText=”Years of Srv.” textAlign=”r

ight”/>

<mx:DataGridColumn labelFunction=”formatHealthInsurance”

headerText=”Health insurance” textAlign=”center”/>

<mx:DataGridColumn dataField=”salary” headerText=”Salary” textAlign=”right

”/>

</mx:Array>

</mx:columns>

</mx:DataGrid>

<mx:Form width=”100%” height=”100%” borderStyle=”solid” borderColor=”#ffffff”>

<mx:FormItem label=”Name”>

<mx:TextInput id=”frmName” width=”200” text=”{dataGrid.selectedItem.

name}”/>

</mx:FormItem>

<mx:FormItem label=”Birthday”>

<mx:DateField id=”frmBirthday” selectedDate=”{dataGrid.selectedItem.birth

day}”/>

</mx:FormItem>

<mx:FormItem label=”Phone”>

<mx:TextInput id=”frmPhone” width=”200” text=”{dataGrid.selectedItem.

phone}”/>

</mx:FormItem>

<mx:FormItem label=”Years of service”>

<mx:NumericStepper id=”frmYos” value=”{dataGrid.selectedItem.yos}”

maxChars=”3” minimum=”1” maximum=”99”/>

</mx:FormItem>

<mx:FormItem label=”Health insurance”>

<mx:ComboBox id=”frmHealthInsurance”

Integration with External ApplicationsCHAPTER 15

Page 669: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

674 RIA WITH ADOBE FLEX AND JAVA

selectedIndex=”{dataGrid.selectedItem.healthInsurance}”

dataProvider=”{bhiOptions}”/>

</mx:FormItem>

<mx:FormItem label=”Salary”>

<mx:NumericStepper id=”frmSalary” value=”{dataGrid.selectedItem.salary}”

maxChars=”10” minimum=”0” maximum=”999999”/>

</mx:FormItem>

<mx:FormItem>

<mx:HBox>

<mx:Button label=”Update” click=”updateRow()”/>

<mx:Button label=”Add” click=”addRow()”/>

<mx:Button label=”Delete” click=”deleteRow()”/>

</mx:HBox>

</mx:FormItem>

</mx:Form>

</mx:VBox>

</mx:Application>

Listing 15.16 The Flex data source application

Excel Worksheet – The Flex Agent HostFinally, we must create a Microsoft Excel document that embeds a Flex agent application (to be precise, it embeds a Flash Player ActiveX set to play the FxExcelAgent.swf) and provides the VBA modules needed to handle External API notifications. We’ll base our code on the previous Excel example.

To start over, create a new Excel document. The VBA code uses early binding, so it’s necessary to add references to the required type libraries. Besides the standard Excel libraries added by default, you need to add references to Shockwave Flash, Microsoft ActiveX Data Objects (ADO), and Micro-soft XML 3.0 or higher. As you will soon see, handling the Flex External API in an ActiveX container is all about XML processing.

Integration with External ApplicationsCHAPTER 15

Page 670: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 675

Figure 15.9 Adding required typelib references for Excel VBA code

Next, define the workbook’s custom property FxChannel: select menu File/Properties to open the Properties dialog, switch to tab Custom, and add the property FxChannel of type String. Set it to _FlexExcelDemo. This property defines the ID of the channel that must be returned in response to the fxQueryChannel notification sent from the Flex agent. The Flex application from Listing 15.16 hard-codes names for listening and sending connections; these names assume the channel with ID _FlexExcelDemo. If you deviate from _FlexExcelDemo, then adjust LISTEN_AGENT and UP-DATE_AGENT in FxExcelSample.mxml. As a convenience, you can get to the workbook properties by right-clicking menu on the workbook file:

Figure 15.10 Adding custom Workbook properties

Integration with External ApplicationsCHAPTER 15

Page 671: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

676 RIA WITH ADOBE FLEX AND JAVA

Now we’re ready to add the necessary Excel VBA modules. Two of them are already familiar to you; we need the ThisWorkbook object module to instantiate the FlexProxy instance in response to the Workbook_Open event and the FlexProxy class module that inserts the Shockwave Flash object and handles the External API notifications. The latest one is a new XmlUtils module that exposes convenient utility functions to work with XML/XSLT.

Let’s start with the ThisWorkbook object module:

‘ File ./FxExcelAdodbRecordset.xls

‘ Object module ThisWorkbook

Private fxProxy As FlexProxy

Private Sub Workbook_Open()

Dim Property As Object

Dim fxChannel As String

For Each Property In CustomDocumentProperties

If Property.Name = “FxChannel” Then

fxChannel = Property.Value

End If

Next Property

If IsEmpty(fxChannel) Or Len(fxChannel) = 0 Then

Dim CRLF As String

CRLF = Chr(13) & Chr(10)

MsgBox “Flex channel property “”FxChannel”” not found.” & CRLF & CRLF & _

“Please open workbook properties and define “ & CRLF & _

“custom property “”FxChannel”” of type “”string””.” & CRLF & _

“Re-open workbook afterwards.”, _

vbCritical + vbOKOnly, “WorkBook Configuration Error”

Exit Sub

End If

Set fxProxy = New FlexProxy

fxProxy.Initialize 1, “fxObject”, fxChannel

End Sub

Listing 15.17 The FxExcelAdodbRecordset workbook module

As you can see, the Workbook_Open event handler checks for the presence of the custom FxChan-nel document property that we just added. Once the check is successfully passed, it creates a Flex-Proxy instance and delegates all the work to this object – as in the Excel example.

‘ File ./FxExcelAdodbRecordset.xls

‘ Class module FlexProxy

Option Explicit

Integration with External ApplicationsCHAPTER 15

Page 672: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 677

Private WithEvents pSheet As Excel.Worksheet

Private WithEvents pFlexObject As ShockwaveFlashObjects.ShockwaveFlash

Private pOleControl As Excel.oleObject

Private pXmlLayout As MSXML2.IXMLDOMElement

Private pFieldsCount As Integer

Private pChannelID As String

Private pTotalRows As Integer

Public Sub Initialize(ByVal sheetID As Variant, ByVal controlID As String, ByVal chan-

nelID As String)

Set pSheet = Worksheets(sheetID)

pChannelID = channelID

Dim oleObject As Excel.oleObject

Dim found As Boolean

found = False

For Each oleObject In pSheet.OLEObjects

If oleObject.Name = controlID Then

found = True

Exit For

End If

Next oleObject

If Not found Then

Set oleObject = pSheet.OLEObjects.Add( _

ClassType:=”ShockwaveFlash.ShockwaveFlash.9”, _

Width:=300, Height:=165, Left:=10, Top:=10 _

)

oleObject.Name = controlID

End If

With oleObject

Call .BringToFront

Call .Activate

End With

Set pOleControl = oleObject

Set pFlexObject = oleObject.Object

With pFlexObject

.Loop = False

.Menu = True

.Playing = False

.EmbedMovie = False

.AllowNetworking = “all”

Integration with External ApplicationsCHAPTER 15

Page 673: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

678 RIA WITH ADOBE FLEX AND JAVA

.AllowScriptAccess = “always”

If Not found Then

.LoadMovie 0, “../bin/FxExcelAgent.swf”

End If

End With

Call PositionOleControl

End Sub

Private Sub pFlexObject_FlashCall(ByVal request As String)

If Not Application.EnableEvents Then Exit Sub

Application.EnableEvents = False

Dim xmlRequest As MSXML2.FreeThreadedDOMDocument, method As String

Set xmlRequest = CreateXmlDocument(request)

method = xmlRequest.selectSingleNode(“/invoke/@name”).Text

If pXmlLayout Is Nothing And method <> “fxMetadata” And method <> “fxQueryChannel”

Then

MsgBox “Protocol exception: call to method “”” & method & “”” before metadata

available”, _

vbCritical + vbOKOnly, “Fx Protocol Error”

GoTo ExitSub

End If

Select Case method

Case “fxQueryChannel”:

pFlexObject.SetReturnValue “<string>” + pChannelID + “</string>”

Case “fxMetadata”:

InitMetadata xmlRequest

pFlexObject.SetReturnValue “<null/>”

Case “fxRowsInserted”:

UpdateRows xmlRequest, True

pFlexObject.SetReturnValue “<null/>”

Case “fxRowsUpdated”:

UpdateRows xmlRequest, False

pFlexObject.SetReturnValue “<null/>”

Case “fxRowsRemoved”:

RemoveRows xmlRequest

pFlexObject.SetReturnValue “<null/>”

End Select

ExitSub:

Set xmlRequest = Nothing

Application.EnableEvents = True

End Sub

Integration with External ApplicationsCHAPTER 15

Page 674: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 679

Private Sub InitMetadata(ByVal xmlRequest)

Dim xmlLayoutDoc As MSXML2.FreeThreadedDOMDocument

Set xmlLayoutDoc = CreateXmlDocument(TransformFlexMetadata(xmlRequest))

Set pXmlLayout = xmlLayoutDoc.documentElement

pFieldsCount = pXmlLayout.selectNodes(“/layout/field”).Length

Set xmlLayoutDoc = Nothing

End Sub

Private Sub UpdateRows(ByVal xmlRequest As MSXML2.FreeThreadedDOMDocument, ByVal isIn-

sert As Boolean)

Dim row As Integer, count As Integer, reset As Boolean

row = CInt(xmlRequest.selectSingleNode(“/invoke/arguments/number[0]”).Text) + 1

count = xmlRequest.selectNodes(“/invoke/arguments/array/property”).Length

Dim newContent As String

newContent = TransformFlexContent(xmlRequest, pXmlLayout)

Dim Target As Excel.range

If isInsert Then

row = row + 1

Dim rows As range

Set rows = pSheet.rows(row)

Dim i As Integer

For i = 1 To count

Call rows.Insert

Next i

Set Target = pSheet.Cells(row, 1)

pTotalRows = pTotalRows + count

Else

If Not xmlRequest.selectSingleNode(“/invoke/arguments/true”) Is Nothing Then

reset = True

pTotalRows = count

Call pSheet.Cells.Clear

End If

Set Target = pSheet.Cells(row, 1)

End If

PopulateRange Target, newContent

Call pSheet.Activate

Dim newSelection As range

If reset Then

Set newSelection = pSheet.UsedRange

Integration with External ApplicationsCHAPTER 15

Page 675: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

680 RIA WITH ADOBE FLEX AND JAVA

Else

Set newSelection = pSheet.range(Target, pSheet.Cells(row + count - 1,

pFieldsCount))

End If

Set Target = Nothing

Call newSelection.CurrentRegion.Columns.AutoFit

Call newSelection.Select

Call PositionOleControl

Set newSelection = Nothing

End Sub

Private Sub RemoveRows(ByVal xmlRequest As MSXML2.FreeThreadedDOMDocument)

Dim row As Integer, count As Integer

row = CInt(xmlRequest.selectSingleNode(“/invoke/arguments/number[0]”).Text) + 1

count = CInt(xmlRequest.selectSingleNode(“/invoke/arguments/number[1]”).Text)

pTotalRows = pTotalRows – count

While count > 0

Call pSheet.rows(row).Delete

count = count - 1

Wend

Call pSheet.Activate

If Not pSheet.UsedRange Is Nothing Then

Dim newSelection As range

Set newSelection = pSheet.range(pSheet.Cells(row, 1), pSheet.Cells(row,

pFieldsCount))

Call newSelection.Select

End If

Call PositionOleControl

End Sub

Private Sub PositionOleControl()

Dim allCells As range

Set allCells = pSheet.Cells(1, 1).CurrentRegion

pOleControl.Top = 10

If allCells Is Nothing Then

pOleControl.Left = 10

Else

pOleControl.Left = allCells.Left + allCells.Width + 10

Integration with External ApplicationsCHAPTER 15

Page 676: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 681

End If

Set allCells = Nothing

End Sub

Private Sub PopulateRange(ByVal Target As range, ByVal newContent As String)

Dim rsContent As ADODB.Recordset

Set rsContent = CreateObject(“ADODB.Recordset”)

rsContent.Open CreateXmlDocument(newContent)

Target.CopyFromRecordset rsContent

Call rsContent.Close

Set rsContent = Nothing

End Sub

Listing 15.18 The FlexProxy class module

The Module FlexProxy should look familiar to you. It’s an extended version of the module we used for passing data to Excel via the clipboard (see Listing 15.14).

The Initialize() method is almost the same. The only difference is the name of the swf plus an ad-ditional channelID parameter that’s saved to the instance variable.

The pFlexObject_FlashCall method got more complex: it handles several events now. Using a help-er function, which we’ll discuss shortly, it parses an XML request to determine the name of the invoked method. Then, depending on the method name, processing is dispatched to the appropri-ate function.

Processing the fxQueryChannel request is trivial: code just returns a value previously saved as a pChannelID instance variable.

Processing fxMetadata, fxUpdateRows, and fxInsertRows requests deserve extra clarification. As you remember, we’re using an Array of Objects to describe the field order, ID, titles, and types. This structure has quite a complex internal XML representation. The same is true for data re-cords passed with fxUpdateRows and fxInsertRows. A naïve approach would be to use the DOM API: traverse the request DOM document, extract the necessary values, and assign them cell-by-cell.

Fortunately, we know better than that. Recall that in the Spreadsheet OWC example we mentioned more efficient ways to copy tabular data into a range of cells. The first option possible with Excel is to put content on the Windows Clipboard and then apply it with the Range.PasteSpecial() method. Although it would be a logical continuation of our example with System.setClipboard(text), it has serious drawbacks in an automated scenario. Come to think of it, we’re hijacking the system clip-

Integration with External ApplicationsCHAPTER 15

Page 677: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

682 RIA WITH ADOBE FLEX AND JAVA

board: whatever the user puts on the clipboard gets wiped out by our automation code. Is there anything to replace the clipboard?

Well, instead of populating the Excel range from the clipboard we can use the Microsoft ADODB.Recordset paired with the Range.CopyFromRecordset(adodbRecordset) method. The wonderful thing about ADODB.Recordset is its ability to load and save records in XML format. We’ll take this route.

In sum, we have a classic task for XSL transformation: get an XML DOM document as input (the request parameter of the FlashCall event) and produce an XML DOM document as output (the content of ADODB.Recordset). The only issue here is that there are two input XML documents: metadata and the data change notification. To overcome this problem we’ll add a metadata docu-ment element as a child to the root of the data change request document.

After performing the XSL transformation, we pass the generated content to the PopulateRange() method. This method creates the ADODB.Recordset with the generated content and then calls Range.CopyFromRecordset() to populate the range.

The last method of the FlexProxy module – fxRemoveRows() – doesn’t deserve much attention here since it closely resembles the corresponding method from the Spreadsheet OWC example.

Now it’s time to look at the custom XmlUtils module (Listing 15.19). It exposes three public func-tions:

• CreateXmlDocument: Creates the instance of the XML DOM document object by the string source provided

• TransformFlexMetadata: Transforms the content of the metadata notification to the XML format used by the FlexProxy class

• TransformFlexContent: Turns the content of the data change notification into the ADODB.Recordset format

‘ File ./ FxExcelAdodbRecordset.xls

‘ Module XmlUtils

Option Explicit

Private xslDataTemplate As MSXML2.xslTemplate

Private xslMetaTemplate As MSXML2.xslTemplate

Private Sub InitXmlUtils()

Dim xlHelperSheet As Worksheet

Set xlHelperSheet = Worksheets(2)

‘Create HTML template

Set xslDataTemplate = CreateObject(“MSXML2.XSLTemplate”)

Set xslDataTemplate.stylesheet = CreateXmlDocument(xlHelperSheet.Cells(1, 1).Value)

Integration with External ApplicationsCHAPTER 15

Page 678: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 683

‘Create META template

Set xslMetaTemplate = CreateObject(“MSXML2.XSLTemplate”)

Set xslMetaTemplate.stylesheet = CreateXmlDocument(xlHelperSheet.Cells(1, 2).Value)

Set xlHelperSheet = Nothing

End Sub

Private Function ExecuteTransformation(ByVal xmlSource As MSXML2.FreeThreadedDOMDocument

, ByVal xslTemplate As MSXML2.xslTemplate)

Dim xslProcessor As MSXML2.IXSLProcessor

Set xslProcessor = xslTemplate.createProcessor

xslProcessor.input = xmlSource

xslProcessor.transform

ExecuteTransformation = xslProcessor.output

Set xslProcessor = Nothing

End Function

Public Function CreateXmlDocument(ByVal source As String) As MSXML2.FreeThreadedDOMDocum

ent

Dim xmlDoc As MSXML2.FreeThreadedDOMDocument

Set xmlDoc = CreateObject(“MSXML2.FreeThreadedDOMDocument”)

xmlDoc.async = False

xmlDoc.loadXML source

Set CreateXmlDocument = xmlDoc

End Function

Public Function TransformFlexMetadata(ByVal fxRequest As MSXML2.FreeThreadedDOMDocument)

As String

If xslMetaTemplate Is Nothing Then

Call InitXmlUtils

End If

TransformFlexMetadata = ExecuteTransformation(fxRequest, xslMetaTemplate)

End Function

Public Function TransformFlexContent(ByVal fxRequest As MSXML2.FreeThreadedDOMDocument,

ByVal layout As MSXML2.IXMLDOMElement) As String

If xslDataTemplate Is Nothing Then

Call InitXmlUtils

End If

fxRequest.documentElement.appendChild layout.CloneNode(True)6

TransformFlexContent = ExecuteTransformation(fxRequest, xslDataTemplate)

End Function

Listing 15.19 The XmlUtils module

Integration with External ApplicationsCHAPTER 15

Page 679: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

684 RIA WITH ADOBE FLEX AND JAVA

Note that TransformFlexMetadata() and TransformFlexContent() use XSL templates. The XSL tem-plate allows the reuse of the Data Transformation Model (DTM) instead of creating and loading it every time a transformation is performed. Importantly, to use the template the source document has to be created as a FreeThreadedDOMDocument. The initialization of the XSL templates is done on-demand in the InitXmlUtils() procedure.

The XSL stylesheets that the templates are based on are stored in the cells on the second worksheet (we renamed it “Helper,” see Figure 15.11): cell “A1” contains the XSL source for transforming data content, cell “B1” is used to hold the metadata XSL template. If you’re using different names or indexes, adjust the InitXmlUtils() procedure correspondingly.

Figure 15.11 The “Helper” worksheet with XSL template sources

The XSL template for transforming metadata is relatively simple. Here we strip out unnecessary elements from the original Flex External API XML format and produce the document that has the root <layout> element with nested <field> definitions.

<?xml version=”1.0” encoding=”UTF-8”?>

<xsl:stylesheet version=”2.0”

Integration with External ApplicationsCHAPTER 15

Page 680: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 685

xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”

exclude-result-prefixes=”xsl”>

<xsl:output method=”xml” encoding=”utf-16” omit-xml-declaration=”no”/>

<xsl:template match=”/invoke/arguments/array”>

<layout><xsl:apply-templates select=”property/object” /></layout>

</xsl:template>

<xsl:template match=”object”>

<field><xsl:apply-templates select=”property”/></field>

</xsl:template>

<xsl:template match=”property”>

<xsl:attribute name=”{@id}”><xsl:apply-templates/></xsl:attribute>

</xsl:template>

<xsl:template match=”string”><xsl:value-of select=”.”/></xsl:template>

</xsl:stylesheet>

Listing 15.20 XSL template for metadata

As an illustration, consider the xMetadata request coming from the FxExcelSample application:

<invoke name=”fxMetadata” returntype=”xml”>

<arguments>

<array>

<property id=”0”>

<object>

<property id=”type”><string>string</string></property>

<property id=”name”><string>name</string></property>

<property id=”title”><string>Full name</string></property>

</object>

</property>

<property id=”1”>

<object>

<property id=”type”><string>date</string></property>

<property id=”name”><string>birthday</string></property>

<property id=”title”><string>Birthday</string></property>

...

<object>

<property id=”type”><string>number</string></property>

<property id=”name”><string>salary</string></property>

<property id=”title”><string>Salary</string></property>

</object>

Integration with External ApplicationsCHAPTER 15

Page 681: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

686 RIA WITH ADOBE FLEX AND JAVA

</property>

</array>

</arguments>

</invoke>

The transformation of this document with the above XSL results in the following XML:

<?xml version=”1.0” encoding=”UTF-16”?>

<layout>

<field title=”Full name” type=”string” name=”name”></field>

<field title=”Birthday” type=”date” name=”birthday”></field>

...

<field title=”Salary” type=”number” name=”salary”></field>

</layout>

By the way, it’s this very document that is used by the InitMetadata() method of FlexProxy. In partic-ular, we’ve used selectNodes(“/layout/field”).Length to determine the number of fields (columns).

As mentioned earlier, we modify fxRowsUpdated() and fxRowsInserted() requests, adding <layout> as a child node of the document element. This lets us easily apply metadata in the second XSL template that transforms the content of the data change nodification from the External API to the ADODB.Recordset format:

<?xml version=”1.0” encoding=”UTF-8”?>

<xsl:stylesheet version=”2.0”

xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”

xmlns:msxsl=”urn:schemas-microsoft-com:xslt”

xmlns:js=”urn:ecma-script:jscript”

xmlns:s=”uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882”

xmlns:dt=”uuid:C2F41010-65B3-11d1-A29F-00AA00C14882”

xmlns:rs=”urn:schemas-microsoft-com:rowset”

xmlns:z=”#RowsetSchema”

exclude-result-prefixes=”xsl msxsl js”>

<xsl:output encoding=”utf-16” method=”xml” omit-xml-declaration=”no”

standalone=”yes”

normalization-form=”fully-normalized” indent=”yes”/>

<!-- Exclude anything but content array from consideration -->

<xsl:template match=”/invoke/arguments/*[name() != ‘array’]”/>

<xsl:template match=”/invoke”>

<xml>

<xsl:apply-templates select=”layout”/>

<xsl:apply-templates select=”arguments/array”/>

Integration with External ApplicationsCHAPTER 15

Page 682: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 687

</xml>

</xsl:template>

<xsl:template match=”layout”>

<s:Schema id=”RowsetSchema”>

<s:ElementType name=”row” content=”eltOnly” rs:CommandTimeout=”0” rs:

updatable=”false”

rs:ReshapeName=”FxRowset”>

<xsl:apply-templates select=”field”/>

<s:extends type=”rs:rowbase”/>

</s:ElementType>

</s:Schema>

</xsl:template>

<xsl:template match=”field”>

<s:AttributeType name=”{@name}” rs:number=”{position()}” rs:

writeunknown=”true”

rs:basecatalog=”FxRowsetCatalog”

rs:basetable=”FxRowsetTable” rs:basecolumn=”{@name}”>

<s:datatype dt:type=”{@type}” rs:maxLength=”255” rs:long=”false” rs:

maybenull=”true”/>

</s:AttributeType>

</xsl:template>

<xsl:template match=”array”>

<rs:data><xsl:apply-templates select=”property/object”/></rs:data>

</xsl:template>

<xsl:template match=”object”>

<xsl:variable name=”row”>

<z:row><xsl:apply-templates select=”property”/></z:row>

</xsl:variable>

<xsl:copy-of select=”$row”/>

</xsl:template>

<xsl:template match=”property”>

<xsl:attribute name=”{@id}”><xsl:apply-templates/></xsl:attribute>

</xsl:template>

<xsl:template match=”string | number”><xsl:value-of select=”.”/></xsl:template>

<xsl:template match=”date”>

<xsl:variable name=”prop” select=”parent::node()/@id”/>

<xsl:variable name=”typeOf” select=”/invoke/layout/field[@name=$prop]/@type”/>

<xsl:choose>

Integration with External ApplicationsCHAPTER 15

Page 683: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

688 RIA WITH ADOBE FLEX AND JAVA

<xsl:when test=”$typeOf=’date’”><xsl:value-of select=”js:date( number(.)

)”/></xsl:when>

<xsl:when test=”$typeOf=’time’”><xsl:value-of select=”js:time( number(.)

)”/></xsl:when>

<xsl:otherwise><xsl:value-of select=”js:dateTime( number(.) )”/></xsl:

otherwise>

</xsl:choose>

</xsl:template>

<xsl:template match=”true”>true</xsl:template>

<xsl:template match=”false”>false</xsl:template>

<msxsl:script language=”JScript” implements-prefix=”js”>

<![CDATA[

function dateTime(v) {

var dt = new Date( v );

return [dt.getFullYear(), __(dt.getMonth()+1), __(dt.getDate())].join(‘-’)

+

“ “ + [__(dt.getHours()), __(dt.getMinutes()), __(dt.getSeconds())].

join(‘:’);

}

function time(v) {

var dt = new Date( v );

return [__(dt.getHours()), __(dt.getMinutes()), __(dt.getSeconds())].

join(‘:’);

}

function date(v) {

var dt = new Date( v );

return [dt.getFullYear(), __(dt.getMonth()+1), __(dt.getDate())].join(‘-

’);

}

function __(v) { return v < 10 ? “0” + v : v; }

]]>

</msxsl:script>

</xsl:stylesheet>

Listing 15.21 The XSL template for data

The ADODB.Recordset format requires recordset schema meta-information. Having rich metadata available as a <layout> element is a trifling task – we merely transform the <field> elements of the original document into the <rs:AttributeType> of the rowset schema.

To transform data entries the template first finds the /invoke/arguments/array node and produces <rs:Data>. From there the template cascades to generate rows for every array element.

Integration with External ApplicationsCHAPTER 15

Page 684: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 689

Next, for every object property the XSL template creates a corresponding attribute in the <z:row/> element, applying the necessary type formatting. XSLT is a functional language, and here its nature simply shines: we just declare rules for processing special types and leave it up to the transforma-tion to automatically pick up a rule depending on the property type!

You can draw parallels with the examples already seen: strings and numbers go as is, Boolean type requires extra effort to convert a node name to node text, and any Date-based type is a problem! Through all the examples we have used several sub-types of date: date-only, time, and timestamp (or full date). However, in ActionScript all we have is Date, and in the External API it’s encoded as a <date> element with the number of milliseconds since “epoch.” To format the date values, we resort to an excellent feature of XSLT 2.0 – the extension functions mechanism. You have to declare your own namespace (urn:ecma-script:jscript) and implement functions in this namespace in a vendor-specific way. The Microsoft-specific way is an attractive one: you declare a script block in JScript and specify that global functions in this block implement an extension function in a certain namespace. Simple and elegant. Conveniently, we just reuse the formatting functions from the Spreadsheet OWC example.

You may notice that the transformation doesn’t add a z:row element directly to the result XML tree, but does create a variable first and then outputs the variable. This trick is necessary to create an empty XML element, i.e., <z:row/> instead of an element with no content <z:row></z:row>. Other-wise ADODB.Recordset fails to read anything except the very first row7.

We’re done preparing. Let’s party! To run the example you have to have the FxExcelSample ap-plication running and the FxExcelAdodbRecordset.xls workbook open – in any order. Figure 15.12 shows the Flex application partially overlapping the workbook. You can modify the Flex data and the Excel workbook will be updated instantly. As you can tell, we purposely let the FxExcelAgent hang in the visible portion of the workbook for purposes of illustration.

Integration with External ApplicationsCHAPTER 15

Page 685: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

690 RIA WITH ADOBE FLEX AND JAVA

Figure 15.12 The Flex/Excel application running

Congratulations, everyone! We’ve got something to write home about.

Getting in the Nitpicking MoodAs you probably noticed, the communication between Flex and Excel applications in our latest example are one-way – the changes in an Excel spreadsheet aren’t propagated back to the Flex data source application. The reason is an unfortunate bug that shows when Shockwave Flash ActiveX is embedded in Excel and manifests in a call to the ShockwaveFlash.CallFunction, failing with a cryptic COM exception.

We sincerely hope that Adobe will have this bug fixed by the time you read these lines. In List-ing 15.22 you can find the code necessary for the Worksheet_Change event handler that traverses modified cells and constructs the request XML in the External API format. In fact, the code in List-ing 15.22 closely resembles the corresponding event handler in the Spreadsheet OWC example; the only difference is that XML elements are created instead of JavaScript objects. The code should be added to the FlexProxy class module.

Integration with External ApplicationsCHAPTER 15

Page 686: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 691

Private Sub pSheet_Change(ByVal Target As Range)

Application.EnableEvents = False

If pXmlLayout Is Nothing Or pTotalRows < 1 Then

GoTo ExitSub

End If

Dim fields As MSXML2.IXMLDOMNodeList

Set fields = pXmlLayout.selectNodes(“/layout/field”)

If Target.row > pTotalRows Or Target.Column > fields.Length Then

GoTo ExitSub

End If

Dim rowCount As Integer, colCount As Integer

rowCount = Min(Target.rows.count, pTotalRows - Target.row + 1)

colCount = Min(Target.Columns.count, fields.Length - Target.Column + 1)

Dim xmlRequest As MSXML2.DOMDocument

Set xmlRequest = CreateXmlDocument( _

“<invoke name=””importRows”” returntype=””xml””>” & _

“<arguments><number/><array/><false/></arguments></invoke>” _

)

‘Offset parameter

Dim paramOffset As MSXML2.IXMLDOMElement

Set paramOffset = xmlRequest.selectSingleNode(“/invoke/arguments/number”)

paramOffset.appendChild xmlRequest.createTextNode(Target.row - 1)

Dim asArray As MSXML2.IXMLDOMElement

Set asArray = xmlRequest.selectSingleNode(“/invoke/arguments/array”)

Dim row As Integer, col As Integer

For row = 1 To rowCount

Dim asIdx As MSXML2.IXMLDOMElement

Set asIdx = xmlRequest.createElement(“property”)

asIdx.setAttribute “id”, row - 1

Dim asObj As MSXML2.IXMLDOMElement

Set asObj = xmlRequest.createElement(“object”)

For col = 1 To colCount

Dim fldProps As MSXML2.IXMLDOMNamedNodeMap

Integration with External ApplicationsCHAPTER 15

Page 687: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

692 RIA WITH ADOBE FLEX AND JAVA

Set fldProps = fields(col + Target.Column - 2).Attributes

Dim asProp As MSXML2.IXMLDOMElement

Set asProp = xmlRequest.createElement(“property”)

asProp.setAttribute “id”, fldProps.getNamedItem(“name”).Text

Dim value As MSXML2.IXMLDOMElement

Dim formula As String

formula = Target.Cells(row, col).formula

If Len(formula) = 0 Then

Set value = xmlRequest.createElement(“null”)

Else

Dim xlValue As Variant

xlValue = Target.Cells(row, col).value

Select Case fldProps.getNamedItem(“type”).Text

Case “boolean”:

If xlValue = False Or xlValue = True Then

Set value = xmlRequest.createElement( _

IIf(CBool(xlValue), “true”, “false”) _

)

End If

Case “number”:

Set value = xmlRequest.createElement(“number”)

If IsNumeric(xlValue) Then

value.appendChild xmlRequest.createTextNode(formula)

End If

Case “string”:

Set value = xmlRequest.createElement(“string”)

value.appendChild xmlRequest.createTextNode(xlValue)

Case Else

Set value = xmlRequest.createElement(“date”)

If IsDate(xlValue) Then

value.appendChild xmlRequest.createTextNode( _

DateDiff(“s”, #1/1/1970#, xlValue) * 1000 _

)

End If

End Select

End If

asProp.appendChild value

asObj.appendChild asProp

Next col

asIdx.appendChild asObj

Integration with External ApplicationsCHAPTER 15

Page 688: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 693

asArray.appendChild asIdx

Next row

‘MsgBox xmlRequest.XML

pFlexObject.CallFunction xmlRequest.XML

ExitSub:

Application.EnableEvents = True

End Sub

Listing 15.22 The Worksheet_Change event handler

What if the bug still persists and you need bidirectional communications? Well, one option is to replace the asynchronous call to CallFunction with polling. Briefly, it could be implemented as described below:

• The Worksheet_Change event handler should be modified to create an object that describes the modification in the External API format (refer to Table 15.2 for details). Instead of <in-vocation> you must create <object> as a root element and then add both asArray and offset as <property> sub-nodes; assign the result to a new FlexProxy instance field, for example, pLastChange of type MSXML2.DOMDocument. Obviously, the CallFunction invocation must be removed from the code.

• The pFlexObject_FlashCall method (see Listing 15.18) should be modified to handle addition-al notification, say, fxQueryChanges. In response to this notification, XML content previously saved to the pLastChange field must be returned with the ShockwaveFlash.SetReturnValue; field cleared afterwards.

• FxExcelAgent (see Listing 15.15) should set up an instance of a flash.utils.Timer to periodi-cally invoke ExternalInterface.call(“fxQueryChanges”). If the result of the External API call isn’t empty, then offset and content must be extracted from the returned object and passed to the xlImportRows method of FxExcelAgent.

Another issue you might find annoying is that when closing the spreadsheet, the user is prompted to save the workbook even when a single cell hasn’t been altered. The cause is the automation code that updates cells immediately after opening the workbook (if the Flex data source application is running). Actually, there’s no single recipe here: every possible option should be validated against the particular use case. For some applications you can invoke the Workbook.Save method to persist changes automatically; for others it’s possible to set the Workbook.Saved property to True and skip confirmations along with saving; for the rest the only valid option is to leave the behavior as is.

SummaryThis chapter took you through the jungles of Flash technologies such as the External API and Local-Connection as well as through the maze of Microsoft old-timers like ADODB and OWC. We’ve been

Integration with External ApplicationsCHAPTER 15

Page 689: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

694 RIA WITH ADOBE FLEX AND JAVA

coding in VBA, JavaScript, XSLT, not to mention ActionScript.

Frankly, it’s only the length of this chapter that prevents us from diving into C++ DLLs or showing you integrating with Excel Pivot tables.

Tough? Well, you didn’t think system integrators earn their money for nothing, did you? But let’s look at the end result. We presented you with a set of generic, parameterized, reusable solutions:

• Two-way integration of Flex and the Office Windows Component (OWC) Spreadsheet embed-ded in the same HTML page. As a reminder, we only left out Pivot Table integration because of space limitations.

• The interactive export of Flex content to an Excel application via the clipboard.

• A live-link between a standalone Flex application and an Excel worksheet (the latter has to be templated by our sample one).

If you find that we’ve paid too much attention to our code, our response is – guilty as charged. But we’ve transferred every line of our solution to you, so that you can take them to users with no black holes.

Integration with External ApplicationsCHAPTER 15

Page 690: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 695

Integration with External ApplicationsCHAPTER 15

Endnotes

1. Experiments show that this is not the only option with the current ExternalInterface implementation.

You can invoke a method of the JavaScript object from ActionScript in the form of ExternalInterface.

call(“jsObjectA.jsObjectB.methodOfB”, arg1, arg2, …, argN). Apparently ExternalInterface invokes JavaS-

cript code via the eval function rather than via Function.apply. However, this is an undocumented feature

and its behavior may be changed in future releases.

2. It’s technically possible to have several versions of Microsoft Office Web Components side by side. A typi-

cal cause of this is upgrading Microsoft Office.

3. Spreadsheet OWC would be simply genius if it had explicit support for the JavaScript Date type. The cur-

rent version converts Date to a UTC string before the assignment and applies string formating to the cell

afterwards.

4. As noted, the Spreadsheet OWC 9 API is very limited. Most important, there are no events to track content

changes. So the rest of the code is applicable to Spreadsheet OWC 10/11 only.

5. “JavaScript” is used internally by CallFunction for the HTML container return type. This forces the result

to be a JSON-formated string that’s applicable to the eval call.

6. Frequently, Microsoft is a standard on its own. W3C defines the importNode method on the Document

interface that must be called to get a copy of the XML node from the other document before adding it.

7. W3C’s XML Specification Edition 3 clarifies this issue: if an element is declared in DTD as EMPTY, then

the <tag/> form SHOULD be used (see http://www.w3.org/TR/REC-xml/#dt-empty). So the behavior of

the ADODB.Recordset XML parser is partly correct. To be absolutely correct it’s necessary to report that

the XML document doesn’t pass DTD/Schema validation rather than return partly parsed content.

Page 691: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 697

INDEX

INDEX

Symbols

-debug 438

-link-report 438

-load-externs 438

.flexLibProperties 419, 439

AAbstractAssembler 212

ActiveMQ xii, 189, 190, 195, 197

ActiveSheet 630

ActiveWorkbook 630

ActiveX xvii, 47, 604, 612, 613, 614, 621, 622, 624,

625, 626, 629, 630, 638, 649, 650, 654, 655, 656,

674, 690

ActiveX Data Objects (ADO 674

addChild() 45, 46, 389, 392, 469, 578

ADODB.Recordset 634, 681, 682, 686, 688, 689, 695

AJAX ix, xxi, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15

Alert 35, 87, 88, 90, 113, 114, 120, 164, 182, 185, 188,

201, 208, 210, 211, 256, 257, 258, 281, 283, 305,

307, 313, 314, 330, 331, 338, 351, 352, 379, 381,

382, 385, 443, 444, 513, 515, 529, 531, 606, 608,

643, 644, 668, 669, 672, 673

allowScriptAccess 607, 610, 621

anonymous function 83, 84, 87, 165, 451, 470, 606,

625

ANT 246, 413

Apollo ix, 5, 12, 13

applet 7, 11

application xxii, xxiii, xxiv, 2, 3, 4, 5, 8, 9, 10, 11, 12,

13, 15, 20, 21, 23, 25, 28, 29, 30, 34, 36, 44, 45, 46,

47, 50, 51, 52, 53, 54, 56, 57, 58, 59, 61, 62, 63, 64,

66, 67, 68, 69, 70, 71, 73, 74, 80, 83, 84, 87, 91, 92,

93, 98, 99, 111, 115, 116, 117, 118, 119, 122, 126,

127, 129, 130, 131, 134, 138, 141, 142, 143, 144,

145, 146, 147, 148, 149, 150, 152, 153, 156, 158,

159, 160, 161, 168, 180, 181, 183, 184, 186, 189,

192, 193, 195, 196, 197, 199, 207, 208, 209, 211,

213, 242, 243, 244, 245, 248, 252, 254, 255, 259,

262, 272, 273, 284, 291, 294, 297, 298, 299, 300,

301, 302, 303, 304, 313, 315, 317, 321, 324, 325,

326, 330, 332, 333, 335, 336, 339, 340, 348, 349,

354, 359, 361, 362, 372, 374, 375, 379, 381, 382,

385, 386, 388, 396, 400-403, 405, 407-413, 415, 416,

Page 692: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

698 RIA WITH ADOBE FLEX AND JAVA

INDEXINDEX

418-424, 426-428, 430, 431, 433, 436-439, 442, 444,

447, 448, 450, 452, 453, 456, 457, 459, 462, 464-467,

473, 475, 477, 485, 486, 489- 491, 497, 498,

500-505, 508, 516, 521, 522, 526, 527, 528, 530, 532,

534, 535, 537, 539, 540, 541, 544-548, 550, 559-563,

565, 566, 567, 569, 570, 574, 578, 579, 580, 585,

591, 595, 601, 604-607, 609-615, 620, 621, 626,

628-630, 633, 635, 636, 642, 643, 648, 649, 650, 654,

655, 658, 659, 660, 661, 662, 663, 664, 668, 674,

675, 685, 689, 690, 693, 694

applicationDomain 401, 403, 404, 424, 425

application domain 362, 401, 413, 421

application domains 45, 362, 401, 431

application project 402, 416, 448

argument 41, 69, 91, 112, 144, 171, 207, 217, 332,

334, 346, 372, 379, 499, 500, 508, 510, 577, 605,

606, 609, 612, 633, 655

ArgumentError 531, 659, 661, 664, 669

ArrayCollection 134, 138, 209, 210, 253, 255, 257,

259, 262, 277, 280, 281, 283, 284, 315, 321, 322,

324, 330, 331, 338, 339, 372, 491, 504, 505, 548,

549, 556, 615, 616, 617, 643, 646, 663, 668, 669, 671

asdoc 21

Asynchronous Completion Token (ACT) 377

as casting 329, 362

attachEvent 638

autoCommit 208, 209, 210

autocomplete 312, 339, 340, 341, 342, 343, 346,

347, 349

automation xxi, 439, 498, 503, 515, 614, 649, 682, 693

automation interface 614

autoSyncEnabled 208, 209, 210

Auto extract 404, 407, 408, 420, 428, 429

AVM 37, 110, 138

BbackgroundAlpha 479

backgroundColor 24, 128, 130, 134, 148, 151, 209,

428, 433, 436, 479, 480, 486, 545, 556, 560, 580

BatchGateway 289, 291, 292, 293, 294, 300

BatchMember xiv, 288, 290, 292, 293, 295, 296

batchRegisteredCollections() 294, 295, 304, 306

BatchService xiv, 293, 294, 295, 297, 304, 305,

306, 308

BeforeContextMenu 641

Boolean 38, 39, 67, 101, 104, 130, 136, 138, 145, 231,

257, 267, 269, 271, 279, 282, 296, 305, 306, 315,

316, 319, 336, 338, 342, 349, 350, 357, 360, 369,

394, 396, 443, 457, 488, 493, 511, 514, 529, 548,

556, 596, 605, 609, 610, 611, 612, 615, 617, 618,

620, 633, 646, 649, 652, 657, 663, 664, 665, 666,

669, 671, 672, 677, 679, 689

bubbling 139, 140, 141, 144, 145, 150, 151

build.xml 25, 26, 243, 245, 246, 247, 248, 413,

414, 417, 418, 419, 423, 500, 501, 541

ButtonBar 29, 33, 63, 172, 516

ByRef 640, 641

bytecode 4, 5, 6, 20, 34, 37

Ccallback 36, 318, 370, 445, 577, 578, 587, 606, 607,

612, 662

CallFrame 604

CallLabel 604

Canvas 29, 33, 63, 128, 134, 135, 171, 172, 173, 175,

176, 178, 187, 188, 200, 202, 272, 273, 276, 277,

545, 546, 548, 549, 552, 554, 555, 556, 559

capture 139, 140, 144, 145

Cell 633

ChangeObject 212, 217, 218, 219, 220, 233, 234, 235,

236, 237, 240, 241, 266, 267, 268, 269, 270, 271,

272, 274, 278, 280, 281, 282

change event 133, 186, 187, 340, 341, 343, 470, 556,

564, 565

Charting xii, 5, 167, 574, 575

ChartItemEvent 188

CheckBox xv, 29, 33, 65, 128, 131, 134, 388, 389, 390,

391, 393, 395, 457, 458, 459, 460, 461, 462, 463,

464, 465, 466, 467, 468, 500, 505, 506, 515, 516

Checkboxed Tree 386

CheckBoxes 442, 463

child domain 401, 430

ClassFactory 459, 481, 482, 483, 484, 494, 495, 496,

497, 499, 508, 509, 510, 511, 512, 513, 516

classpath 30, 162, 189, 197, 244, 246

class definitions 105, 362, 401, 403, 463, 502

clipboard 634, 637, 643, 648, 649, 656, 681, 682, 694

closure 41, 86, 87, 88, 89, 90, 91, 451, 606, 609, 612

Page 693: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 699

INDEXINDEX

CollectionEvent 259, 260, 262, 269, 270, 308, 464,

470, 472

com.theriabook.collections.DataCollection 254, 295,

302, 304, 305

com.theriabook.controls.CheckBox 458, 459, 460,

461, 462, 466, 467, 500, 505

com.theriabook.controls.dataGridClasses. 500

com.theriabook.controls.dataGridClasses.

DataGridColumn 448, 496, 500, 501, 512, 513, 514

com.theriabook.controls.Label 481, 500, 505, 513

com.theriabook.DAOFlex.DAOException 216, 223

com.theriabook.datasource 210, 211, 213, 214, 221,

223, 224, 226, 227, 232, 253, 260, 261, 263, 264,

265, 277, 284, 299, 300, 305

com.theriabook.remoting.BatchGateway 292, 300

com.theriabook.remoting.BatchMember 292, 293,

295

ComboBoxes 332, 442

CommandBeforeExecute 641

commitProperties() 315, 316, 317, 320, 337, 350,

458, 465

commitRequired 279, 280, 282, 284, 297, 305, 306

compc 47, 402, 405, 448

compilation errors 24

compiler options 24, 490

component manifest file 447, 487

concurrency 161, 166, 169, 173, 175, 180, 182, 185,

256, 258, 296, 329, 331, 334, 338, 348, 352, 377,

381, 382, 383, 443, 514

consumer 3, 192, 199, 201, 202

ControlBar 33, 209, 210, 260, 272, 276, 284, 297, 301,

302, 303, 560, 561, 564, 565, 569

createChildren() 46, 345, 351, 388, 390, 393, 395, 584,

586, 591, 600

createItem() 212

createUpdateEvent() 262, 264

creationComplete 46, 87, 88, 89, 114, 138, 141, 143,

148, 149, 151, 165, 166, 169, 172, 176, 182, 184,

200, 209, 254, 259, 275, 284, 301, 303, 313, 330,

332, 335, 336, 370, 379, 382, 404, 435, 443, 445,

450, 452, 456, 460, 465, 467, 475, 478, 485, 491,

497, 501, 505, 524, 526, 527, 607, 608, 615, 643,

663, 668

crossdomain.xml 117, 181

CSS 12, 44, 70, 324, 413

CSSStyleDeclaration 359, 360, 447

CurrencyFormatter 127, 128, 135, 451, 452, 453, 455

currentDomain 401, 402, 403, 410, 420, 424, 425

Custom events 147

DDAOException 216, 217, 220, 223, 225, 226, 228, 234,

235, 240, 241, 286, 326, 327, 344

DAOFlex xiii, 207, 211, 212, 215, 216, 221, 222, 223,

224, 229, 230, 232, 233, 242, 243, 244, 245, 246,

247, 248, 249, 265, 299, 308

DAOFlex-runtime.properties 244

DAOFlex code generator: 247

Data-Driven Programming xvi, 498

DataCollection 254, 285, 287, 288, 294, 295, 296, 302,

304, 305, 306, 366, 381, 382

dataDescriptor xiv, 369, 371, 373, 380, 384

dataField xiv, 116, 119, 124, 128, 135, 159, 166, 169,

173, 176, 182, 183, 184, 200, 210, 254, 259, 273,

275, 276, 277, 301, 303, 313, 314, 318, 319, 320,

321, 324, 331, 332, 335, 336, 337, 348, 349, 353,

354, 356, 357, 360, 423, 444, 445, 446, 447, 450,

452, 453, 456, 458 -467, 470, 472, 476, 477, 478,

479, 480, 485, 487, 489, 492, 493, 498, 501, 507,

531, 532, 619, 647, 673

DataGrid xii, xv, xvi, 33, 94, 116, 117, 119, 120, 122,

124, 125, 127, 128, 130, 133, 135, 159, 166, 168,

169, 171, 172, 173, 176, 181, 182, 184, 187, 188,

200, 206, 207, 209, 210, 254, 259, 273, 275, 276,

277, 300, 301, 302, 303, 322, 385, 401, 422, 423,

426, 427, 441, 442, 443, 444, 445, 447, 448, 449,

450, 451, 452, 453, 455, 456, 457, 458, 459, 460,

461, 463, 464, 465, 466, 467, 469, 470, 471, 472,

473, 475, 476, 477, 478, 479, 481, 483, 485, 486,

487, 489, 491, 492, 493, 494, 495, 497, 498, 499,

500, 501, 502, 503, 504, 505, 507, 508, 509, 511,

512, 513, 515, 516, 517, 530, 531, 532, 614, 619,

647, 673

DataGridColumn xv, 116, 119, 124, 128, 135, 136,

159, 166, 169, 173, 176, 182, 183, 184, 200, 210,

254, 259, 273, 275, 276, 277, 301, 303, 423, 442,

444, 445, 446, 447, 448, 449, 450, 451, 452, 453,

454, 455, 456, 457, 458, 459, 460, 461, 462, 466,

Page 694: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

700 RIA WITH ADOBE FLEX AND JAVA

467, 468, 476, 477, 478, 479, 480, 481, 483, 484,

485, 486, 492, 494, 495, 496, 498, 499, 500, 501,

502, 503, 505, 507, 509, 510, 511, 512, 513, 514,

515, 531, 532, 619, 647, 673

DataGridListData 458, 463, 464, 465, 470, 471, 472,

487, 489, 493

dataProvider 116, 119, 122, 124, 125, 127, 128, 130,

133, 134, 135, 159, 166, 168, 169, 171, 172, 173,

176, 178, 181, 182, 184, 185, 188, 200, 201, 209,

210, 254, 255, 259, 273, 274, 275, 278, 300, 301,

302, 303, 312, 313, 314, 315, 316, 317, 318, 319,

320, 321, 323, 324, 331, 335, 337, 338, 340, 341,

342, 343, 346, 347, 349, 350, 351, 352, 366, 367,

368, 369, 371, 372, 373, 379, 380, 383, 384, 394,

422, 426, 427, 444, 462, 464, 465, 470, 472, 477,

485, 492, 499, 502, 504, 507, 515, 516, 531, 532,

553, 554, 559, 562, 564, 565, 568, 569, 579, 581,

585, 589, 590, 591, 592, 593, 594, 598, 600, 619,

620, 647, 648, 668, 673, 674

DataService 208, 209, 236, 252, 253, 254, 266, 308,

370, 521

Data Access Object (DAO) 207, 376, 378

data adapters 206

Data Binding xi, xv, 92, 99, 122, 393

data collaboration 206

data synchronization xxiv, 252, 613

Date x, 34, 38, 39, 67, 182, 184, 210, 211, 212, 213,

214, 215, 216, 221, 222, 224, 226, 229, 231, 233,

238, 254, 255, 260, 277, 284, 301, 307, 506, 529,

531, 532, 580, 581, 585, 586, 592, 593, 594, 597,

598, 599, 605, 608, 609, 610, 611, 617, 618, 631,

632, 633, 644, 645, 646, 657, 671, 672, 688, 689, 695

debugger xxiii, 5, 8, 21, 66, 67, 68, 427, 520, 534, 536,

540

debugging xxiv, 5, 8, 21, 66, 67, 68, 242, 438, 516, 520,

522, 523, 530, 532, 534, 539, 541

decimalSeparator 489, 490

DefaultDataDescriptor 368, 369

DELETE 212, 219, 232, 237, 266, 267, 280, 282

deletedCount 267, 268, 269, 270, 271, 272, 276, 279,

280, 282, 284, 302, 303

deleteItem() 212

deployment 2, 5, 6, 11, 14, 25, 245, 246, 400, 407, 408,

413, 416, 418

destination 92, 94, 95, 97, 122, 160, 161, 162, 164,

165, 166, 169, 170, 173, 174, 175, 177, 180, 181,

182, 185, 189, 191, 192, 193, 194, 195, 199, 201,

206, 207, 208, 209, 211, 212, 213, 214, 245, 246,

248, 252, 253, 254, 255, 256, 257, 258, 259, 260,

266, 277, 281, 283, 284, 287, 288, 291, 292, 293,

294, 295, 296, 299, 300, 306, 312, 325, 326, 329,

331, 332, 333, 334, 335, 336, 338, 339, 343, 346,

348, 349, 350, 351, 352, 353, 354, 356, 357, 360,

362, 366, 378, 381, 382, 383, 387, 413, 442, 443,

444, 445, 450, 452, 456, 460, 465, 467, 475, 478,

485, 497, 514, 515, 525, 526, 527, 529

destination-aware collection 254, 259

Destination-Aware Collections xiii, 253

destination-aware ComboBox 336, 339, 349, 352,

366, 382

DHTML 4, 8, 10, 12, 58

Dispatch interface 614

DisplayList 578

DisplayObject 45, 138, 409, 435, 468, 469, 471, 513,

514, 554

doCreate() xiii, 238, 240, 241

doDelete() 235, 238

doInsert() 235

DOM Level 0 638

DOM Level 2 638

doUpdate() 235, 236, 237

Drop-In Renderer xv, 457, 467

dropdownWidth 324, 331, 332, 335, 336, 348, 353,

356, 357, 360

DTD 196, 695

durable subscriber 192

Dynamic classes 41

dynamic linking 404, 409

EE4X xi, 37, 117, 122, 124, 126, 132, 138, 164, 181, 367,

503, 563, 570

E4X XML 367

ECMAScript xxii, 4, 33, 37, 41, 80, 87, 126, 632

EditingManager 494, 495, 496, 497, 500, 512

editorDataField 301, 467, 492, 495, 497

EMBED 58, 607, 611, 628

event.hitData.item 187, 188, 200

INDEXINDEX

Page 695: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 701

EventDispatcher 45, 85, 138, 139, 263, 264, 295, 305

event listener 34, 90, 138, 139, 149, 334, 373, 382, 481

Excel xvii, 604, 613, 620, 625, 627, 630, 632, 633, 636,

639, 642, 643, 649, 650, 651, 652, 653, 654, 656,

658, 662, 663, 668, 674, 675, 676, 677, 679, 681,

682, 689, 690, 694

executeBatch( 291

executeTransactionBatch() 291, 292

expando 504, 506, 638

explodeRadius 168, 169, 173, 176, 200

extendedProperties 459, 460, 461, 462, 466, 484,

494, 511

External xvii, 403, 404, 603, 604, 605, 606, 607, 608,

609, 611, 612, 613, 615, 617, 619, 621, 623, 625,

627, 629, 631, 633, 635, 637, 639, 641, 643, 645,

647, 649, 651, 653, 654, 655, 657, 658, 659, 661,

662, 663, 665, 667, 668, 669, 671, 673, 674, 675,

676, 677, 679, 681, 683, 684, 685, 686, 687, 689,

690, 691, 693, 695

ExternalInterface 47, 430, 605, 606, 607, 608, 611,

612, 613, 614, 615, 617, 620, 644, 655, 658, 663,

664, 665, 666, 693, 695

ExternalInterface.call() 655

FFaçade xiv, 378, 379

FaultEvent 84, 164, 166, 170, 174, 177, 182, 185, 188,

199, 255, 256, 257, 258, 280, 281, 283, 289, 294,

295, 296, 304, 305, 306, 331, 334, 335, 338, 351,

352, 380, 384, 385, 443, 444, 514, 525

FDS xiii, 4, 5, 115, 122, 156, 161, 181, 193, 206, 207,

217, 266, 287, 307, 308, 520

fill() 209, 212, 253, 254, 255, 256, 281, 304, 306, 332,

333, 334, 335, 336, 346, 348, 382, 386, 443, 445,

450, 452, 456, 460, 465, 467, 475, 478, 485, 497

fill-method 213, 215, 221, 224, 226, 228, 232

Filtering xii, 130

flash.display.Loader 401, 424, 425, 548, 556

flash.events.EventDispatcher 263, 295, 305

flash.net.navigateToURL 183

flashObject.CallFunction 655

flashObject.SetReturnValue 655

Flash Player xxiii, 2, 4, 5, 6, 7, 10, 12, 13, 14, 20, 21, 22,

23, 24, 27, 31, 32, 33, 34, 37, 42, 45, 46, 47, 56, 57,

62, 66, 92, 115, 138, 139, 140, 144, 149, 183, 253,

328, 362, 401, 516, 520, 575, 604, 605, 606, 607,

611, 613, 620, 629, 650, 654, 655, 674

flex.data.assemblers.AbstractAssembler 212

flex.data.ChangeObject 212, 217, 266

Flex2Ant 439

flexibility xxiii, 62, 83, 89, 308, 313, 321, 367, 400, 416,

575, 613

Flex Data Services xiii, xxiv, 4, 5, 6, 21, 30, 47, 69, 115,

156, 161, 189, 206, 252, 266, 288, 307, 308, 521,

534, 541

Flex Library project 448

Flex Remoting xiii, 206, 252, 253, 266, 285, 287,

307, 525

FOR/EVENT 638

formatData 453, 454, 455, 456, 457, 459, 485, 495,

496, 511

formatString 445, 447, 449, 450, 451, 452, 453, 454,

455, 456, 457, 459, 480, 485, 492, 495, 496, 497,

498, 501, 511, 590, 597, 599

formatters 445, 446, 449, 451, 455, 496, 500

formatting 128, 129, 135, 136, 442, 444, 445, 446,

450, 451, 486, 494, 495, 524, 632, 633, 655, 657, 689

FormattingManager xv, 447, 449, 450, 451, 452, 454,

456, 459, 485, 494, 495, 496, 500, 511, 515

framework.swf 416, 427, 438

frame rate 27, 28, 29, 138

FreeThreadedDOMDocument 678, 679, 680, 683, 684

FSCommand 604, 605

fx:DataGridColumn 447, 448, 450, 453, 454, 456, 458,

459, 460, 462, 466, 467, 468, 476, 480, 481, 485,

486, 492, 498, 507

Ggenerated 23, 24, 27, 52, 54, 57, 59, 69, 70, 93, 95, 97,

98, 99, 137, 139, 160, 209, 213, 221, 228, 232, 235,

245, 246, 248, 249, 299, 355, 402, 410, 411, 412,

418, 515, 580, 611, 648, 649, 682

generator class 481

GET 178, 182

getChangedPropertyNames() 218, 236

getChangedValues() 218, 219, 237

getChildren() 370, 378, 379, 382, 383, 385

getDefinitionByName() 486, 516

INDEXINDEX

Page 696: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

702 RIA WITH ADOBE FLEX AND JAVA

getItem() 212

getOperation() 334

getters 85, 97, 138, 577

global scope 38, 625

Google Finance 574, 575, 601

graphics.beginFill() 479

HHBox 33, 63, 101, 102, 103, 104, 128, 134, 152, 175,

177, 178, 200, 201, 210, 323, 324, 325, 331, 336,

348, 349, 353, 355, 356, 358, 361, 461, 477, 478,

516, 554, 564, 565, 568, 569, 594, 620, 648, 674

hierarchical data provider 367

HTML xvii, 3, 5, 6, 9, 12, 13, 23, 24, 25, 27, 44, 47, 57,

58, 73, 98, 99, 115, 116, 118, 121, 122, 321, 539,

559, 604, 605, 607, 609, 611, 613, 614, 620, 621,

622, 624, 626, 628, 629, 634, 637, 638, 639, 640,

642, 643, 648, 649, 655, 682, 694, 695

HTTPService 92, 116, 117, 119, 120, 121, 122, 156,

178, 180, 181, 182, 185, 202, 545, 547, 552, 559,

561, 562, 565, 568

IIDataRenderer 387, 392, 468, 469, 471

IDispatch 634

IDropInListItemRenderer 387, 392, 394, 468, 469, 471

IEventDispatcher 139, 262, 263, 264, 425

IFactory 357, 359, 360, 481, 482, 483, 484, 494, 495,

509, 511, 512, 553

ILogger 523, 524, 525, 526, 527, 529

IManaged 262, 263, 264, 265

INITIAL_CONTEXT_FACTORY, 189

InitialContext 189, 198, 286, 289, 290

inline component 323, 358

inline statements 35

INSERT 212, 220, 232, 240, 241, 628, 665, 667

instance 6, 23, 34, 41, 44, 45, 68, 81, 82, 83, 86, 87, 88,

89, 91, 92, 95, 106, 110, 119, 122, 124, 125, 146, 152,

172, 189, 209, 242, 252, 255, 257, 263, 268, 288,

289, 290, 294, 325, 333, 334, 339, 353, 358, 359,

369, 373, 387, 394, 404, 424, 430, 435, 445, 459,

481, 482, 490, 495, 499, 534, 554, 555, 606, 607,

609, 632, 654, 659, 676, 681, 682, 693

instance method 606, 609

integration xix, 4, 6, 13, 14, 58, 193, 428, 562, 604,

613, 620, 622, 628, 643, 649, 668, 694

interface xxii, xxiv, 11, 12, 106, 107, 108, 109, 110,

111, 112, 138, 139, 262, 263, 264, 266, 367, 368,

369, 394, 434, 435, 436, 437, 468, 469, 481, 495,

523, 545, 554, 564, 565, 614, 634, 662, 663, 695

Interfaces xi, 42, 106, 108, 110

Internet Explorer 7, 28, 29, 612, 614, 622, 624, 625,

638

invalidateDisplayList() 389, 484, 511, 585, 591, 592,

597, 598, 600

invalidateProperties() 315, 316, 317, 320, 337, 350,

458, 464, 591, 600

invalidateSize() 392, 587, 591, 600

IPropertyChangeNotifier 262

isBranch 367, 369

isItemModified() 268

isItemNew() 268

itemClick 187, 188, 200

itemEditor 301, 474, 492, 495, 496, 497, 503

itemRenderer 183, 184, 273, 275, 276, 323, 324, 325,

331, 336, 348, 349, 353, 354, 355, 356, 357, 358,

360, 361, 387, 458, 459, 460, 461, 462, 466, 467,

468, 474, 476, 477, 478, 479, 480, 481, 484, 494,

495, 499, 501, 502, 503, 507, 508, 511, 512, 513,

553, 554, 555, 559, 562, 564, 565, 568, 569

itemToLabel() 347

itemUpdated() 262, 465

item editor 467, 470

item renderer 321, 322, 323, 385, 386, 387, 396, 458,

467, 468, 470, 480, 481, 484, 494, 554

ITreeDataDescriptor 367, 368, 369, 370

IUID 262, 263, 264, 265

JJAR 245, 403

java.util.List 213, 221, 266, 299

JavaBean 169, 577

JavaScript xxi, xxii, 7, 8, 9, 10, 11, 12, 13, 36, 37, 47,

57, 58, 80, 82, 87, 98, 116, 121, 123, 125, 604, 605,

606, 607, 608, 609, 611, 612, 613, 614, 620, 621,

622, 623, 624, 626, 627, 628, 629, 630, 632, 633,

636, 637, 638, 641, 655, 690, 694, 695

Java Transaction API (JTA 287

INDEXINDEX

Page 697: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 703

jdbc/theriabook 216, 217, 221, 224, 225, 227, 232,

235, 246, 249, 299, 326, 327, 344, 376

JDBCConnection 216, 217, 223, 225, 226, 227, 228,

233, 234, 235, 289, 326, 327, 344, 376

JDBC data source 326

JDBC driver 242, 244

JDT 50

JIT 4, 14, 81

JMS xii, xxiv, 6, 13, 156, 158, 189, 190, 191, 193, 194,

195, 196, 197, 198, 200, 202, 521

JMS adapters 202

JNDI 189, 190, 194, 197, 198, 229, 244, 291

jndi.properties 189, 197

JRun 181, 243, 244, 534, 535, 536

JScript 622, 625, 630, 632, 633, 634, 641, 643, 688, 689

JSP xi, 73, 115, 116, 117, 118, 134, 539

JVM 3, 6, 7, 65, 534, 535

jvm.config 22, 534, 535

Llabel 5, 28, 31, 32, 34, 35, 54, 59, 60, 61, 65, 67, 87, 88,

90, 93, 94, 98, 108, 118, 119, 120, 128, 131, 134,

138, 140, 141, 143, 147, 151, 152, 170, 171, 172,

173, 176, 183, 184, 200, 210, 260, 272, 273, 275,

276, 277, 284, 297, 301, 302, 303, 312, 313, 314,

315, 316, 318, 319, 320, 337, 347, 349, 351, 366,

367, 368, 369, 370, 371, 373, 379, 380, 383, 384,

388, 390, 391, 392, 395, 409, 427, 428, 429, 430,

431, 433, 434, 435, 436, 437, 446, 449, 461, 462,

463, 465, 467, 469, 473, 474, 476, 477, 485, 494,

501, 502, 506, 523, 524, 526, 527, 548, 549, 557,

559, 560, 564, 565, 567, 568, 569, 594, 595, 608,

615, 618, 619, 620, 643, 647, 648, 666, 667, 668,

672, 673, 674

labelField 313, 318, 319, 320, 321, 324, 331, 332,

336, 337, 347, 348, 349, 353, 354, 356, 366, 367, 368

labelFunction xv, 123, 124, 128, 135, 138, 168, 169,

171, 173, 176, 200, 318, 335, 347, 357, 360, 444,

445, 446, 447, 449, 452, 456, 494, 619, 647, 673

Layouts xi, 33, 61

library xxii, 5, 8, 11, 12, 21, 30, 36, 37, 159, 312, 401,

403, 404, 405, 406, 410, 412, 413, 416, 418, 420,

423, 428, 429, 430, 434, 438, 439, 442, 450, 500,

501, 502, 650

library project 416, 439

linkage variable 209, 355, 420, 502

LinkBar 33, 172, 516

LinkButton 183, 184, 385, 549, 550, 559, 567, 568

Link Type 403, 406, 408, 420, 429, 438, 501

listData 394, 395, 396, 458, 463, 464, 465, 468, 469,

470, 472, 473, 476, 482, 483, 487, 489, 493, 510

Loader 33, 401, 424, 425, 548, 549, 550, 551, 556,

557, 558

LocalConnection xvi, xvii, 430, 524, 528, 529, 530,

531, 604, 605, 650, 658, 659, 660, 661, 662, 663,

664, 665, 668, 669, 693

log 46, 66, 71, 225, 226, 520, 521, 522, 523, 524, 525,

528, 530, 531, 532

logger 234, 286, 287, 289, 290, 291, 522, 523, 524, 526,

527, 528, 532

logging 216, 520, 521, 522, 523, 524, 525, 526, 527,

528, 529, 530, 532, 533, 534, 541

MManaged xiii, 211, 252, 262, 264, 265

MaskedInput 486, 487, 494, 495, 496, 500, 505, 506

measure() 392, 395, 587

Merge-in 403, 404

message xxiv, 21, 36, 71, 87, 91, 119, 121, 126, 127,

128, 130, 131, 132, 133, 134, 136, 137, 161, 190,

191, 192, 193, 194, 195, 196, 199, 201, 258, 281,

283, 331, 335, 338, 352, 409, 444, 515, 524, 525,

526, 527, 528, 529, 531, 532, 606, 610, 622, 644,

653, 656, 663, 664, 665, 666, 667, 669

Message-Oriented-Middleware (MOM) 190

Message Broker 196

messaging-config.xml 193

Microsoft Windows 614, 622

MiniDebugTarget 523

modifiedCount 267, 268, 269, 270, 271, 272, 276, 279,

280, 281, 282, 283, 284, 302, 303

module 433, 434, 438, 651, 654, 676, 681, 682,

683, 690

MP3 547, 569

multi-column dropdown list 312

mx.controls.Button 141, 143, 392

mx.controls.dataGridClasses.DataGridColumn 447,

449, 451, 452, 454, 455, 456, 459, 468, 483, 484,

INDEXINDEX

Page 698: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

704 RIA WITH ADOBE FLEX AND JAVA

494, 505, 509, 510, 511

mx.controls.listClasses.ListBase: 512

mx.controls.SWFLoader 401

mx.controls.treeClasses.TreeItemRenderer 387

mx.core.ClassFactory 459, 483, 484, 494, 496, 509,

511, 513

mx.formatters.SwitchSymbolFormatter 445, 446,

449, 451

mx.modules.ModuleLoader 401

mx.preloaders.Preloader 402

mx.rpc.AbstractOperation 257, 334, 338, 443, 513

mx.vaidators.RegExpValidator 490

mxmlc 21, 22, 23, 24, 25, 26, 27, 28, 31, 47, 402, 410,

413, 418, 429

MXML syntax 442

NNamespaces x, xi, 29, 112

NaN 95, 488, 489, 490, 491, 493

NavBar 172

newInstance() 40, 481, 482, 483, 499, 508, 509, 510,

516, 553

Number 38, 39, 42, 83, 84, 85, 86, 89, 94, 96, 97, 105,

107, 113, 114, 128, 135, 136, 137, 163, 168, 170,

174, 177, 201, 211, 263, 265, 328, 390, 391, 395,

461, 463, 465, 476, 479, 488, 489, 490, 491, 492,

493, 549, 553, 557, 576, 578, 583, 584, 586, 587,

588, 590, 593, 599, 609, 610, 611, 612, 618, 646,

657, 666, 672

NumericInput 486, 487, 488, 489, 490, 492, 493, 494,

496, 497, 500, 505, 506

OObject xi, xiii, xiv, xv, 15, 39, 40, 82, 89, 98, 104, 105,

111, 124, 128, 130, 136, 139, 168, 170, 174, 177,

201, 206, 207, 215, 217, 231, 261, 263, 264, 266,

268, 270, 271, 272, 274, 278, 280, 282, 289, 290,

297, 307, 314, 315, 316, 317, 318, 319, 320, 321,

322, 324, 332, 336, 337, 348, 349, 350, 361, 368,

369, 370, 371, 373, 375, 376, 377, 378, 379, 380,

381, 382, 383, 384, 385, 387, 394, 395, 396, 402,

410, 412, 418, 423, 428, 445, 446, 449, 450, 451,

452, 454, 455, 456, 457, 458, 459, 460, 462, 463,

464, 465, 466, 467, 470, 472, 474, 476, 480, 482,

483, 484, 485, 486, 487, 489, 493, 494, 495, 496,

499, 500, 501, 504, 506, 508, 509, 510, 511, 512,

513, 514, 553, 576, 577, 579, 591, 600, 605, 608,

616, 618, 644, 645, 646, 647, 650, 651, 652, 656,

657, 658, 666, 669, 670, 672, 676, 677

Object-oriented 105, 577

Observable 138

Observer 138

Office Web Components (OWC) 613, 624, 625

OLEObjects 652, 654, 677

on-demand loading 357, 421

onEnterFrame 553, 554, 555, 605

onload 627, 629, 630

overshadowing 401

Pperformance 3, 4, 14, 15, 21, 28, 29, 37, 46, 65, 66, 81,

111, 160, 163, 253, 316, 328, 335, 461, 508, 516,

522, 574, 613, 634

PieChart 168, 169, 172, 173, 176, 187, 188, 200, 574

PivotTable 643

POJO 13, 115, 156, 158, 160, 161, 162, 189, 253, 385

Polymorphism xi, 106, 110

portlet 400, 409, 421

POST 116, 119, 178, 527

PreparedStatement 216, 218, 219, 220, 224, 225, 227,

236, 237, 240, 241, 326, 327, 344, 376

prepareStatement() 240

preventDefault() 488, 494, 516

preventRendererReuse 507, 511, 512, 514

productivity 2, 3, 361, 400, 408, 409, 416

ProgID 624, 654

properties 27, 41, 44, 58, 60, 64, 80, 82, 85, 86, 95, 96,

97, 99, 105, 106, 112, 115, 122, 139, 149, 161, 163,

172, 181, 182, 189, 191, 194, 195, 197, 208, 213,

229, 244, 245, 246, 249, 253, 262, 264, 267, 268,

292, 300, 313, 314, 315, 316, 318, 319, 321, 326,

333, 353, 358, 377, 378, 428, 429, 434, 435, 437,

447, 448, 453, 454, 459, 460, 468, 471, 481, 482,

483, 484, 489, 490, 494, 495, 496, 497, 499, 502,

508, 509, 510, 511, 515, 516, 521, 577, 578, 586,

605, 607, 636, 637, 657, 675, 676

propertyChange 96, 97, 263, 269

PropertyChangeEvent 260, 261, 262, 263, 264, 267,

INDEXINDEX

Page 699: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 705

269, 270, 279, 282, 305, 306

Property Bag xvi, 503

PROVIDER_URL 189

Qqueue 190, 191

RRadioButtonGroup 33, 468, 470, 471, 505

RadioButtonGroupBox xv, 467, 468, 469, 470, 471,

473, 474, 475, 500, 505, 506

RadioButtons 442, 471

Range 633, 634, 636, 637, 653, 654, 681, 682, 691

refresh() 130, 131, 134, 137, 257, 258, 281, 283

registerCollection() 294

RemoteClass 163, 211, 263, 264, 265, 266, 292,

328, 411

RemoteObject 115, 156, 160, 161, 166, 169, 172,

173, 175, 176, 196, 199, 202, 255, 256, 257, 258,

295, 296, 325, 326, 328, 329, 331, 332, 333, 334,

335, 338, 346, 351, 352, 370, 377, 381, 382, 383,

443, 513, 514, 525

remoting-config.xml 161, 253, 291, 326

resetState() 271, 280, 281, 283, 297, 307

resource xxiv, 353, 354, 356, 357, 358, 359, 361,

378, 421, 422, 438, 439

resources 12, 24, 37, 47, 53, 58, 65, 70, 175, 354,

357, 358, 359, 360, 361, 401, 403, 416, 417, 419,

420, 421, 424, 438, 490, 523, 667

resource class 353, 358

ResultEvent 163, 166, 169, 173, 176, 255, 256, 257,

258, 281, 283, 294, 295, 296, 330, 331, 334, 335,

338, 346, 351, 352, 377, 380, 383, 384, 443, 444,

514, 515, 547, 559

RSL xv, 401, 402, 403, 404, 407, 412, 420, 421, 422,

423, 501, 516

RSS 156, 178, 179, 180, 182

RTMP 14, 193, 195, 521

RuntimeException 195, 196, 216

runtimeStyles 480, 481, 482, 483, 484, 485, 486, 494,

510, 511

runtime errors 24, 86

SSafari 612

Security.allowDomain 607, 660

Security.allowInsecureDomain 607

security domains 45, 401

SELECT 221, 224, 232, 234, 343

selectedIndex 187, 260, 272, 274, 276, 278, 284, 301,

302, 303, 304, 313, 314, 316, 318, 319, 320, 337,

349, 350, 465, 467, 475, 478, 485, 553, 554, 559,

616, 617, 619, 645, 648, 667, 670, 674

selectedItem 132, 133, 137, 187, 188, 200, 272, 276,

284, 301, 313, 314, 319, 320, 337, 349, 465, 477,

485, 553, 554, 564, 565, 568, 569, 616, 617, 619,

620, 645, 647, 648, 670, 671, 673, 674

self-initializing library 420

send() 116, 117, 119, 256, 258, 296, 334, 335, 338,

352, 443, 514, 529, 547, 559, 560, 564, 569, 658,

659, 660

sendBatch() 294

setItemNotModified() 268

setRollbackOnly() 287

setStyle() 482

setters 85, 97, 577

setTimeout() 91, 341, 347, 370

SetVariable 604

SharedObject 604, 605

SheetChange 638, 642

Shockwave Flash 650, 674, 676, 690

showRoot 366, 367, 368

skin 583, 584, 585, 591, 633

SOAP 156

stack 66, 87, 88

stateless protocol 2

static linking 404

static method 41

stored procedures 252

String xi, 35, 38, 39, 40, 41, 42, 67, 80, 81, 82, 83, 85,

87, 88, 90, 91, 96, 97, 98, 105, 106, 107, 121, 124,

128, 136, 137, 150, 162, 163, 164, 166, 168, 169,

170, 173, 174, 176, 177, 184, 185, 186, 188, 189,

191, 198, 201, 211, 212, 214, 215, 216, 218, 220,

225, 227, 231, 236, 240, 241, 255-258, 263, 264,

265, 272, 286, 288, 289, 291, 292, 293, 295, 299,

306, 312, 313, 318, 319, 327, 328, 329, 333, 336,

INDEXINDEX

Page 700: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

706 RIA WITH ADOBE FLEX AND JAVA

338, 340-344, 346, 347, 349, 351, 352, 357-361,

375, 376, 383, 409, 424, 425, 428, 433, 434, 436,

443, 445, 446, 447, 449-451, 452, 454, 455, 456,

459, 462, 463, 465, 467, 480, 483, 485, 486, 489,

490, 492, 493, 495, 496, 499, 502, 503, 504,

508-511, 514, 524, 525, 527, 528, 529, 531, 532,

563, 567, 581, 585, 587, 589, 590, 594, 599, 600,

605, 608-612, 616, 618, 619, 641, 644, 646, 647,

652, 653, 655, 657, 663, 664, 666, 667, 669,

670, 672, 675-679, 681, 683, 692

SWC 30, 402, 403, 404, 406, 407, 412, 413, 418, 438,

439, 448, 501

Swing 3, 6, 7, 11, 13, 14, 30, 36, 62, 90, 169, 171, 186

sync() 217, 252, 266, 280, 281, 283, 284, 285, 287, 293

sync-method 212, 213, 215, 217, 219, 221, 232, 233,

234, 235

System.setClipboard 643, 644, 681

SystemManager 45, 46, 402, 410, 411, 426, 430, 431,

435, 437

system domain 401

TTabNavigator 33, 172, 272, 273, 275, 277, 284

target 25, 26, 65, 89, 91, 96, 139, 140, 141, 144, 145,

186, 229, 232, 290, 362, 401, 413, 414, 418, 439,

470, 471, 521, 522, 523, 524, 525, 526, 527, 528,

529, 532, 539, 566, 567

TextInput xiv, 28, 33, 59, 61, 65, 67, 87, 88, 93, 94, 95,

118, 120, 152, 339, 340, 343, 345, 430, 431, 436,

437, 467, 479, 487, 488, 490, 492, 496, 505, 506,

560, 561, 564, 565, 568, 569, 608, 609, 612, 619,

647, 673

theriabook-manifest.xml 448, 487

theriabook.swc 448, 450, 479, 487, 490, 501, 502, 515

theriabook.swf 500, 501

thousandsSeparator 489, 490

Thumbnail xvi, 552

TileList 33, 554, 560, 561, 562, 564, 568

Timer 91, 136, 550, 551, 556, 557, 570, 693

Tomcat xvi, 116, 161, 181, 189, 195, 243, 244, 537,

538, 539, 540, 541

topic 63, 158, 189, 190, 191, 192, 193, 194, 196, 197,

198, 199, 201, 218, 328, 361, 385, 515

trace 21, 28, 38, 40, 41, 66, 67, 68, 81, 82, 84, 86, 105,

120, 140, 144, 145, 147, 148, 149, 152, 195, 196,

258, 260, 261, 281, 283, 412, 418, 423, 425, 427,

430, 500, 520, 521, 522, 523, 526

TraceTarget 523, 524

transactions xxi, 252, 285, 287

Transfer Object Assembler 377

transitions 65

TreeItemRenderer xv, 387, 388, 389, 395

TreeListData 394, 395, 396

Tree control 366, 367, 369, 370, 377, 381, 382, 385,

387, 396

typeof 319, 320, 337, 349, 492, 610, 638, 641

UUIClassFactory 481, 482, 483, 484, 494, 495, 499, 500,

501, 502, 503, 505, 507, 508, 509, 510, 511, 512,

513, 515

UIComponent 46, 359, 387, 392, 433, 468, 477, 516,

586, 587

undefined 38, 81, 82, 88, 96, 97, 111, 112, 371, 373,

379, 383, 394, 396, 433, 436, 462, 463, 464, 465,

489, 490, 491, 492, 493, 513, 515, 611, 612, 656

undoItem() 268

UPDATE 218, 232, 233, 234, 236, 237, 240, 266, 267,

271, 464, 470, 472, 666, 668, 669, 671, 675

updateDisplayList() 389, 461, 463, 477, 479, 578

updateItem() 212

Vvalidators.properties 490

valueCommit 319, 337, 349, 471, 472

variableRowHeight 182, 184, 504, 507

VBA 604, 632, 633, 649, 650, 651, 654, 674, 675,

676, 694

VBox 30, 31, 32, 33, 35, 63, 128, 135, 186, 200, 433,

562, 564, 565, 568, 569, 594, 595, 608, 619, 620,

647, 648, 668, 673, 674

VBScript 630, 632, 634, 641

VDividedBox 33, 186, 187, 209, 210, 297, 298

Version Control System xi, 73

view states 566, 570

VT_ARRAY 641

VT_DATE 632, 633

INDEXINDEX

Page 701: SYS-CON Books Rich Internet Applications With Adobe Flex & Java

RIA WITH ADOBE FLEX AND JAVA 707

WWEB-INF/classes 162

WEB-INF/lib 162, 213, 244, 246

WebService 370

Web 2.0 xxi, 4, 146

Web Services xvi, 9, 34, 156, 253, 544, 561, 570

wedge 171, 187, 188

wordWrap 108, 182, 183, 184, 507

Workbook 630, 650, 651, 675, 676, 693

WPF ix, 4, 7, 9, 10

WTP xvi, 537, 538, 539, 541

XXMLHttpRequest 4, 7

XMLList 39, 159, 163, 164, 167, 168, 170, 174, 177,

201, 367, 373

XMLListCollection xii, 126, 130, 131, 134, 138, 164,

181, 185, 367

XPath 164, 223

xsl:apply-templates 223, 234, 685, 686, 687

xsl:call 225, 228, 229, 232, 234, 236, 238, 239, 240

xsl:template 222, 223, 225, 226, 229, 231- 240, 685,

686-688

xsl:value-of 222, 223, 225, 226, 228, 229, 232-240,

685, 687, 688

XSLT 604, 676, 689, 694

YYahoo! Finance 178, 179

ZZIP 211, 214, 220, 227, 230, 241, 403

INDEXINDEX