lundi 20 avril 2009

GraniteDS 2.0 on Google App Engine

Google App Engine is the buzzword of the week for Java developers, so of course we have tried to make GraniteDS 2.0 work on it.

After fixing a few issues we managed to have an application working correctly, that you can see at http://gdsgae.appspot.com (log in with admin/admin or user/user). The application is relatively simple, but it demonstrates a complete integration between GraniteDS, Spring MVC and JDO/DataNucleus. The Eclipse project can be downloaded here.

Having a working environment in the local development server has been quite easy, but two bugs in the GAE production environment prevented GDS to work correctly. First there is a bug in XML/XPath support (GAE 1255) that we use for configuration parsing, and we had to add support for Xalan as an alternative XPath provider. Second there is a bug in request.getInputStream (GAE 1339) that throws EOFExceptions and that we could work around by buffering the stream in the AMF servlet.

So the first good news is that the latest GraniteDS 2.0 snapshot perfectly works in GAE if you add xalan.jar and serializer.jar (from the Apache Xalan distribution) in your WEB-INF/lib folder.

Then by pure coincidence we had been working lately on JDO and DataNucleus support in GDS (thanks a lot to Stephen More for his work on it) so it was natural to try it with the GAE datastore. The second good news is that GraniteDS fully supports JDO detached objects from GAE, and that all Tide features can work with JDO annotated entities (including lazy loading).
There seem to be a lot of bugs in the GAE persistence support though when not using Key objects as entity ids. So we have added the GAEKeyConverter that can serialize GAE keys to strings in ActionScript, and support can be configured in granite-config.xml with :
<converters>
<converter type="org.granite.messaging.amf.io.convert.impl.GAEKeyConverter"/>
</converters>

JDO annotated entities has also been implemented in the GDS Eclipse builder to generate AS3 classes from a JDO model and it works with the Google Eclipse plugin. If you also use Flex Builder, it's recommended to setup the order or the builders in the project properties as follows: Java, GraniteDS, Enhancer, Flex, GAE Project validator, GAE Webapp validator.

So we're happy to be able to add Google App Engine to the list of the Java platforms supported by GraniteDS, after Tomcat, Jetty, JBoss 4/5, GlassFish V2/V3 and WebLogic.

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.

lundi 6 avril 2009

Building a Flex multi-tab CRUD application : adding security and server push

The first part of this article has described how to build the main application. The goal of this second part is to demonstrate how you can easily add security and collaborative updates to this existing application with the gdsflex 0.3 Grails plugin.

Before anything, we need to upgrade the previous project to the latest gdsflex plugin release 0.3 with :
grails install-plugin gdsflex

The usual way of adding security to a Grails application is to use the Acegi/Spring security Grails plugin. So now we have to setup this plugin.
grails install-plugin acegi

grails create-auth-domains User Role Requestmap

grails generate-manager

This will install the plugin and generate a set of gsp pages to create roles and users. Now browse http://localhost:8080/gdsexample/role/create, and create 2 roles ROLE_ADMIN and ROLE_USER. Then http://localhost:8080/gdsexample/user/create, and create 2 users admin and user, each one with one of the previously created roles. The names of the roles are important for Spring security and have to start with 'ROLE_'.

We also have to enable the Spring security integration in the GraniteDS config file:

web-app/WEB-INF/granite/granite-config.xml, uncomment or add the following line:
<security type="org.granite.messaging.service.security.SpringSecurityService"/>

Once this setup is done, we have to define a login page in the Flex application. To do this, we will simply add a ViewStack in the main MXML that will display either the login page or the application depending on the user login state. This ViewStack will be bound to the Tide Identity component that handles user authentication :

grails-app/views/GDSExample.mxml :
<?xml version="1.0" encoding="utf-8"?>

<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
layout="vertical"
backgroundGradientColors="[#0e2e7d, #6479ab]"
preinitialize="Spring.getInstance().initApplication()">

<mx:Script>
<![CDATA[
import org.granite.tide.spring.Spring;
import org.granite.tide.validators.ValidatorExceptionHandler;
import org.granite.tide.spring.Identity;
import org.granite.tide.events.TideResultEvent;
import org.granite.tide.events.TideFaultEvent;

Spring.getInstance().addExceptionHandler(ValidatorExceptionHandler);

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


[Bindable]
private var message:String;

private function loginResult(event:TideResultEvent):void {
message = "";
}

private function loginFault(event:TideFaultEvent):void {
message = event.fault.faultString;
}
]]>
</mx:Script>

<mx:ViewStack id="appView" selectedIndex="{identity.loggedIn ? 1 : 0}" width="100%" height="100%">

<mx:VBox width="100%" height="100%" verticalAlign="middle" horizontalAlign="center" >
<mx:Panel title="Login"
horizontalAlign="center"
verticalGap="0" paddingTop="8" paddingBottom="8"
xmlns:mx="http://www.adobe.com/2006/mxml">

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

<mx:Label text="{message}"/>

<mx:Button label="Login"
click="identity.login(username.text, password.text, loginResult, loginFault);"/>
</mx:Panel>
</mx:VBox>

<mx:VBox id="mainUI" width="100%">
<mx:ApplicationControlBar id="acb" width="100%">
<mx:Label text="GraniteDS / Grails example" fontSize="18" fontWeight="bold" color="#f0f0f0"/>
<mx:Button label="Logout" click="identity.logout()"/>
</mx:ApplicationControlBar>

<BookUI id="bookUI" width="100%" height="100%"/>
</mx:VBox>

</mx:ViewStack>
</mx:Application>

As you can see, most of the code we have added is the login form. The authentication is completely handled by the Identity component. Here we make use of its two main methods login / logout and of its loggedIn property that is bindable and is here used to select which view is displayed. We also define a fault handler to display a login error message when the entered credentials are not correct.

You can check now on http://localhost:8080/gdsexample/GDSExample.swf that only authenticated people can see and change existing data.

To further refine the security of the application, we could decide that only administrators are allowed to delete books in our database. This can be done server-side with Spring security, but here we'll just see how to modify the UI according to the user access rights. This is not enabled by default in the plugin, we just have to change the plugin settings, and add a GraniteDSConfig.groovy in grails-app/conf:

grails-app/conf/GraniteDSConfig.groovy :
graniteConfig {

springSecurityAuthorizationEnabled = true

}

Then we have to make a few changes in the MXML UI to hide the Delete button when the user is not an administrator:

grails-app/views/BookEdit.mxml :

import org.granite.tide.spring.Identity;

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


<mx:HBox>
<mx:Button label="Save" click="save()"/>
<mx:Button label="Delete"
enabled="{!create}"
visible="{identity.ifAllGranted('ROLE_ADMIN')}"
click="remove()"/>
<mx:Button label="Cancel" click="cancel()"/>
</mx:HBox>

You can check that if you don't log in with a user having the ROLE_ADMIN role, you don't have the Delete button. You should of course also enable server-side Spring security (for example with the @Secured annotation) and define a AccessDeniedExceptionHandler that would catch all security errors to display a uniform message (see the documentation or GDS/Tide/Spring example for more information).
The Identity component provides 4 bindable boolean methods ifAllGranted, ifNotGranted, ifAnyGranted and hasPermission corresponding to the JSP tags available in the Spring security tag library. The hasPermission is not supported right now by the Grails plugin but it depends on the configuration of Spring security ACLs that itself would probably require a complete article.

To finish this post, we will add real-time multiuser updates by adding the Gravity push functionality to our application. First we have to enable these functionalities in the plugin settings:

grails-app/conf/GraniteDSConfig.groovy :
graniteConfig {

springSecurityAuthorizationEnabled = true

gravityEnabled = true

dataDispatchEnabled = false
}

Then we have to define a Gravity channel and topic in the services-config.xml Flex configuration:

web-app/WEB-INF/flex/services-config.xml:
<services-config>

<services>
<service id="granite-service"
class="flex.messaging.services.RemotingService"
messageTypes="flex.messaging.messages.RemotingMessage">

<destination id="spring">
<channels>
<channel ref="my-graniteamf"/>
</channels>
<properties>
<factory>tideSpringFactory</factory>
<persistenceManagerBeanName>tidePersistenceManager</persistenceManagerBeanName>
</properties>
</destination>
</service>

<service id="gravity-service"
class="flex.messaging.services.MessagingService"
messageTypes="flex.messaging.messages.AsyncMessage">
<adapters>
<adapter-definition id="simple" class="org.granite.gravity.adapters.SimpleServiceAdapter"/>
</adapters>

