mardi 7 avril 2009

What's new in GraniteDS 2.0

GraniteDS 2.0 beta 2 has just been released, and is almost feature complete. There will probably be a 2.0 RC adding the only important missing feature (OSGi support), and then we shoud be ready for the final release. This post only describes the most important new features in this release, but this beta 2 also contains a whole lot of bug fixes and improvements since the 1.2 release.

New packaging

As stated in the migration notes, the most visible change is the completely new packaging of the libraries and examples. The persistence/lazy loading support has been completely refactored to simplify the support of new JPA/JDO providers. Consequently the Flex swc libraries have no more dependency on the server-side persistence technology. There are now only 2 swcs: 'granite-essentials.swc' that includes the minimal mandatory persistence classes and must be linked with -include-library, and 'granite.swc' which can be linked as a standard Flex library. This new packaging should allow for smaller compiled swfs as the Flex compiler can now decide which classes need to be included. Finally the Tide framework libraries has been merged in the 'normal' GDS libraries, both client side and server side.

Improved example projects

The example projects have been improved and are included in the main distribution, in the 'examples' folder. Here is the list of these examples :
  • graniteds_pojo: most simple one with a basic POJO session service and a MXML calling a RemoteObject
  • graniteds_chat: simple chat example demonstrating use of Gravity to handle near real-time communication between Flex clients
  • graniteds_ejb3: simple address book with EJB3/JPA services
  • graniteds_spring: simple address book with Spring/JPA services. Also shows integration with Spring security
  • graniteds_guice: simple CRUD application with Guice/WARP services
  • graniteds_tide_ejb3/graniteds_tide_spring/graniteds_tide_seam: same address book with EJB3/JPA, Spring/Hibernate or Seam/JPA. Shows the Tide client framework, paged collections, lazy loading, client-side authorization integration and multiuser data updates (you can see it by opening different browser on the application). The Seam example uses the new typed event model of GDS 2.0. The Tide/EJB3 and Tide/Spring examples now have a deployment configuration that allow to deploy them in GlassFish V2 with the TopLink JPA provider.
  • seam_flexbooking: completely rewritten example that exactly reproduces the look and behaviour of the original Seam example. Shows the use of the Tide client framework, and use of client-side conversations integrated with Seam conversations. This is the most advanced example for demonstrating the tight integration with Seam.
Support for more server platforms

The core GraniteDS supports new JPA providers and application servers. More specifically GlassFish V2 and V3 with Hibernate or TopLink/EclipseLink are now viable deployment platforms, even without specific Gravity integration.
JPA provider integration :
  • Hibernate
  • TopLink
  • EclipseLink
  • OpenJPA (new in 2.0)
  • DataNucleus/JPOX (new in 2.0)
Security integration :
  • Tomcat 6
  • Jetty 6
  • JBossWeb (with the Tomcat security service)
  • WebLogic (new in 2.0)
  • GlassFish V2
  • GlassFish V3 (new in 2.0)
Gravity server push :
  • Tomcat 6 Comet
  • JBoss 5/JBossWeb comet events
  • Jetty 6 continuations
  • Any other application server without comet support with Jetty servlet and jetty-util.jar
There have been also a lot of improvements in the Tide framework, in the client framework itself and also in the integration with server frameworks.

Tide/Client framework

- UI components defined in MXML can now be automatically registered in the Tide context. A preinitialize handler should be defined in the main application and call Seam.getInstance().initApplication() / Spring.getInstance().initApplication() / Ejb.getInstance().initApplication(). The main MXML is then registered in the Tide context and all children MXML annotated with [Name] will be registered automatically in the context with their component name (or with the name provided in the annotation). For example:

<mx:Application preinitialize="Seam.getInstance().initApplication()">
<Login id="loginUI"/>
</mx:Application>

Login.mxml :
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[Name]
</mx:Metadata>
<mx:Script>

<![CDATA[
import org.granite.tide.seam.security.Identity;

[Bindable] [In]
public var identity:Identity;

[Bindable]
public var message:String = null;

private function tryLogin(username:String, password:String):void {
identity.username = username;
identity.password = password;
identity.login();
}
]]>
</mx:Script>

<mx:Text htmlText="<i>(Type in user/user or admin/admin)</i>" textAlign="center"/>

<mx:Form>
<mx:FormItem label="Username">
<mx:TextInput id="username"/>
</mx:FormItem>
<mx:FormItem label="Password">
<mx:TextInput id="password" displayAsPassword="true"
enter="tryLogin(username.text, password.text);"/>
</mx:FormItem>
</mx:Form>

<mx:Text text="{message}" textAlign="center"/>

<mx:Button label="Login"
click="tryLogin(username.text, password.text);"/>
</mx:Panel>

Here the Login.mxml will be registered in the Tide context with the name 'loginUI'. That means that it can be injected with the [In] annotation and dispatch Tide events. The name can be overriden with [Name("someName")]. It is also possible to specify a conversation scope with [Name("someName", scope="conversation")], in this case the component will not be registered automatically but will still have to be set in the conversation context with tideContext.someName = theComponent.

