mardi 18 juin 2013

Une jauge avec D3.js

Si vous ne connaissez pas D3.js je vous invite à jeter un oeil sur cette librairie javascript qui fait des merveille: http://d3js.org/
Pour résumer d3.js facilite le binding entre des données et le dom d'une page html (balise SVG inclu).

 Prenez le temps de parcourir les exemples, certains sont assez bluffant de par le rendu visuel mais aussi souvent de par la simplicité du code.
 En ce qui me concerne dans un cadre professionnel  j'ai déja fait quelques graph avec un peu d’interactions, et j'ai aussi utilisé le dendrogram (http://bl.ocks.org/mbostock/4063570) en lui ajoutant lui aussi de l'interaction, le gros avantage du SVG c'est qu'il s'agit de balises au meme titre qu'un bon vieux DIV, donc au final elles gèrent les mêmes événements et les mêmes CSS, contrairement a la balise canvas qui elle est une boite noir.
Dernièrement je me suis amusé à détourner une jauge pour lui donner un aspect, a mon sens, plus fun et plus visuel.
 Je suis donc parti de cet exemple :
 http://bl.ocks.org/msqr/3202712
 Pour en arriver à ceci :

<script src="http://d3js.org/d3.v2.min.js" type="text/javascript"></script>
 <style>
 body {
  font-family:  Helvetica, Arial, sans-serif;
  margin: 32px;
 }
 
 #power-gauge g.arc {
  fill: steelblue;
 }

 #power-gauge g.pointer {
  fill: #e85116;
  stroke: #b64011;
 }
 
 #power-gauge g.label text {
  text-anchor: middle;
  font-size: 14px;
  font-weight: bold;
  fill: #666;
 }
 
 #power-gauge .valueText {
  text-anchor: middle;
  font-size: 30px;
  font-weight: bold;
  fill: blue;
 }
 
 #power-gauge .titleText {
  text-anchor: middle;
  font-size: 20px;
  font-weight: bold;
  fill: #FFF;
 }
 
</style>


<div id="power-gauge">
</div>
<script>
var gauge = function(container, configuration) {
 var that = {};
 this.config = {
  size      : 200,
  clipWidth     : 200,
  clipHeight     : 110,
  ringInset     : 20,
  ringWidth     : 20,
  
  minValue     : 0,
  maxValue     : 10,
  
  minAngle     : -90,
  maxAngle     : 90,
  
  transitionMs    : 1000,
  
  majorTicks     : 5,
  labelFormat     : d3.format(',g'),
  labelInset     : 10,
  title      : 'Title',
  
  selectColor     : function(d){
          if(d>8){
           return 'red';
          }else if(d>6 && d<=8){
           return 'orange'
          }else if(d>4 && d<=6){
           return 'yellow';
          }else if(d>2 && d<=4){
           return  'blue';
          }else if(d>0 && d<=2){
           return 'green';
          }
         }
 };
 
 
 var range = undefined;
 var r = undefined;

 var value = 0;
 
 var svg = undefined;
 var arc = undefined;
 var scale = undefined;
 var ticks = undefined;
 var tickData = undefined;
 
 var value = {previous:0, value:0};

 var donut = d3.layout.pie();
 
 this.deg2rad = function (deg) {
  return deg * Math.PI / 180;
 }
 
 this.newAngle =  function(d) {
  var ratio = scale(d);
  var newAngle = this.config.minAngle + (ratio * range);
  return newAngle;
 }
 
 this.configure = function(configuration) {
  var that = this;
  var prop = undefined;
  for ( prop in configuration ) {
   this.config[prop] = configuration[prop];
  }
  
  range = this.config.maxAngle - this.config.minAngle;
  r = this.config.size / 2;

  // a linear scale that maps domain values to a percent from 0..1
  scale = d3.scale.linear()
   .range([0,1])
   .domain([this.config.minValue, this.config.maxValue]);
   
  ticks = scale.ticks(this.config.majorTicks);
  tickData=[1];
  console.log("Tickdata:"+tickData);
  this.arc = d3.svg.arc()
   .innerRadius(r - this.config.ringWidth - this.config.ringInset)
   .outerRadius(r - this.config.ringInset)
   .startAngle(function(d, i) {
    var ratio = d * i;
    var value =that.deg2rad(that.config.minAngle + (ratio * range));
    console.log('start angle:'+value);
    return value;
   })
   .endAngle(function(d, i) {
    var ratio = d * (i+1);
    console.log('minAngle='+that.config.minAngle+', ratio='+ratio+' , range='+range);
    var value =that.deg2rad(that.config.minAngle + (ratio * range));
    console.log('end angle:'+value);
    return that.deg2rad(that.config.minAngle + (ratio * range));
   });
   
  this.arcPointer = d3.svg.arc()
   .innerRadius(r - this.config.ringWidth - this.config.ringInset)
   .outerRadius(r - this.config.ringInset)
   .startAngle(function(d, i) {
    return that.deg2rad(-90);
   })
   .endAngle(function(d, i) {
    var ratio = scale(d);
    return that.deg2rad(that.config.minAngle + (ratio * range));
   });
 }
 
 this.centerTranslation = function centerTranslation() {
  return 'translate('+r +','+ r +')';
 }
 
 this.isRendered = function() {
  return (svg !== undefined);
 }

 
 this.render = function(newValue) {
  var that = this;
  svg = d3.select(container)
   .append('svg:svg')
    .attr('class', 'gauge')
    .attr('width', this.config.clipWidth)
    .attr('height', this.config.clipHeight);
  
  var centerTx = this.centerTranslation();
  
  this.arcs = svg.append('g')
    .attr('class', 'arc')
    .attr('transform', centerTx);
  
  this.arcs.selectAll('path')
    .data([1])
   .enter().append('path')
    .attr('fill', '#FFF')
    .attr('d', this.arc);
  
  var lg = svg.append('g')
    .attr('class', 'label')
    .attr('transform', centerTx);
  lg.selectAll('text')
    .data(ticks)
   .enter().append('text')
    .attr('transform', function(d) {
     var ratio = scale(d);
     var newAngle = that.config.minAngle + (ratio * range);
     return 'rotate(' +newAngle +') translate(0,' +(that.config.labelInset - r) +')';
    })
    .text(this.config.labelFormat);

   
  this.arcs2 = svg.append('g')
   .attr('class', 'arc')
   .attr('transform', centerTx);
  

  
  var textValue = svg.append('g')
    .attr('class', 'arc')
    .attr('transform', centerTx);
    
  var titleValue = svg.append('g')
    .attr('transform', 'translate('+r +','+ (r +25)+')');
    
  this.valueTextCenter = textValue.append('text').attr('class','valueText').text('0,0');
  this.titleTextCenter = titleValue.append('text').attr('class','titleText').text(this.config.title);
  this.update(newValue === undefined ? 0 : newValue);
 }

 
 this.update = function(newValue, newConfiguration) {
  var that = this;
  if ( newConfiguration  !== undefined) {
   this.configure(newConfiguration);
  }
  value.previous = value.value;
  value.value = newValue;
  this.valueTextCenter.text(newValue.toFixed(0)+"%");
  var ratio = scale(newValue);
  var newAngle = this.config.minAngle + (ratio * range);
  
  indicator = this.arcs2.selectAll('path').data([value.value]);
  indicator.enter().append("svg:path").transition().ease('linear')
  .duration(this.config.transitionMs)
  .attrTween('d', function(a){
   
     var i = d3.interpolate(value.previous, a);
     //this._current = i(0);
     return function(t) {
    return that.arcPointer(i(t));
     };

  });
  indicator.transition()
     .ease("linear")
     .duration(this.config.transitionMs)
     .attrTween("d", function(a){
   
     var i = d3.interpolate(value.previous, a);
     //this._current = i(0);
     return function(t) {
    return that.arcPointer(i(t));
     };

  }).attr('fill',function(d){
   return that.config.selectColor(d);
  });
   

 }


 this.configure(configuration);
 
};
</script>

<script>
function onDocumentReady() {

 
 var powerGauge = new gauge('#power-gauge', {
  size: 300,
  clipWidth: 300,
  clipHeight: 300,
  ringWidth: 30,
  maxValue: 100,
  transitionMs: 1000,
  selectColor: function(d){
      if(d>80){
       return 'red';
      }else if(d>60 && d<=80){
       return 'orange'
      }else if(d>40 && d<=60){
       return 'yellow';
      }else if(d>20 && d<=40){
       return  'blue';
      }else if(d>0 && d<=20){
       return 'green';
      }
     }
 });
 powerGauge.render(); 
 
 function updateReadings() {

  powerGauge.update(Math.random() * 100);
 }
 
 updateReadings();
 setInterval(function() {
  updateReadings();
 }, 3 * 1000);
}

if ( !window.isLoaded ) {
 window.addEventListener("load", function() {
  onDocumentReady();
 }, false);
} else {
 onDocumentReady();
}
</script>

Je suis pas encore un specialiste javascript, j'ai mes bonnes vieilles habitude issue du java, j'ai pas pu m'empecher d'en faire un objet afin de pouvoir l'instancier via un "new" sans vraiment savoir s'il s'agit d'une bonne pratique en javascript.
Evidemment on peut en instancier autant qu'on veut par page.
De plus le code est encore à épurer, mais il est fonctionnel !



jeudi 14 février 2013

Démarrer un projet NodeJS et Express

Je vais dévier un peu du sujet de base de ce blog, qui est censé être complètement dédié à java et a la programmation orienté objet pour parler d'une technologie qui a le vent en poupe.
Cette technologie c'est nodeJS, que je définirais pour le moment comme un serveur d'application pour javascript basé sur l'interpreteur javascript de Chrome.

Le site nodeJS : http://nodejs.org/

Une fois l'installation terminé, on a accès a node.
L'execution d'un programme se fait de la façon suivante :

node monscript.js

Bon c'est pas le but de ce post, mais juste histoire de mettre le pied a l’étrier de ceux qui ne connaîtraient pas du tout nodeJS, voila comment réaliser un petit 'HelloWorld'.
Ouvrez un editeur de texte et tapez la ligne suivante :

console.log('Hello world');
Enregistrez le fichier sous le nom HelloWorld.js, puis exécutez node :
node HelloWorld.js
Normalement vous devriez voir apparaître Hello world dans votre console, si ce n'est pas le cas c'est probablement que le répertoire bin de node n'est pas dans votre PATH.

Node s'accompagne de npm, qui est le gestionnaire de package de nodejs, il permet l'installation de modules nodejs très simplement.

Express est l'un de ces nombreux module disponible via npm.
Express est défini comme un framework d'application web, dans la mesure ou nodejs n'est pas un serveur web, il est judicieux d'utiliser Express afin d'eviter (entre autres choses) de coder un serveur http.

Apres une rapide introduction rentrons dans le vif du sujet.
D'abord l'installation d'Express :

npm install -g express
Ici on demande a npm d'installer Express de maniere "générale" (-g), n'ayant pas spécifié de version a priori il installe la derniere version stable.

Nous allons maintenant utiliser Express afin de créer notre application.
express -s -H mywebapp
Ici on demande à express de nous créer le projet 'mywebapp', avec gestion des sessions web (-s) et l'utilisation du moteur de template Hogan (-H).
Je vous invite à taper express --help pour voir les differentes options qui s'offrent à vous.

Une fois exécuté  le répertoire mywebapp sera créé, mettez vous a l’intérieur de se répertoire  vous remarquerez plusieurs fichiers et sous répertoire, il s'agit du squelette de votre projet web.
Entre autre, il y a un fichier app.js qui est le point d'entré de l'application, mais nous y reviendrons.
Pour le moment on va s’intéresser au fichier package.json qui lui défini les dépendances de notre projet.

A ce stade le projet n'est pas encore fonctionnelle, on doit encore demander a npm d'installer les module dont notre projet dépend.
On se place dans le répertoire mywebapp, afin d’exécuter npm :
npm install 
Vous remarquerez que cette fois on ne spécifie aucun nom de module, en fait npm va lire le fichier package.json et installer les modules qui y sont spécifié.
Cette fois il ne les installent pas de maniere "générale", mais de maniere specifique au projet "mywebapp".

On peut alors vérifier que dans le répertoire mywebapp/node_modules sont bien présent express et hjs.

A partir de la votre projet est fonctionnelle, pour le vérifier, dans le repertoire mywebapp tapez:
node app.js

Par defaut express ecoute sur le port 3000, ouvrez un navigateur et tapez l'url :
http://localhost:3000/

Vous devriez obtenir la page d'accueil de votre premiere application nodejs express !!
La webapp est prête à "accueillir" votre code...

Voila le but de cette article n’étant pas de détailler le code, je vais m’arrêter la.
Mais je vous invite à analyser un peu le code de app.js, et voir comment de app.js une requete sur localhost:3000/ aboutit au fichier views/index.hjs, en tant que developpeur web on s'y retrouve assez vite.


jeudi 6 décembre 2012

Principe ouvert/fermé (Open/close principle OCP).