<destination id="bookManagerTopic">
<properties>
<session-selector>true</session-selector>
</properties>
<channels>
<channel ref="my-gravityamf"/>
</channels>
<adapter ref="simple"/>
</destination>
</service>
</services>

<factories>
<factory id="tideSpringFactory" class="org.granite.tide.spring.SpringServiceFactory">
</factory>
</factories>

<!--
! Declares my-graniteamf channel.
!-->
<channels>
<channel-definition id="my-graniteamf" class="mx.messaging.channels.AMFChannel">
<endpoint
uri="http://{server.name}:{server.port}/{context.root}/graniteamf/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>

<channel-definition id="my-gravityamf" class="org.granite.gravity.channels.GravityChannel">
<endpoint
uri="http://{server.name}:{server.port}/{context.root}/gravityamf/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>

</services-config>

Here we have just added a new channel-definition, gravity-service and topic destination in the configuration, now we have to tell the Flex client and the Grails components to send/receive the data updates on this topic. The server part is relatively simple, we just have to annotate our controllers/services with the DataEnabled annotation:
@DataEnabled(topic="bookManagerTopic", params=ObserveAllPublishAll.class, publish=DataEnabled.PublishMode.ON_SUCCESS)

This annotation specifies the topic and has two other important parameters:

- 'params' defines a publishing selection class. To keep things simple here, we will just write a very simple selection class that allow everyone to observer and publish all updates. For some reason, using Groovy classes in annotations triggers an infinite loop in the Grails compilation, so we will just write a Java class this time:

src/java/ObserveAllPublishAll.java
import org.granite.tide.data.DataObserveParams;
import org.granite.tide.data.DataPublishParams;
import org.granite.tide.data.DataTopicParams;

public class ObserveAllPublishAll implements DataTopicParams {

public void observes(DataObserveParams params) {
// Define key/value pairs that the client is able to observe
// Will be used to construct the topic selector for the client calling the component
}

public void publishes(DataPublishParams params, Object entity) {
// Define key/value pairs to add in the published message headers
// Will be used to match against the topic selector of the subscribed clients
}
}

- 'publish' defines the publishing mode. ON_SUCCESS indicates that all remote calls on annotated components that do not trigger exceptions will dispatch the corresponding entity updates. In general you will want to have more control on the dispatching and use the MANUAL mode to be sure that updates are dispatched only on transaction success and include the dispatch in a Grails interceptor. We could also have used a transactional JMS topic to have a real transactional behaviour. Let's keep it simple for this example.

On Flex side, you then have to add the data observer component in the main MXML:

grails-app/views/GDSExample.mxml :
            import org.granite.tide.data.DataObserver;

// Register a data observer component with the name of the topic
Spring.getInstance().addComponent("bookManagerTopic", DataObserver);
// Binds the subscribe and unsubscribe methods of the component to the application login/logout events
Spring.getInstance().addEventObserver("org.granite.tide.login", "bookManagerTopic", "subscribe");
Spring.getInstance().addEventObserver("org.granite.tide.logout", "bookManagerTopic", "unsubscribe");

[In]
public var bookManagerTopic:DataObserver;

An interesting thing here is how you can bind a Tide event to any Tide component method, allowing for a complete decoupling between the component and the application.

Now if you open two browsers on the Flex application (in different http sessions, so for example you can try with one Firefox and one Internet Explorer, or better on two different machines), you can check that updates saved on one browser are (almost) immediately visible on the other.

If you look at the console, you will most likely see a lot of error stack traces concerning some RetryRequest error. This has something to do with some Grails filter logging the Jetty continuation retry exception. That's not extremely important, and it will be addressed in a later release. These configuration aspects of Gravity and other advanced features will also be improved to avoid having many places where to change configuration and centralize most of it in the GraniteDSConfig.groovy file.


Now we've seen a simple use of most features of GraniteDS. The complete project is available [here|http://www.graniteds.org/confluence/download/attachments/9371677/gdsexample-0.3.zip] and can be unzipped after having created the project and installed the various plugins (also remember to create roles and user otherwise you won't be able to log in).
We could have implemented the server side with any other Java server technology supported out-of-the-box by GDS/Tide (JBoss Seam, Spring, EJB3) with minimal change on the Flex side, but the Grails/GraniteDS combination seems a good choice to quickly develop Flex applications with a Java server backend.