- The Tide framework supports a new typed event model that can be optionally used instead of TideUIEvent/TideUIConversationEvent. Any Flex event can now be dispatched by the UI components, but their types have to be specified in an Event annotation (and of course the Event annotation must be kept in the Flex compiler options).
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[Name]
[Event(name="login")]
</mx:Metadata>
<mx:Script>

<![CDATA[
[Bindable]
public var message:String = null;
]]>
</mx:Script>

<mx:Text htmlText="<i>(Type in user/user or admin/admin)</i>" textAlign="center"/>

<mx:Form>
<mx:FormItem label="Username">
<mx:TextInput id="username"/>
</mx:FormItem>
<mx:FormItem label="Password">
<mx:TextInput id="password" displayAsPassword="true"
enter="dispatchEvent(new LoginEvent(username.text, password.text))"/>
</mx:FormItem>
</mx:Form>

<mx:Text text="{message}" textAlign="center"/>

<mx:Button label="Login"
click="dispatchEvent(new LoginEvent(username.text, password.text))"/>
</mx:Panel>

With the LoginEvent class:
    public class LoginEvent extends Event {

public var username:String;
public var password:String;


public function LoginEvent(username:String, password:String):void {
super("login", true, true);
this.username = username;
this.password = password;
}

public override function clone():Event {
return new LoginEvent(username, password);
}
}

A client component can then register an observer with this event type :
[Observer("login")]
public function loginHandler(event:LoginEvent):void {
...
}

Such events can also trigger client conversations by implementing org.granite.tide.events.IConversationEvent that requires a property getter conversationId (that defines the conversationId and replaces the first argument of TideUIConversationEvent).

Tide/Seam

- There have been some improvements in the interactions between Tide client conversations and Seam server conversations. Tide is now able to detect when the conversationId has been changed on the server or when a new conversation has begun. There are also two new client components ConversationList and Conversation that are client counterparts to the Seam components. ConversationList is a list of currently existing Seam conversation and Conversation can be used for example to set a description on the current conversation, in case you need interoperability between Flex-initiated conversations and classic Web-initiated Seam conversations.

- The client-side status messages has been improved and Tide now supports control specific messages. A client component StatusMessages can be injected and has a messages property (which replaces context.messages) and a controlMessages property that is a map of messages keyed by controlId. A new client-side validator allows to bind such control messages to Flex UI components. A (minor) beneficial side-effect of this change is also that you can now have a context variable named 'messages'.

These new features are used in the new Seam booking example.

Tide/Spring/Grails

Spring integration was relatively lacking in some parts compared to the Seam integration. GDS 2.0 brings many new features in the Tide/Spring integration.

- Tide/Spring now supports Spring MVC controllers (only annotation-based) besides the existing support for pure services. That allows for easy binding between server-provided data and the Flex application. All model variables returned from a controller's ModelAndView are bound to Tide context variable, and thus can be directly injected and bound to UI components.
<mx:Application preinitialize="Spring.getInstance().initApplication()">
<mx:Script>
[In] [Bindable]
public var people:ArrayCollection;

[In]
public var peopleController:Object;
</mx:Script>

<mx:VBox>
<mx:DataGrid dataProvider="{people}">
<mx:columns>
<mx:DataGridColumn dataField="name"/>
</mx:columns>
</mx:DataGrid>

<mx:TextInput id="searchString"/>
<mx:Button label="Get list" click="peopleController.list({searchString: searchString.text})"/>
</mx:VBox>
</mx:Application>

Spring controller:
@Controller("peopleController")
@Transactional
public class PeopleController {

protected SessionFactory sessionFactory;

@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}

@Transactional
@RequestMapping("/people/list")
public ModelMap createPerson(@RequestParam("searchString") String searchString) {
List people = sessionFactory.currentSession()
.createQuery("select p from Person p where p.name like :searchString")
.setString("searchString", searchString)
.list();
return new ModelMap("people", people);
}
}

- The Spring integration has also been extended to support Grails controllers and services. The Grails integration is available as a Grails plugin (plugin page here: http://www.grails.org/plugin/gdsflex). See previous blog entries for a detailed tutorial on the usage of the plugin.

- Also there is now support for client-side integration with Spring security authorization. The Tide Identity component now has 4 new methods, ifAllGranted, ifAnyGranted, ifNotGranted and hasPermission that can be used to show/hide various parts of the UI depending on the UI rights. The method names are stricly copied from the Spring security JSP tag library. The ifSomethingGranted(roleNames) are straightforward and take a comma separated list of role names. The hasPermission(entity, action) can be used to protect access to individual entities when using Spring security ACLs on the server.
These new methods require the configuration of a server-side identity component on the Spring context :
<bean id="identity" class="org.granite.tide.spring.security.Identity"/>

or to handle ACLs :
<bean id="identity" class="org.granite.tide.spring.security.AclIdentity"/>



The last important new feature is the multiuser data update but it's relatively complex and there will be a blog entry about it later.

Aucun commentaire: