Meeting 7
Aus JSUG
When: Monday, November 24th, 2008 - 19:00
Where: Freihaus HS4
This time we got two demonstrations of slightly different (web) frameworks. On the one side the web application framework Seam using EJB3 and JSF, on the other side Flash based Rich Internet Applications developed with Flex. They both have in common that they use components as the building block and declare them in XML.
Download the introduction slides (pdf, 1.2MB).
Inhaltsverzeichnis |
[Bearbeiten] RIAs with Adobe Flex
Download the slides (pdf, 0.9MB).
You can also view the slides online.
Download the sourcecode as *.zip (2.1MB).
[Bearbeiten] Agenda
- Preface
- Definition of the term "Filthy Rich Clients"
- Limitation of using traditional JavaScript vs Flash-based technologies
- Getting Started
- Some hard facts
- Simple ActionScript example
- 8 Demonstrations
- Hello World - Styling components using external CSS
- Listener & Binding - Handling (user-) events and bind variables
- ExternalInterface - Invoke JavaScript functions from ActionScript
- XmlRequest - Use the DataGrid to display XML fetched via HTTPService
- Transitions - Animate state transitions with the ViewStack
- Cairngorm - Write a common registration form in a good (MVCS) style
- BlazeDS - Let ActionScript and Java talk to each other
- AIR App - Create your first Flex desktop application
- Summary
- Technologies overview (see links below)
- Diagram of the MVC pattern provided by Cairngorm
- Appendix: ActionScript compared to the Java language
[Bearbeiten] Content
Christoph Pickl gave an introduction to Adobe Flex. As its predecessor Flash itself is a collection of tools (Flash Player, Flash authoring), Flex is also more than a single product but consists of an open source SDK, an ActionScript 3 application framework, an Eclipse based IDE called Flex Builder, XML based declarative language, the Flash Player for web applications and the AIR runtime for desktop applications. With additionals pieces of software like Red5 (an open source RTMP server for streaming, recording) or BlazeDS (open source alternative for remoting and messaging capabilities of Adobe's LifeCycle Data Services) you can even natively talk to a powerful backend which is written in a totally different language (Java, C#, PHP, ...) or just exchange plain XML so any language will suite.
A simple ActionScript example (looks just like Java, doesnt it?):
package at.jsug { import logging.Logger; public class Demo extends AbstractDemo implements IDemo { // private static final logger, just as you are used to it with apache commons logging/log4j private static const LOG: Logger = Logger.getLogger("at.jsug.Demo"); // every class member must be declared (either it is a var/const or a function) private var name: String; // constructor public function Demo(name: String) { this.name = name; } public function myOperation(value: Number): int { LOG.info("myOperation(value=" + value + ")"); return value / 2; } // mandatory override keyword, nice ;) public override function toString(): String { return "Demo[name=" + this.name + "]"; } }}
[Bearbeiten] Demos
[Bearbeiten] Hello World
HelloWorld.xml:
<?xml version="1.0" encoding="utf-8"?> <mx:Application layout="vertical" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Style source="helloWorld.css" /> <mx:Label text="Hello World!" /> </mx:Application>
helloWorld.css:
Application { background-color: white; vertical-align: middle; } Label { color: black; font-size: 40; font-weight: bold; }
[Bearbeiten] Listener & Binding
Listener.mxml:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> <mx:Script> <![CDATA[ import mx.controls.Button; private function onClick(event: MouseEvent): void { const label: String = (event.currentTarget as Button).label; trace("button with label [" + label + "] clicked"); } ]]> </mx:Script> <mx:Button label="Click me" click="onClick(event)" /> </mx:Application>
ListenerBinding.mxml:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> <mx:Script> <![CDATA[ import mx.controls.Button; [Bindable] private var boundString: String = ""; private function onClick(event: MouseEvent): void { const label: String = (event.currentTarget as Button).label; this.boundString = "button with label [" + label + "] clicked"; } ]]> </mx:Script> <mx:Label text="{this.boundString}" /> <mx:Button label="Click me" click="onClick(event)" /> </mx:Application>
[Bearbeiten] JavaScript Invocation
JavaScript.mxml:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> <mx:Script> <![CDATA[ private function doJs(): void { ExternalInterface.call("doInvokeExternalJavascript", 42); } ]]> </mx:Script> <mx:Button label="Invoke JavaScript Function" click="doJs()" /> </mx:Application>
javascript.js:
// include this file in project/html-template/index.template.html: // <script src="javascript.js" language="javascript"></script> function doInvokeExternalJavascript(meaningOfLife) { alert('Meaning of life: ' + meaningOfLife); }
[Bearbeiten] XmlRequest
XmlRequest.mxml:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="onCreationComplete()"> <mx:Script> <![CDATA[ import mx.rpc.http.HTTPService; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.collections.ArrayCollection; [Bindable] private var personsData: ArrayCollection = new ArrayCollection(); private var personsService: HTTPService; private function onCreationComplete(): void { this.personsService = new HTTPService(); this.personsService.url = "persons.xml"; this.personsService.addEventListener(ResultEvent.RESULT, this.onPersonsResult); this.personsService.addEventListener(FaultEvent.FAULT, this.onPersonsFault); this.personsService.send(); } private function onPersonsResult(event: ResultEvent): void { this.personsData = event.result.persons.person as ArrayCollection; } private function onPersonsFault(event: FaultEvent): void { throw new Error("ERROR: " + event); } ]]> </mx:Script> <!-- alternatively could have created and configured instance via MXML: <mx:HTTPService id="personsService" url="persons.xml" result="onPersonsResult(event)" fault="onPersonsFault(event)" /> --> <mx:DataGrid dataProvider="{this.personsData}" /> </mx:Application>
[Bearbeiten] Transitions
Transitions.mxml:
<?xml version="1.0" encoding="utf-8"?> <mx:Application layout="vertical" horizontalAlign="center" verticalAlign="middle" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:ApplicationControlBar width="100%"> <mx:ToggleButtonBar dataProvider="{this.viewStack}" width="100%" /> </mx:ApplicationControlBar> <mx:ViewStack id="viewStack" width="100%" height="100%"> <mx:Canvas label="Switch Red" width="100%" height="100%" backgroundColor="#FF0000" showEffect="WipeDown" hideEffect="WipeUp" /> <mx:Canvas label="Switch Green" width="100%" height="100%" backgroundColor="#00FF00" showEffect="WipeDown" hideEffect="WipeUp" /> <mx:Canvas label="Switch Blue" width="100%" height="100%" backgroundColor="#0000FF" showEffect="WipeDown" hideEffect="WipeUp" /> </mx:ViewStack> </mx:Application>
[Bearbeiten] Cairngorm
Controller.as:
package jsug.cairngorm { import com.adobe.cairngorm.control.FrontController; public class Controller extends FrontController { public function Controller() { this.addCommand(FoobarEvent.EVENT_ID, FoobarCommand); } } }
FoobarEvent.as:
package jsug.cairngorm { import com.adobe.cairngorm.control.CairngormEvent; public class FoobarEvent extends CairngormEvent { public static const EVENT_ID: String = "foobarEvent"; public function FoobarEvent(bubbles:Boolean=false, cancelable:Boolean=false) { super(EVENT_ID, bubbles, cancelable); } public override function toString():String { return "FoobarEvent[]"; } } }
FoobarCommand.as:
package jsug.cairngorm { import com.adobe.cairngorm.commands.ICommand; import com.adobe.cairngorm.control.CairngormEvent; import mx.controls.Alert; public class FoobarCommand implements ICommand { public function FoobarCommand() { // nothing to do } public function execute(_event: CairngormEvent): void { trace("FoobarCommand.execute()"); // event actually not needed - const event: FoobarEvent = _event as FoobarEvent; const p: Person = ModelLocator.instance.person; const message: String = "My name is " + p.lastName + "! " + p.firstName + " " + p.lastName + ". "; const title: String = "Greetings"; Alert.show(message, title); } } }
JsugCairngorm.mxml:
<mx:Application layout="vertical" horizontalAlign="center" verticalAlign="middle" xmlns:cairngorm="jsug.cairngorm.*" xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*"> <!-- have to load controller manually by simply instantiating it --> <cairngorm:Controller /> <mx:Script> <![CDATA[ import mx.events.ValidationResultEvent; import jsug.cairngorm.FoobarEvent; import jsug.cairngorm.ModelLocator; private function onSendPerson(): void { trace("Application.onSendPerson()"); // most important line; dispatches cairngorm event and maps to FoobarCommand new FoobarEvent().dispatch(); } ]]> </mx:Script> <local:PersonPanel sendPerson="onSendPerson()" /> </mx:Application>
... for full sourcecode download attached zip ...
[Bearbeiten] BlazeDS
Person.as:
package { [Bindable] [RemoteClass(alias="jsug.blazeds.Person")] public class Person { public var id: int; public var firstName: String; public var lastName: String; public function Person() { } } }
PersonService.as:
package { import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.rpc.remoting.RemoteObject; /* this class extends a remote object which could have alternatively declared in XML as follows: <mx:RemoteObject id="personService" destination="personService" showBusyCursor="true"> <mx:method name="selectAllPersons" result="onPersonsResult(event)" fault="onPersonsFault(event)" /> </mx:RemoteObject> */ public dynamic class PersonService extends RemoteObject { public function PersonService() { this.destination = "personService"; // defined in remoting-config.xml file down below this.addEventListener(ResultEvent.RESULT, this.onResult); this.addEventListener(FaultEvent.FAULT, this.onFault); } public function doSelectAllPersons(): void { this.selectAllPersons(); } private function onResult(event: ResultEvent): void { Model.instance.persons = event.result as ArrayCollection; } private function onFault(event: FaultEvent): void { Alert.show(String(event.message), "Person Service Error"); } } }
Person.java:
package jsug.blazeds; public class Person { private int id; private String firstName; private String lastName; public Person(final int id, final String firstName, final String lastName) { this.id = id; this.firstName = firstName; this.lastName = lastName; } @Override public String toString() { return "Person[id=" + this.id + "]"; } // getters/setters omitted }
PersonService.java:
package jsug.blazeds; import java.util.Date; import java.util.LinkedList; import java.util.List; public class PersonService { public List<Person> selectAllPersons() { System.out.println("PersonService.selectAllPersons() invoked at " + new Date()); final List<Person> persons = new LinkedList<Person>(); persons.add(new Person(0, "Christoph", "Pickl")); persons.add(new Person(1, "Florian", "Motlick")); return persons; } }
remoting-config.xml:
<?xml version="1.0" encoding="UTF-8"?> <service id="remoting-service" class="flex.messaging.services.RemotingService"> <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="personService"> <properties> <source>jsug.blazeds.PersonService</ source> <scope>application</scope> </properties> </destination> </service>
... for full sourcecode download attached zip ...
[Bearbeiten] AIR App
JsugCompleteAir.mxml:
<?xml version="1.0" encoding="utf-8"?> <mx:WindowedApplication layout="vertical" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:FileSystemTree id="tree" width="100%" height="100%" directory="{File.userDirectory}"/> <mx:Label text="File: {this.tree.selectedItem.name}" width="100%" /> </mx:WindowedApplication>
JsugCompleteAir-app.xml:
<?xml version="1.0" encoding="UTF-8"?> <application xmlns="http://ns.adobe.com/air/application/1.0"> <id>JsugCompleteAir</id> <filename>JsugCompleteAir</filename> <name>JsugCompleteAir</name> <version>v1</version> <initialWindow> <content>[This value will be overwritten by Flex Builder in the output app.xml]</content> <title>AIR App</title> <width>400</width> <height>400</height> </initialWindow> </application>
[Bearbeiten] Links
- http://blog.flexexamples.com/ ... Huge amount of code samples
- http://books.google.com/books?id=7fbhB_GlQEAC ... Flex 3 Cookbook
- http://tv.adobe.com/ ... Online video tutorials
- [Interactive Styles Explorer Interactive Styles Explorer] ... Interactive styles explorer
- http://www.jamesward.com/census/ ... Dataloading benchmark application
[Bearbeiten] Libraries, Frameworks
- http://code.google.com/p/as3logger/ ... Log4J for Flex
- http://opensource.adobe.com/wiki/display/flexunit/FlexUnit ... JUnit for Flex
- http://www.springsource.org/extensions/se-springactionscript-as ... Springframework for Flex
- http://flicc.sourceforge.net ... Nicely integrated dependency injection framework for Flex
- http://opensource.adobe.com/wiki/display/blazeds/BlazeDS ... Server-based Java remoting and web messaging technology
- http://opensource.adobe.com/wiki/display/cairngorm/Cairngorm ... Lightweight micro-architecture for RIAs
[Bearbeiten] Other Flex Presentations
- Building Business Critial Rich Internet Applications with Adobe Flex
- Thinking in Flex
- Enterprise RIA with Flex and Java
- Flex 2.0 at Work in combination with Spring and Hibernate
- Let's Talk about FLEX, baby! (German)
[Bearbeiten] Seam Demo
Download the slides (pdf, 27KB).
You can also view the slides online.
[Bearbeiten] Content
Florian Motlik gave a short demo of the JBoss Seam web application framework. It is built on top of EJB3 and integrates JSF and Wicked. Furthermore it heavily relies on dependency injection (to be precisely it makes use of "bijection").
Bijection works with the easy use of annotations:
You use @Out to put a variable into a scope and @In to insert a variable from the scope into a SessionBean (the variable names is taken as the identifier).
You primarily work with two type of beans: A SessionBean represents the controller of the MVC pattern and takes input from the user and acts upon that data (they can be either stateful or stateless). On the other side, an EntityBean is something you persist in the database (with the help of hibernate, JPA or any other similar persistence framework).
[Bearbeiten] Examples
A seam entity bean:
@Entity @Name("user") @Scope(SESSION) @Table(name="users") public class User implements Serializable { private static final long serialVersionUID = 1881413500711441951L; private String name; private String username; private String password; public User(final String name, final String password, final String username) { this.name = name; this.password = password; this.username = username; } public User() { // nothing to do } @NotNull @Length(min=5, max=15) public String getPassword() { return this.password; } public void setPassword(final String password) { this.password = password; } @NotNull public String getName() { return this.name; } public void setName(final String name) { this.name = name; } @Id @NotNull @Length(min=5, max=15) public String getUsername() { return this.username; } public void setUsername(final String username) { this.username = username; } }
A seam session bean:
@Stateless @Name("register") public class RegisterAction implements Register { @In private User user; @PersistenceContext private EntityManager em; @Logger private Log log; public String register() { final String sql = "select username from User where username=#{user.username}"; final List existing = em.createQuery(sql).getResultList(); if(existing.size() == 0) { em.persist(user); log.info("Registered new user #{user.username}"); return "/registered.jsp"; } FacesMessages.instance().add("User #{user.username} already exists"); return null; } }
[Bearbeiten] Links
- http://www.jboss.com/products/seam ... Official Website
- http://docs.jboss.org/seam/1.2.1.GA/reference/en/html/tutorial.html ... Seam tutorial