Un petit post pour parler du principe ouvert/fermé (Open/close principle OCP).

D'abord la définition wikipédia :
http://fr.wikipedia.org/wiki/Principe_ouvert/ferm%C3%A9

"En programmation orientée objet, le principe ouvert/fermé affirme qu'une classe doit être à la fois ouverte et fermée. Ouverte signifie qu'elle a la capacité à être étendue. Fermée signifie qu'elle peut être modifiée par extension, sans modification de son code source. L'idée est qu'une fois qu'une classe a été approuvée via des revues de code, des tests unitaires et d'autres procédures de qualifications, vous ne voulez pas changer la classe mais seulement l'étendre. En pratique, le principe ouvert/fermé fait bon usage de l'abstraction et du polymorphisme."

Suivre l'OCP revient à écrire du code que l'on peut étendre sans avoir à le modifier!
Ça peut sembler incohérent, mais non, il n'y a pas d'erreur dans cette définition.

Contrairement au SRP, l'OCP n'est pas forcement un principe à appliquer à toutes les classes.
Il faut savoir identifier les fonctionnalités amené à souvent évoluer.
A partir de la le principe revient à créer un code qui sera amené à ne plus etre modifié, en faisant une abstraction de se qui évoluera.

Maintenant qu'on à défini l'OCP passons a un exemple, on va proposer une fonctionnalité qui consiste à calculer l'air d'un rectangle.

La classe Rectangle (sans les accesseur pour plus de lisibilité) :
  1. public class Rectangle {
  2.     private double width;
  3.    
  4.     private double height; 
  5.    
  6. }

La classe AreaCalculator avec sa methode de calcul d'air de plusieurs rectangles :
  1. public class AreaCalculator {
  2.    
  3.     public double area(List<Rectangle> shapes){
  4.         double area = 0;
  5.         for (Rectangle rectangle : shapes) {
  6.             area += rectangle.getWidth() * rectangle.getHeight();
  7.         }
  8.         return area;
  9.     }
  10.    
  11. }

Nous voila avec une solution qui devrait bien fonctionner. Mais, à peine quelques jours plus tard on vous demande la possibilité de calculer l'air de cercles en plus des rectangles.
Le client est roi et puis ça n'a rien de compliqué, alors on s'y colle en modifiant la classe AreaCalculator et en ajoutant une classe Circle :
  1. public class Circle {
  2.     private double radius;
  3.    
  4.    
  5. }

  1. public class AreaCalculator {
  2.    
  3.     public double area(List<Object> shapes){
  4.         double area = 0;
  5.         for (Object shape : shapes) {
  6.            
  7.             if(shape instanceof Rectangle) {
  8.                 Rectangle rectangle = (Rectangle) shape;
  9.                 area += rectangle.getWidth() * rectangle.getHeight();
  10.             }else{
  11.                 Circle circle = (Circle) shape;
  12.                 area += circle.getRadius() * circle.getRadius() * Math.PI;
  13.             }
  14.            
  15.         }
  16.         return area;
  17.     }
  18.    
  19. }

Voila, vous avez répondu à l'attente du client, il est content... Mais  il semble qu'il y a anguille sous roche.
En effet, on peut très justement se demander pourquoi se limiter aux rectangles et aux cercles, c'est d'ailleurs ce que se dit le client, il revient alors vers vous pour vous demander si ajouter le calcul de l'air d'un triangle serait compliqué.
Pour cela vous allez devoir modifier une nième fois la classe AreaCalculator au risque de casser ce qui fonctionne, la classe AreaCalculator n'est donc pas fermé à la modification.
Et pourtant il semble judicieux, étant donné le nombre de modification déjà apporté et les éventuelles modifications à venir, d'appliquer l'OCP sur AreaCalculator.
Pour se faire, nous allons créer un Interface Shape avec une methode area() se chargeant de calculer l'air d'un Shape.

  1. public interface Shape {
  2.    
  3.     public abstract double area();
  4. }

Maintenant on va faire en sorte que les classes Rectangle et Circle implémente l'interface Area :
  1. public class Circle implements Shape {
  2.     private double radius;
  3.     @Override
  4.     public double area() {
  5.         return  radius * radius * Math.PI;
  6.     }
  7.    
  8.    
  9. }
  1. public class Rectangle implements Shape {
  2.     private double width;
  3.    
  4.     private double height;
  5.     @Override
  6.     public double area() {
  7.        
  8.         return width * height;
  9.     }  
  10. }
A partir de ce modele, on peut réaliser un algorithme de calcul d'air capable de faire completement abstraction du type de "Shape" :

  1. public class AreaCalculator {
  2.    
  3.     public double area(List<Shape> shapes){
  4.         double area = 0;
  5.         for (Shape shape : shapes) {
  6.             area += shape.area();  
  7.         }
  8.         return area;
  9.     }
  10.    
  11. }
Dorénavant l'ajout d'une nouvelle forme géométrique ne passe plus par la modification de l'algorithme du calcul d'air, mais uniquement par l'ajout d'une classe implémentant l'interface Shape ce qui rendra cette nouvelle classe utilisable par l'algorithme de calcul d'air.
AreaCalculator et sa methode de calcul d'air est alors fermé a la modification et ouverte à l’évolution.

Entre autres les desgin pattern Strategy, Template method ou encore Visitor découlent directement de l'OCP.

vendredi 23 novembre 2012

Node.js ou comment avoir du javascript coté serveur.

C'est en cherchant quelques informations sur l'utilisation des WebSockets coté serveur en Java, que j'ai découvert nodeJS.
WebSocket est un protocole de communication définit dans HTML 5.
Cette nouveauté me semble etre une petite révolution, sans rentrer dans les détails les websocket créent un canal de communication bidirectionnel entre le client et le serveur sur le protocole HTTP qui lui avait l’énorme lacune d’être unidirectionnel ( requête client -> réponse serveur).
Du coup beaucoup de choses deviennent possible de façon standard !!
Que le serveur puisse interroger ou notifier les clients change assez radicalement les choses.

Pour en revenir à ma recherche, je voulais juste savoir s'il existait déjà des façons d'implmenter les WebSockets coté serveur en java.

J'ai d'abord lu cet article :

http://blog.zenika.com/index.php?post/2012/06/22/Les-WebSockets-en-pratique

Il s'agit d'un petit comparatif de quelques solutions pour utiliser les websockets.
L'auteur conclu que pour lui "node.js & Socket.IO" est la solution qui fait référence.

J'ai un peu honte de l'avouer mais c'est la premiere fois que j'entendais parler de node.js, et d'une technologie basé sur du Javascript coté serveur, je me suis senti obligé de creuser un peu.
A priori Node.js à pas mal de succès de plus la communauté node.js semble très active, avec de nombreux modules déja existant.
Nodejs utilise l'interpreteur javascript de google chrome, autrement dit : c'est rapide !

J'ai donc voulu essayé, pour ça j'ai suivi ce tutoriel :

http://nodejs.developpez.com/tutoriels/javascript/node-js-livre-debutant/

Et puis j'ai vu ce tutoriel vidéo qui m'a convaincu de la simplicité de nodejs + Socket.IO :

Créer un tchat avec NodeJS et Socket.IO

Ca parait tellement simple !!
Par contre ça passe par un apprentissage Javascript, je dois bien avouer que pour moi qui suis un pur développeur objet en java certaines lignes de code sont déconcertantes.
Mais je reconnais que ça peut parfois être élégant !

Je manque peut etre d'imagination mais je vois pas nodejs concurrencer JAVA coté serveur, je n'ose imaginer une couche métier en javascript.
J'imagine que le succès de nodejs vient à la base des developpeurs front end (html,css,javascript), qui peuvent ainsi s'exprimer coté serveur.

Faut reconnaître que c'est assez surprenant de voir que le javascript s'invite sur les serveurs, alors que Java lui disparaît progressivement coté client, quand on voit les possibilités de HTML5 même JavaFX va avoir du mal à se faire un place coté client.

vendredi 16 novembre 2012

Riche Internet Application (RIA) HTML5 vs JavaFX 2

Dernièrement je me documente un peu sur JavaFX2.
A l'epoque de sa sortie j'ai été étonné de voir l'investissement d'oracle sur JavaFX dont la version 1 avait fait un flop.
La deuxieme mouture à l'air de suciter pas mal d’intérêt, et je dois avouer que ce que j'ai vu m'a plu.
La page de démo de JavaFX :
http://www.oracle.com/technetwork/java/javafx/samples/index.html

Je vais donc garder un oeil sur le sujet, attendre quelques retour et probablement l'essayer histoire de me faire mon idée sur ce futur remplaçant de Swing, car JavaFX est bien présenté comme le remplaçant de Swing.

Avant de tester JavaFX, j'ai fais le tour du net pour voir ce qu'on en dit.

Et j'ai trouvé par hasard une entré de blog dont le titre a suscité ma curiosité:

When to use JavaFX 2 instead of HTML5 for a Rich Internet Application (RIA)?

L'auteur précise que pour lui une application internet riche est un application très centré sur le client, avec un chargement initial permettant d'offrir une interaction fluide avec l'utilisateur.
C'est pour cette raison qu'il ne considère pas les framework (orienté serveur) tel que Vaadin,JSF, Wicket ...etc comme des solutions pour des applications internet riche.
Dans ce domaine HTML5 à de l'avenir c'est inévitable et l'auteur est d'accord avec ça  par contre on est en droit de se poser la question sur JavaFX, mais le fait qu'il soit comparé à HTML5 est  plutôt flatteur pour JavaFX.

A noter que pour l'auteur, flash et silverlight sont mort, meme Adobe et Microsoft sont passé au HTML5.

voici les pour et les contres de HTML5 selon l'auteur du blog :

Les pour
 -Standard W3C
-C'est le futur, c'est indéniable !
-Aucun plugin n'est necessaire, peut etre utilisé sur tous les navigateurs (compatibles)
-Déja de nombreux composant et nombreuse fonctionnalités disponnible.
Les contres
-Développement HTML/Javascript au lieu de java => gros inconvénient pour des développeurs JAVA -Spécification pas encore finalisé (d’après la roadmap ça sera pas avant 2014!) -Les navigateur ne sont pas (encore) tous compatible -Demande un effort de développement supplémentaire pour être compatible multi-navigateurs (Même si certains framework javascript résolvent ce problème c'est un effort supplémentaire.
JAVAFX :
Les pour

-Offre une API java  => permet de tirer parti de vos compétences java et d'utiliser les librairies et fonctionnalités existantes.
-Offre la possibilité d'utiliser des Domain Specific Language (DSL) type Groovy (GroovyFX) ou Scala (ScalaFX)
-Un language optionnel de définition de "layout" (FXML) permettant de séparer la définition de l'interface du comportement => A vous de choisir codage d'interface en java ou en FXML.
-Meme environnement de developpement pour le coté serveur et le coté client (utile pour le debugging, refactoring ... etc.)
-Pas de problème de compatibilité de navigateurs
-Support de CSS comme en HTML
-HTML et Javascript peuvent etre integré dans une application JavaFX
-Swing et JavaFX peuvent être utilisé dans la meme application, donc les application swing existante peuvent évoluer vers JavaFX.
-JavaFX 2 propose une architecture unifié permettant de deployer un projet dans divers contexte (application standalone, application web ou java web start) .En attendant la possibilité de déployer sur téléphones mobiles.

Les contres

-Une JRE est nécessaire chez le client.
-L'auteur precise que JavaFX n'est pas open source, mais je pense que ca à évolué depuis.
-JavaFX n'est pour le moment autorisé sur mac qu'en version "developer preview"), ce n'est plus le cas depuis la version 2.1
-Idem pour Linux ce n'est plus le cas depuis la version 2.2
-Pas encore d'information sur JavaFX Mobile du moins je n'ai rien trouvé !
-Offre moins de composant et fonctionnalités que HTML5
-JavaFX est le futur remplacant de Swing, le developpement est différent à cause des nouveaux concept comme les animations. Mais ceci n'est pas vraiment un désavantage, cela reste plus facile d'apprendre à utiliser JavaFX pour un développeur java que d'apprendre le HTML et le javascript nécessaire pour faire quelque chose d’équivalent.

En résumé la conclusion de l'auteur c'est que HTML5 et JavaFX sont deux excellent moyen de développer une RIA.
Selon lui pour les applications web publique il parait difficile d'imposer JavaFX et l'installation de java chez tous les utilisateurs.
Mais pour les applications internes à une entreprise JavaFX a sans doute sa carte à jouer.

Dans l'ensemble, pour le moment je suis assez d'accord, d'autant qu'il souligne qu'une autre alternative pourrait venir de GWT et un éventuel support de HTML 5 qui pourrait bien mettre tout le monde d'accord :
http://www.google.com/events/io/2011/sessions/gwt-html5-a-web-developers-dream.html

GWT ou JAVAFX ?
Quoi qu'il en soit il semble que les développeurs Java attendent une solution "full JAVA", et ne souhaitent pas particulièrement acquérir des compétences pointu en HTML5 pour développer des RIA.