Granite Data Services Sun, 20 Oct 2013 17:54:33 +0000 en hourly 1 http://wordpress.org/?v=3.1.3 Migrating from GraniteDS 3.0.0.M3 to 3.0.0.RC1 (JavaFX) http://feedproxy.google.com/~r/GraniteDataServices/~3/uZlc0FwpdMA/ /blog/2013/10/15/migrating-from-graniteds-3-0-0-m3-to-3-0-0-rc1-javafx/#comments Tue, 15 Oct 2013 09:50:18 +0000 Franck /?p=2165 There have been once again some changes in the client APIs which will require changing your code.

Package names

Several packages have been renamed to a more consistent convention under the prefix ‘org.granite.client.javafx’. Usually a simple ‘Optimize imports’ or search/replace in your IDE should be enough to do the necessary changes.

ChannelFactory API

The ChannelFactory API has been slighly changed to allow different channel types:

ChannelFactory channelFactory = new JMFChannelFactory();
channelFactory.start();
MessagingChannel channel = channelFactory.newMessagingChannel("long-polling",
    "longPollingChannel", "http://localhost:8080/gravityamf/amf.txt")
Consumer consumer = new Consumer(channel, "stocks", "europe");

For a websocket channel, you will also have to configure the transport:

ChannelFactory channelFactory = new JMFChannelFactory();
channelFactory.setMessagingTransport("websocket",
    new JettyWebSocketTransport());
channelFactory.start();
MessagingChannel channel = channelFactory.newMessagingChannel("websocket",
    "websocketChannel", "ws://localhost:8080/myapp/gravityamf/amf")
Consumer consumer = new Consumer(channel, "stocks", "europe");

For a UDP channel, assuming the granite-client-java-udp.jar library is present:

ChannelFactory channelFactory = new JMFChannelFactory();
channelFactory.start();
MessagingChannel channel = channelFactory.newMessagingChannel("udp",
    "udpChannel", "http://localhost:8080/myapp/gravityamf/amf.txt")
Consumer consumer = new Consumer(channel, "stocks", "europe");

Of course you can mix all these channels and get them from the same ChannelFactory.

Additionally you can use the ServerApp API if you don’t want to build the url yourself:

ServerApp serverApp = new ServerApp("/myapp", false, "localhost", 8080);
ChannelFactory channelFactory = new JMFChannelFactory();
channelFactory.start();
MessagingChannel lpChannel = channelFactory.newMessagingChannel("long-polling",
    "longPollingChannel", serverApp);
MessagingChannel wsChannel = channelFactory.newMessagingChannel("websocket",
    "websocketChannel", serverApp);
MessagingChannel udpChannel = channelFactory.newMessagingChannel("udp",
    "udpChannel", serverApp);
ServerSession API

The ServerSession API has also been updated.

You can now use the ServerSession to build Consumer and Producer objects:

ServerSession serverSession = new ServerSession("/myapp", "localhost", 8080);
serverSession.setMessagingTransport("websocket",
    new JettyWebSocketTransport());
serverSession.start();
Consumer lpConsumer = serverSession.getConsumer("long-polling",
    "stocks", "europe");
Consumer wsConsumer = serverSession.getConsumer("websocket",
    "stocks", "usa");
Consumer udpConsumer = serverSession.getConsumer("websocket",
    "stocks", "asia");

The GraniteDS Team.

]]>
/blog/2013/10/15/migrating-from-graniteds-3-0-0-m3-to-3-0-0-rc1-javafx/feed/ 0 /blog/2013/10/15/migrating-from-graniteds-3-0-0-m3-to-3-0-0-rc1-javafx/
Migrating from GraniteDS 3.0.0.M3 to 3.0.0.RC1 (Flex) http://feedproxy.google.com/~r/GraniteDataServices/~3/qDcnm8F6O30/ /blog/2013/10/15/migrating-from-graniteds-3-0-0-m3-to-3-0-0-rc1-flex/#comments Tue, 15 Oct 2013 09:50:09 +0000 Franck /?p=2162 The ServiceInitializer API has been removed and replaced by ServerSession and IServerApp. The following code:

Tide.getInstance().getContext().serviceInitializer
    = new DefaultServiceInitializer('/myapp');

Should be replaced by:

Tide.getInstance().mainServerSession.serverApp = new SimpleServerApp('/myapp');

Why this (apparently) cosmetic change ? The difference is that you can now create other ServerSession objects for other server applications (other context roots, other server urls, …). Implicit service proxies will still be routed to the main ServerSession, if you want to connect to another server, you will have to attach the service proxies to the other ServerSession:

Tide.getInstance().mainServerSession.serverApp = new SimpleServerApp('/myapp');
var appSession2:ServerSession =
    new ServerSession('/myapp2', false, 'myserver2', '8080');
Tide.getInstance().getContext().appSession2 = appSession2;
Tide.getInstance().getContext().myService2 = new MyService2(appSession2);

// Service proxy for MyService on http://localhost:8080/myapp
[Inject]
public var myService:MyService;

// Service proxy for MyService2 on http://myserver2:8080/myapp2
[Inject]
public var myService2:MyService2;

The GraniteDS Team.

]]>
/blog/2013/10/15/migrating-from-graniteds-3-0-0-m3-to-3-0-0-rc1-flex/feed/ 0 /blog/2013/10/15/migrating-from-graniteds-3-0-0-m3-to-3-0-0-rc1-flex/
GraniteDS 3.0.0.RC1 is out http://feedproxy.google.com/~r/GraniteDataServices/~3/QxeUTbyGG-U/ /blog/2013/10/15/graniteds-3-0-0-rc1-is-out/#comments Tue, 15 Oct 2013 09:50:00 +0000 Franck /?p=2114 Granite Data Services 3.0.0.M3 is out and available for download . Maven artifacts are also available from the Maven central repository .

What's New?

The most visible change in this release is the new build process which is now entirely based on Gradle: instead of several Git repositories (one for each module), you will now find everything under a single repository called . The documentation is now based on , a much lighter solution than our previous docbook generation. This heavy lifting of our build tools was long needed and should help us to bring more frequent releases. It will also make much easier to contribute to the project, either code or docs by simple pull requests in github.

On the technical features side, here are some highlights:

  • UDP support for real-time messaging (Flex, Java, JavaFX, Android).
  • Incremental serialization (aka ChangeSets), which only transmits a set of modified properties instead of complete object graphs (Flex only for now).
  • The Tide ServerSession API, previously only available on Java/JavaFX clients has been ported to Flex and allows Flex/Tide clients to connect to multiple servers.
  • The JMF protocol can now be used with WebSockets for very compact and fast serialization of messages (Java/JavaFX only).
  • You can serialize AS3 Vectors from the server with the help of specific annotations (Flex only).

Of course, many other bug fixes and improvements are also packaged in this release and you can find the complete list on Jira.

What's Next?

3.0.0.GA

We plan to release the final 3.0.0 version at early/mid November. The APIs are now final and we won’t add any new feature, but focus only on bug fixes, documentation, examples and tutorials. You are thus invited to use the RC1 as soon as possible and report quickly anything that looks like a bug.

3.1.0

The next major release will introduce at least these new features or improvements:

  • WebSocket 1.0 (JSR 356) client and server support for Java/JavaFX.
  • Incremental serialization (ChangeSets) for Java/JavaFX clients.
  • Improved Android support with data binding.
  • JSON support for HTML/JavaScript clients.

Licensing

You can refer to the previous post for the release announcement of 3.0.0.M3 for the new licensing of the different modules. To summarize, all server and basic libraries are now released under the LGPL 2.1 (previously LGPL 2.0), and all advanced client libraries and UDP client and server integration are released under a dual GPL 3.0 / commercial license.

All current users of GraniteDS can send a request at [email protected] for a free one year ‘early bird license’ for all these libraries until the final 3.0.0.GA release.

Migrating from GraniteDS 3.0.0.M3 to 3.0.0.RC1

There have been once again some changes in the client APIs which will require some changes in your code. Be sure to check our posts about “Migrating from GraniteDS 3.0.0.M3 to 3.0.0.RC1 (Flex)” and “Migrating from GraniteDS 3.0.0.M3 to 3.0.0.RC1 (JavaFX)”.

Comments are welcome,

The GraniteDS Team.

]]>
/blog/2013/10/15/graniteds-3-0-0-rc1-is-out/feed/ 3 /blog/2013/10/15/graniteds-3-0-0-rc1-is-out/
Bidirectional Serialization of AS3 Vectors http://feedproxy.google.com/~r/GraniteDataServices/~3/oQMookSKzsc/ /blog/2013/08/27/bidirectional-serialization-of-as3-vectors/#comments Tue, 27 Aug 2013 08:12:43 +0000 Franck /?p=2104 GraniteDS 3.0.0.RC1 (not released yet, but the Vector serialization feature is implemented in the trunk) allows bidirectional serializations of AS3 Vectors (Flex <--> Java). Vectors serialization was only working from Flex to Java but not the other way (same limitation in other Flex / Java backend such as BlazeDS and LCDS AFAIK).

A sample application on Github is a ready to use demonstration of this new feature: follow the instructions on the (README.md).

]]>
/blog/2013/08/27/bidirectional-serialization-of-as3-vectors/feed/ 1 /blog/2013/08/27/bidirectional-serialization-of-as3-vectors/
JavaFX Tutorial Updated http://feedproxy.google.com/~r/GraniteDataServices/~3/ltd-_RLbsyo/ /blog/2013/08/23/javafx-tutorial-updated/#comments Fri, 23 Aug 2013 12:30:21 +0000 Franck /?p=2097 Following the 3.0.0.M3 release, you can find an updated tutorial about GraniteDS / JavaFX data management on Github : just follow the instructions displayed on the landing page of the project (README.adoc).

]]>
/blog/2013/08/23/javafx-tutorial-updated/feed/ 0 /blog/2013/08/23/javafx-tutorial-updated/
Introducing JMF (Java Messaging Format) http://feedproxy.google.com/~r/GraniteDataServices/~3/fSAahSulkdU/ /blog/2013/08/22/introducing-jmf-java-messaging-format/#comments Thu, 22 Aug 2013 12:01:56 +0000 Franck /?p=1905 GraniteDS release 3.0.0.M2 introduced a new serialization format for Java clients which is largely inspired by (Action Message Format version 3). It is called JMF, which stands for Java Messaging Format, and it is now used, starting with GraniteDS 3.0.0.M3, as the standard serialization format between Java / JavaFX / Android client applications and GraniteDS servers.

Why JMF?

Basically because we love AMF but AMF isn’t good for Java client applications: with AMF, for example, all Java numeric types (byte, short, int, long, float, double, etc.) are serialized either as ActionScript int or Number, which leads to useless conversions in the context of a Java to Java data exchange and can even lead to a loss of precision (large long values cannot be represented as Numbers without truncation). Furthermore, all collections (java.util.*) are normalized to a single ArrayCollection type, BigDecimals are converted to Numbers, enum values to nothing standard (ActionScript has no enum types), etc.

Using AMF with Java clients is certainly possible, and we do have AMF support for Java clients in GraniteDS, but it is pointlessly complicated and, as a result, requires a full bunch of ugly workarounds.

That said, JMF is largely inspired by AMF and designed with the following goals in mind:

  • Compactness: serialized data must be as small as possible (even more than with AMF).
  • Completeness: circular references must be correctly handled, everything that should be serialized must be serialized.
  • Accuracy: no trans-typing, no pointless conversions, unless explicitly wanted.
  • Extensibility: it must be possible to plug dedicated codecs for specific data types.
  • Observability: data flow must be understandable by programs that have no knowledge of what is serialized.
  • Security: only data that are meant to be serialized must be serialized.
  • Speed: serialization must be as fast as possible (but without breaking the previous goals).

How to Use JMF?

You basically need to do nothing to use JMF: HTTP headers of serialized data, unless you have configured your Java client application to do otherwise, use a specific mime-type which tells the GraniteDS backend to switch to JMF instead of AMF serialization for the incoming request and corresponding response.

The JMF mime-type is currently “application/x-jmf+amf”, the “+amf” part stating that the messages inside the request are instances of flex.messaging.messages.Message (possibly enclosed in an AMF0 envelop). We are considering to use our own message envelops when using JMF and the mime-type is likely going to be “application/x-jmf” only in later versions.

The good thing with this mime-type switch mechanism is that you can have Java (or even JavaFX / Android) client applications using the exact same backend used by Flex client applications: there is nothing specific to configure on the server-side.

How does JMF Compare to AMF and Standard Java Serialization in Terms of Size?

With small messages such as a ping message (which purpose is to make sure that the server is alive and to negotiate serialization options), JMF vs. AMF vs. Standard Java Serialization (ie. java.io.ObjectOutputStream) gives these results:

  • JMF: 113 bytes.
  • AMF: 261 bytes.
  • Java Serialization: 894 bytes.

With bigger messages (a list of person with collections of contacts), you can typically get these results:

  • JMF: 2115 bytes.
  • AMF: 2749 bytes.
  • Java Serialization: 5278 bytes.

As a general result, but without conducting an extensive benchmark, we have found that:

  • The size of JMF encoded data can be up to 1/3 of the size of AMF encoded data.
  • The size of JMF encoded data can be up to 1/8 of the size of Java serialized data.
  • JMF encoding is always smaller than AMF or standard Java serialization encoding.

What about Security?

AMF serialization doesn’t state anything about what is (de)serializable or not: this lack of control can lead to a security breach that was discovered by Wouter Coekaerts and reported . Granite Data Services fixed this issue by adding a AMF3DeserializerSecurizer that controls which class can be instantiated at deserialization time (see this post about GraniteDS 2.2.1.GA).

Unlike AMF but just like the standard Java serialization, JMF only encodes primitive types and classes that implement java.io.Serializable. As a result, the above vulnerability doesn’t affect JMF, unless classes that shouldn’t be serialized implement (which is a design bug) java.io.Serializable.

Is there a public JMF specification?

Not yet. However, you are free to have look at the implementation, it’s fully open-source, just like the rest of the GraniteDS platform.

]]>
/blog/2013/08/22/introducing-jmf-java-messaging-format/feed/ 1 /blog/2013/08/22/introducing-jmf-java-messaging-format/
GraniteDS 3.0.0.M3 is out http://feedproxy.google.com/~r/GraniteDataServices/~3/YRotH5PoaXw/ /blog/2013/08/20/graniteds-3-0-0-m3-is-out/#comments Tue, 20 Aug 2013 15:44:48 +0000 Franck /?p=2011 Granite Data Services 3.0.0.M3 is out and available for download . Maven artifacts are also available through Maven central repositories .

This new release is a very important milestone on our way to the final 3.0.0.GA version, both from technical and business point-of-views:

What's new on the technical side?

Beside the usual , the 3.0.0.M3 version of our platform brings the following new features (some of them were already present in previous milestones, though at an early development stage):

  • JavaFX support is maturing: you can benefit from all advanced features which were only available in Tide / Flex and a more efficient serialization protocol than AMF3 for Java clients (JMF – Java Messaging Format)
  • Android support is brand new in this release: you can already develop full featured mobile applications connected to a GraniteDS backend and this new feature also relies on the improved JMF protocol.
  • All Java client libraries are now built using : we are moving the build process of the all platform to Gradle and are planning to “gradlelize” everything before the final 3.0.0.GA.

Our focus for the next release (likely a 3.0.0.RC1) will be on Android, UDP-based real-time messaging and JSON support.

Documentation and tutorials need an update, we will post new resources as soon as they are available.

What's new on the business side?

GraniteDS has always been an avid supporter of open source software. Our business model so far has been to sale yearly subscriptions to an enterprise version of our platform, embedding some very advanced features absent from the community version, together with an access to professional support services.

While we have had and still have some success with this model, it is clearly targeted to mid / big organizations developing critical applications: most of our users are simply using the community version, either because they don’t need any support or can’t afford to pay for our enterprise subscription packages. As a result, generated revenues are below what we need to move to the next step and truly develop and promote GraniteDS in the long run.

After considering different options, we have decided to move to the following business model:

  • GraniteDS stays open source and is even more open source than ever: the few advanced features which were not available in the community platform are going to be in the same Github repository, just like everything else (planned for the 3.0.0.RC1).
  • Most of the platform is still released under the license, though we are going to move it to (version 2.0 is now largely outdated and so to say historical); Advanced libraries (Granite Android and JavaFX Libraries in 3.0.0.M3) are released under the license, with the option of purchasing a commercial license.
  • Support packages are still available, alongside the new commercial licenses: they are two different kind of things and the only purpose of the commercial license is to enable people to use our GPL 3.0 software without the legal requirement to distribute their products under a compatible open source license. However, commercial license fees will be waived for our current customers, provided they have a up-to-date yearly-subscription to one of our enterprise package and as long as they renew this subscription.
  • Commercial licenses are affordable and even free for early birds: see our pricing.

We sincerely hope that you will understand this move, which is, from our point-of-view, pretty fair: open source and free software is great and must stay free for people developing open source software as well. For those who develop commercial software, and unless they can live with the basic features of our platform, an affordable and predictable financial contribution is now asked.

Before asking us any question about this new business model / commercial license, please read our licensing FAQ and the license itself.

So, what's going to be LGPL and GPL?

In the 3.0.0.M3 release, only the Granite Android and JavaFX Libraries are released under GPL 3.0, with the option of purchasing a commercial license: this includes Tide for Java client applications, but the basic Java client features (eg. low-level remoting and real-time messaging) are still LGPL. Everything else in the platform is LGPL (server-side libraries, Flex client libraries, code generation tools).

Starting with the next 3.0.0.RC1 release, two other products will be available under the same dual-licensing scheme (GPL 3.0 / GraniteDS SLA):

  • Granite Advanced Flex Library: former enterprise-only features (such as differential serialization), ActionScript3 big numbers implementation, Flex validation (Bean Validation implementation on the Flex side) and Tide for Flex.
  • Granite UDP Libraries: UDP-based real-time messaging (for Flex and Java clients).

Again, we are and will be offering free commercial licenses for early birds, so it is not going to hurt your wallet if you fast enough (the final 3.0.0.GA won’t be released before early October at the earliest).

Comments are welcome, The GraniteDS Team.

]]>
/blog/2013/08/20/graniteds-3-0-0-m3-is-out/feed/ 3 /blog/2013/08/20/graniteds-3-0-0-m3-is-out/
GraniteDS 3.0.0.M2 is out http://feedproxy.google.com/~r/GraniteDataServices/~3/P1sKOffxPOk/ /blog/2013/05/17/graniteds-3-0-0-m2-is-out/#comments Fri, 17 May 2013 14:35:13 +0000 William /?p=1893 Granite Data Services 3.0.0.M2 is out and available for download . Maven artifacts are also available through Maven central repositories .

This release is the second milestone for GraniteDS 3.0 and mostly contains bug fixes and improvements (see the complete changelog on JIRA ).

This release introduces the new experimental JMF protocol that will replace the AMF protocol when serializing objects between Java servers and Java clients. More details on this in a later post.

It also includes many improvements and bug fixes on the JavaFX client libraries, like integration with CDI/Weld on the client and a view scope for Spring and CDI. The JavaFX tutorial will soon be updated to demonstrate these features. Thanks a lot to Daniele Renda who was extremely helpful improving/testing/using these JavaFX libraries on a real-world application.

Thanks also to Erguder Bekrek, Thomas Ott and clemieux for their patches and help on the Flex libraries.

The tutorials and Maven archetypes will be upgraded to 3.0 M2 soon.

Important note:
GraniteDS 3.0.0.M1 has introduced support for a websocket transport in both Flex and Java clients. As this is still an experimental feature not yet released in a stable release and not really used by a lot of people, we are strongly considering moving this feature to the enterprise version of GraniteDS and complete the development of this feature only in the enterprise version. Thus all websocket related source files will likely be removed from github very soon.

Upgrading from 2.x to 3.0.x :

  • You must now use the 3.0 xsd for Seam and Spring XML configurations : http://www.graniteds.org/config http://www.graniteds.org/public/dtd/3.0.0/granite-config-3.0.xsd
  • The flex-filter element must be replaced by server-filter
  • When using Servlet 3 annotation configuration, @FlexFilter must be replaced by @ServerFilter

You can find the reference documentation here :

  • Flex:
  • Java/JavaFX:

The Eclipse tooling of GDS 3.0 (Gas3 builder and wizard) can be updated from our Eclipse .

]]>
/blog/2013/05/17/graniteds-3-0-0-m2-is-out/feed/ 3 /blog/2013/05/17/graniteds-3-0-0-m2-is-out/
Thoughts about JavaFX and Bean Validation http://feedproxy.google.com/~r/GraniteDataServices/~3/GyhUGjysdpA/ /blog/2012/11/29/thoughts-about-javafx-and-bean-validation/#comments Thu, 29 Nov 2012 14:46:25 +0000 William /?p=1853 Coming from Adobe Flex, it has been quite a surprise to discover that JavaFX 2 does not provide anything particular concerning input validation. However we can make use of the existing UI event model to execute validations on the text inputs.

In a first naive implementation, we could simply think of an event handler that will manually validate the input on the fly :

textField.setOnKeyTyped(new EventHandler<KeyEvent>() {
    @Override
    public void handle(KeyEvent event) {
        String text = ((TextInputControl)event.getTarget()).getText());
        if (text.length() < 2 || text.length() > 25)
            ((Node)event.getTarget()).setStyle("-fx-border-color: red");
        else
            ((Node)event.getTarget()).setStyle("-fx-border-color: null");
    }
});

Here we have processed the validation manually, but we now would like to be able to leverage the Bean Validation API to simplify this. Bean Validation is not meant to be applied on individual values (as the value of the input) but on data beans holding the validation constraint annotations. However in general a text field would be used to populate a data bean, so we could do something like this :

public class Person {

    @Size(min=2, max=20)
    private String name;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

Having defined this bean, we could change the previous code by this :

final Validator validator = validatorFactory.getValidator(Person.class);

textField.setOnKeyTyped(new EventHandler<KeyEvent>() {
    @Override
    public void handle(KeyEvent event) {
        String text = ((TextInputControl)event.getTarget()).getText());
        Set<ConstraintViolation<Person>> violations =
            validator.validateValue(Person.class, "name", text);
        if (!violations.isEmpty())
            ((Node)event.getTarget()).setStyle("-fx-border-color: red");
        else
            ((Node)event.getTarget()).setStyle("-fx-border-color: null");
    }
});

This is definetely better, but we still have to do this for each and every input field, which is not very convenient.

One possibility would be to write a reusable validation handler, like this :

public class ValidationEventHandler<T> extends EventHandler<KeyEvent> {

    private Class<T> beanClass;
    private String propertyName;

    public ValidationEventHandler(Class<T> beanClass, String propertyName) {
        this.beanClass = beanClass;
        this.propertyName = propertyName;
    }

    @Override
    public void handle(KeyEvent event) {
        String text = ((TextInputControl)event.getTarget()).getText());
        Set<ConstraintViolation<T>> violations =
            validator.validateValue(beanClass, propertyName, text);
        if (!violations.isEmpty())
            ((Node)event.getTarget()).setStyle("-fx-border-color: red");
        else
            ((Node)event.getTarget()).setStyle("-fx-border-color: null");
    }
}

Which would lead to this on each field :

textField.setOnKeyTyped(
    new ValidationEventHandler<Person>(Person.class, "name"));

Still we have to define this handler on each input field, so it can be configured with the bean class and property.

If we want to go a bit further, we will need a generic way to determine the links between input fields and beans. This link is application-specific, but if the application uses data binding, we can maybe find this information.

textField.text().bindBidirectional(person.name());

Unfortunately, there does not seem to be any generic reflection API on property bindings. Using the following extremely ugly hack we can find the target of a particular binding:

private Property<?> lookupBindingTarget(Property<?> inputProperty) {
    try {
        Field fh = inputProperty.getClass().getDeclaredField("helper");
        fh.setAccessible(true);
        Object helper = fh.get(inputProperty);
        Field fcl = helper.getClass().getDeclaredField("changeListeners");
        fcl.setAccessible(true);
        Object changeListeners = fcl.get(helper);
        if (changeListeners != null && Array.getLength(changeListeners) > 0) {
            ChangeListener<?> cl =
                (ChangeListener<?>)Array.get(changeListeners, 0);
            try {
                Field fpr = cl.getClass().getDeclaredField("propertyRef2");
                fpr.setAccessible(true);
                WeakReference<?> ref= (WeakReference<?>)fpr.get(cl);
                Property<?> p = (Property<?>)ref.get();
                return p;
            }
            catch (NoSuchFieldException e) {
                 log.debug("Field propertyRef2 not found on " + cl, e);
                 return null;
            }
        }
        log.debug("Could not find target for property %s", inputProperty);
	return null;
    }
    catch (Exception e) {
        log.warn(e, "Could not find target for property %s", inputProperty);
        return null;
    }
}

Using this hack, We are now able to determine automatically the beans to validate for all input fields in a form. This is exactly what the FormValidator class provided by GraniteDS is built to do. It scans all inputs in a JavaFX container and add listeners on them. It then calls the corresponding validator on the target bean and propagates the constraint violations to the input by dispatching a particular event of type ValidationResultEvent.

Using FormValidator with data binding, you would simply have to do this:

FormValidator formValidator = new FormValidator();
formValidator.setForm(formContainer);

Then all bound input fields will be validated on-the-fly depending on the Bean Validation constraints defined on the target beans. The FormValidator dispatches validation events on the container so you can react and display hints and error messages to the user:

formContainer.addEventHandler(ValidationResultEvent.ANY,
    new EventHandler<ValidationResultEvent>() {
    @Override
    public void handle(ValidationResultEvent event) {
        if (event.getEventType() == ValidationResultEvent.INVALID)
            ((Node)event.getTarget()).setStyle("-fx-border-color: red");
        else if (event.getEventType() == ValidationResultEvent.VALID)
            ((Node)event.getTarget()).setStyle("-fx-border-color: null");
    }
});

Note that for now, the FormValidator requires that the data bean implements the GraniteDS interface DataNotifier which basically means that the bean is able to dispatch JavaFX events. This requirement could be possibly removed in a future release.

public interface DataNotifier extends EventTarget {
    public <T extends Event> void addEventHandler(EventType<T> type,
        EventHandler<? super T> handler);
    public <T extends Event> void removeEventHandler(EventType<T> type,
        EventHandler<? super T> handler);
}

The bean would then be implemented like this :

public class Person {

    private EventHandlerManager __handlerManager
        = new EventHandlerManager(this); 

    @Override
    public EventDispatchChain buildEventDispatchChain(EventDispatchChain t) {
        return t.prepend(__handlerManager);
    }

    public <T extends Event> void addEventHandler(EventType<T> type,
        EventHandler<? super T> handler) {
        __handlerManager.addEventHandler(type, handler);
    }
    public <T extends Event> void removeEventHandler(EventType<T> type,
        EventHandler<? super T> handler) {
        __handlerManager.removeEventHandler(type, handler);
    }
}

This is once again not ideal but this can be easily extracted in an abstract class.

The validation step of the FormValidator is as follows:

Set<ConstraintViolation<Object>> allViolations
    = validatorFactory.getValidator().validate((Object)entity, groups);

Map<Object, Set<ConstraintViolation<?>>> violationsMap
    = new HashMap<Object, Set<ConstraintViolation<?>>>();
for (ConstraintViolation<Object> violation : allViolations) {
    Object rootBean = violation.getRootBean();
    Object leafBean = violation.getLeafBean();
    Object bean = leafBean != null
        && leafBean instanceof DataNotifier ? leafBean : rootBean;

    Set<ConstraintViolation<?>> violations = violationsMap.get(bean);
    if (violations == null) {
        violations = new HashSet<ConstraintViolation<?>>();
        violationsMap.put(bean, violations);
    }
    violations.add(violation);
}

for (Object bean : violationsMap.keySet()) {
    if (bean instanceof DataNotifier) {
        ConstraintViolationEvent event =
            new ConstraintViolationEvent(
                ConstraintViolationEvent.CONSTRAINT_VIOLATION,
                violationsMap.get(bean)
            );
        Event.fireEvent((DataNotifier)bean, event);
    }
}

Obviously that would be easier if we could plug in the bean validation lifecycle of each bean and fire the events during the validation itself, itself of doing this kind of postprocessing.

Conclusion

We have already most of the building blocks to integrate JavaFX and Bean Validation but it is definitely not as smooth as it should be.

The main pain points are the following :

  1. No way to plug in the validation lifecycle of Bean Validation
  2. No easy-to-use JavaFX UI components to display error popups
  3. No reflection API on the JavaFX data binding
  4. No simple event dispatch facility for beans

In fact, the two last points could be totally unneccessary if the validation was directly built-in the data binding feature, much like converters. It is probably possible to build something like a ValidatableStringProperty, we will let this for a future post.

]]>
/blog/2012/11/29/thoughts-about-javafx-and-bean-validation/feed/ 2 /blog/2012/11/29/thoughts-about-javafx-and-bean-validation/
Granite Data Services 3.0.0.M1 and Apache Flex 4.8.0 http://feedproxy.google.com/~r/GraniteDataServices/~3/BP2NzOFRz8I/ /blog/2012/11/28/granite-data-services-3-0-0-m1-and-apache-flex-4-8-0/#comments Wed, 28 Nov 2012 17:02:13 +0000 Franck /?p=1807 The last 3.0.0.M1 release of GraniteDS (see announcement here) is compatible with the last 4.8.0 release of . The two GraniteDS SWCs compiled with Apache Flex 4.8.0 can be found here:

We didn’t have time to include those SWCs in our binary release, but we will include them in the next upcoming release (or milestone). Keep in mind that GraniteDS 3.0.0.M1 is still in a beta stage and that you can experience unexpected gitchs (report them in our ).

Build them yourself

If you want to build yourself GraniteDS SWCs against the Apache Flex release, proceed as follows:

1. Download Apache Flex(R) 4.8.0 and unzip/untar it somewhere (say “flex48″). 2. Spawn a command shell and type (Apache Ant must be installed):

cd flex48/framework
ant thirdparty-downloads

Answer yes to all questions.

3. Download GraniteDS 3.0.0.M1 sources and unzip it somewhere (say “gds30″). 4. Edit env.properties and env45.properties, and fix the FLEX_HOME variable (absolute path to flex48). 5. Then run:

cd gds30
ant -f build.xml
ant -f build-flex45.xml

The first build will fail after building granite-essentials.swc, when trying to compile granite.swc: just ignore that error and proceed with build-flex45.xml.

The two GraniteDS SWCs buit against Apache Flex 4.8.0 should be in the build directory (granite-essentials.swc and granite-flex45.swc): just rename them to granite-essentials-flex48.swc and granite-flex48.swc, you’re done.

]]>
/blog/2012/11/28/granite-data-services-3-0-0-m1-and-apache-flex-4-8-0/feed/ 0 /blog/2012/11/28/granite-data-services-3-0-0-m1-and-apache-flex-4-8-0/