Les nouvelles librairies dans Java 7

le 05/11/2008 par Marc Bojoly
Tags: Software Engineering

La nouvelle version de Java 7 est en cours de préparation. Le passage de la licence propriétaire à un développement open source a introduit un certain retard et beaucoup d'incertitudes concernant son contenu et sa date de release. Cependant, nous considérons que les nouvelles fonctionnalités proposées constituent des opportunités fortes intéressantes pour ce langage, et qu'il est utile de les étudier, indépendamment des doutes qui peuvent encore les entourer. En cas de doute, nous préciserons les réserves concernant une librairie ou l'exemple de code proposé compilait avec la version de prévisualisation 1.7.0-ea-b37.

Pour cela nous allons réaliser une série d'articles, permettant de prendre connaissance de ces fonctionnalités de manière concise. Le premier de cette liste décrira brièvement la majorité des fonctionnalités et renverra la description des fonctionnalités les plus complexes à de prochains articles.

Modularité

Les deux JSR 294 et 277 traitent respectivement des superpackages et des Java Module System. Elles ont pour objectif de définir des mécanismes de déploiement permettant la prise en compte des versions, des dépendances et des visibilités entre packages. Certainement l'un des points les plus sujet à polémiques car il entre en concurrence avec un autre standard : le standard OSGI. Ce thème fera donc l'objet d'un prochain article qui lui sera entièrement dédié.

Java 7 définit un certain nombre de nouvelles librairies que nous allons détailler. Un grand nombre sont reprises de projets open source où elles ont pu être testées et éprouvées. Nous allons voir pour chacune d'entre-elles les apports comparées aux librairies du JDK actuel et les éventuelles différences avec les projets open source qui les ont portées.

NIO2

La JSR-203 donnera naissance au package NIO2.

Celui-ci permettra d'une part l'amélioration du connecteur de flux Channel introduit par NIO. Ce type de connecteur a défini depuis java 1.4 une interface commune pour toutes les connections vers des périphériques (fichiers, socket). En java 7, le Channel sur les sockets permettra désormais le multicast.

D'autre part, une nouvelle API sur le système de fichiers permettra de gérer les liens symboliques, les attributs, de parcourir les répertoires, de s'abonner à des notifications sur le système de fichiers. Des systèmes de fichiers particuliers pourront être pris en charge via un mécanisme de plugin.

L'asynchronisme sera également rendu possible. Ainsi, on pourra affecter un pool de threads à un ensemble de modifications sur des channels.

Il s'agit donc d'une librairie très technique qui, telle NIO, ne sera que très rarement utilisée par la majorité des développeurs. Cependant le push serveur nommé COMET a été rendu possible par les fonctionnalités introduites par le package NIO . En somme NIO2 est une source d'innovations pour les développements serveurs futurs.

Aujourd'hui ces nouvelles fonctionnalités apparaissaient dans le package nio de la version de prévisualisation.

Units and Quantites

La JSR-275 est très fortement inspirée de l'API JScience. Sans la reprendre en totalité, elle permet d'utiliser le typage fort et les génériques pour fiabiliser la manipulation de mesures et d'unités. Par exemple, à la compilation il ne sera pas possible d'affecter des mètres dans un champ typé par une longueur. Des mécanismes de parsing et de conversion en chaînes de caractères seront directement proposés par le framework.

Cette API est basée sur trois notions fondamentales liées entre elles que nous allons étudier

  • Quantity désigne une propriété mesurable d'un objet
  • Measurable représente une mesure : une grandeur caractéristique d'un objet pouvant exprimée sous forme numérique.
  • Unit désigne une unité dans laquelle peut être exprimée une mesure.

Tout d'abord Quantity est une interface sans aucun membre. Son unique objectif est d'assurer le typage fort. Intuitivement deux quantités sont deux grandeurs qui ne peuvent pas être additionnées entre elles. Les quantités disponibles sont regroupées dans le package javax.measure.quantity. On pourra citer Length, Time.

Measurable permet de stocker la mesure et de la comparer à d'autres mesures d'une même dimension. Tout objet implémentant Measurable doit renvoyer un nombre d'unités.

Prenons un exemple, nous écrirons

Mesurable<length> l = Measure.valueOf(10, SI.KILOMETER);
Mesurable<time> t = Measure.valueOf(1, NonSI.HOUR);
</time></length>

Dans cet exemple nous avons fait apparaître l'unité, qui est l'échelle d'une quantité. Ainsi nous devons la spécifier à côté de la valeur numérique, par exemple SI.METER et NonSI.SECOND.

L'interface Measurable ne conserve pas l'unité d'origine. A la lecture du champ l, je ne saurais pas si l'utilisateur a saisi des mètres ou des kilomètres.

System.out.println(l.doubleValue(SI.METER));//10000

L'implémentation de Measurable repose sur la classe abstraite Measure. Dans ce cas l'unité d'origine est conservée. Deux implémentations concrètes existent : pour une classe V décimale ou une classe V vectorielle.

L'API permet également de convertir les unités compatibles ou de définir des unités composites.

Unit<velocity> v = SI.METER.divide(SI.SECOND).asType(Velocity.class);
</velocity>

En revanche la conversion se limite aux seules unités. L'interface Measurable n'expose pas de méthode d'addition, de multiplication, etc... Le développeur souhaitant calculer une vitesse travaillera sur les nombres sous-jacents et reconstruira ensuite une mesure de vitesse.

Measurable<velocity> v =   Measure.valueOf( l.doubleValue(SI.METER)*t.doubleValue(NonSI.HOUR), NonSI.KILOMETRES_PER_HOUR);
</velocity>

L'API assure enfin la transformation vers les chaînes de caractères.

System.out.println(v.toString()); //2.777777777777778E-4 km/h

En somme, cette API apporte une robustesse supplémentaire au code par le typage au prix d'un code un peu plus verbeux. La déclaration simplifiée proposée par l'inférence de type en Java 7 devrait permettre de limiter ce problème. Le package correspondant n'apparaissait pas encore dans la version de prévisualisation.

Date and Time API

La JSR-310 est tirée de l'API joda-time car portée par le même leader Stephen Colebourne. Elle doit permettre de corriger les principaux problèmes présents dans le JDK tout en restant compatible avec les classes existantes. Le package correspondant n'est pas encore disponible dans la version de prévisualisation.

Les nouvelles classes sont d'abord immuables afin d'être sûres vis à vis des threads. Elles sont toutes basées sur le standard ISO_8601 et fournissent des méthodes de formatage et de parsing adaptées. Enfin la compatibilité avec XML, SQL et le système actuel est réalisée via un jeu d'interfaces. Le système actuel java.util.Date, etc... ne sera donc pas déprécié.

Afin de comprendre l'organisation de l'API, il faut comprendre que deux lignes de temps sont représentées, chacune avec plusieurs notions:

  • la ligne continue pour les mesures entre machines.

- Elle est représentée par la classe Instant qui est un incrémental portant sur le nombre de millisecondes depuis le 1er janvier 1970.

Instant b = new Instant(0); 
System.out.println(b);//1970-01-01T00:00:00.000Z

- L'intervalle entre deux instants est représenté par une classe dont le nom n'est pas encore fixé. Admettons Interval.

Interval interval = new Interval(b, new Instant());
System.out.println(interval);//1970-01-01T00:00:00.000/2008-10-19T20:46:40.031

- La durée, représentée par un nombre et indépendante de la ligne de temps est représentée par la classe Duration. Elle dispose théoriquement d'une précision du niveau de la nanoseconde, mais l'implémentation dans Joda-time se limite à la milliseconde : la seule capable d'être stockée sur un long.

Duration d = new Duration(b, new Instant());
System.out.println(d);//PT1224449416.671S - Nombre de secondes

- Les opérations d'addition de durée sur l'intervalle et de transformations d'intervalles en durées sont implémentées.

  • la ligne de temps humaine exprimée en jours, heures, secondes.

- Plusieurs classes sont fournies afin de représenter les dates dans différents calendriers (grégorien, hébreu, japonais, etc..) et dans différentes zones horaire. Citons LocalDate/LocalTime/LocalDateTime, OffsetDate, Year, MonthOfYear, Day, DayOfWeek, HourOfDay, ZonedDateTime etc... Toutes les valeurs retournées sont des classes ou des enums, ce qui est une différence par rapport à joda-time.

DateTime dt = new DateTime(); 
System.out.println(dt);//2008-10-19T22:50:16.546+02:00 
System.out.println(dt.getMonthOfYear());//10

- Par rapport à Joda-Time, la notion de Matcher permet de vérifier des caractéristiques d'une date. Par exemple vérifier qu'une date fait partie de l'année en cours.

System.out.println(dt.matches(year(2008)));//true

- La notion d'Adjuster est également une nouveauté par rapport à joda-time. Elle permet de renvoyer une date modifiée

System.out.println(dt.with(year(2006)));//2006-10-19T22:50:16.546+02:00

- La nouvelle notion de Resolver permet de corriger les dates erronées.

DateResolver res = DateResolvers.previousValid();
System.out.println(new date(2007, 2, 30, res));2007-02-28

- La notion de période, de durée écoulée entre deux instants est également représentée par une classe du même nom. Plus que la notion d'intervalle elle prend en compte les particularités du calendrier comme le décalage horaire.

Instant b = new Instant(0);//1970-01-01 
Instant i = new Instant();//2008-10-19 
Period p2 = new Period(b, i); 
System.out.println(p2);//P38Y9M2W4DT21H50M28.734S

- La date courante, enfin, supporte l'inversion de contrôle ce qui permet de faciliter grandement les tests d'un système, en simulant le début du mois pour tester certains traitements par lot.

@Resource private Clock clock; // inject a subclass returning always 2006-10-18 //... 
System.out.println(clock.today());//2006-10-18

Cache API

La JSR 107 est connue aujourd'hui sous le nom d'API JCache. Elle est implémentée, parfois de façon incomplète, par de nombreux frameworks tels qu'EHCache, JBoss cache, OSCache par exemple. Ses classes principales sont le CacheManager qui permet de charger une configuration, le Cache qui stocke des instances de type Element. L'utilisation la plus simple de cette API est donc la suivante :

CacheManager manager = new CacheManager(); 
manager.addCache("testCache"); 
Cache cache = manager.getCache("testCache"); 
Element element = new Element("key1", "value1"); 
cache.put(element); 
System.out.println(cache.get("key1"));

Cette API permet également de définir une instance de CacheListener permettant de s'abonner aux événements sur le cache. Elle autorise également le chargement de collections d'objets en implémentant l'interface CacheLoader. Des informations de configuration sur le cache peuvent enfin être lues et écrites grâce aux implémentations des interfaces CacheStatistics et EvictionPolicy. Le package correspondant n'est pas encore disponible dans la version de prévisualisation.

Concurrence

Une nouvelle API de gestion de la concurrence doit être inclue dans java 7. Elle est fortement inspirée du framework fj et sera regroupée dans le package java.util.concurrency. Du fait de l'arrivée massive des processeurs multi-coeurs sur le marché, les technologies facilitant l'écriture de programme parallélisable nous semblent gagner en importance. En conséquent ce thème fera l'objet d'un prochain article.

XQuery API

La JSR 225 consiste à fournir une API permettant de soumettre des requêtes XQuery à toute source de données pouvant être vues comme du XML. Certaines bases de données relationnelles offrent ainsi une API XQuery. XQuery 1.0 est un langage de requêtage de données XML défini par le W3C.

L'API XQuery, connue à ce jour sous le nom de XQJ possède un certain nombre d'implémentations : citons une implémentation commerciale par DataDirectXQuery, Saxon, OJXQI d'Oracle, etc...

Elle possède de grandes similitudes avec l'API JDBC. A l'heure actuelle le package n'est pas disponible dans la version de prévisualisation.

XQDataSource xqds = new sXQDataSource(); 
XQConnection xqc = xqds.getConnection(); 
XQExpression xqe = xqc.createExpression(); 
XQSequence xqs; xqs = xqe.executeQuery("doc('orders.xml')//order@id='174'"); 
while (xqs.next()) {   
  System.out.println(xqs.getItemAsString(null)); 
}

Resources consumption management

Cette API a pour objectif de donner accès à la consommation des différentes ressources (CPU, nombre de connections à la base de données) par la JVM. Elle doit permettre à la fois d'être notifiée des modifications des valeurs mais aussi de les influencer par des contraintes. Cette JVM est liée à la JSR-121 qui permet de spécifier des "Isolate", c'est à dire des portions de code java isolées les unes des autres au niveau de la consommation de ressources, un peu à la manière des deux machines virtuelles à qui on affecte chacune un seul CPU.

Aujourd'hui, aucun code fonctionnel ne semble mettre en oeuvre ces principes. L'utilisation actuelle la plus proche de ce concept serait l'Application Resource Allocation de Grizzly. Grizzly est le moteur HTTP du serveur d'application glassfish. Ces problématiques de gestion de ressources seront particulièrement utilisées par les serveurs d'applications, en particulier lorsqu'ils proposent comme Grizzly des fonctionnalités de reverse ajax ou streaming client très consommatrices en ressource.

Changements mineurs

La classe java.util.Currency subira quelques améliorations. La plus importante d'entre elles consiste à stocker la liste des devises non plus dans rt.jar mais dans un fichier séparé. Toute mise à jour de la norme ISO pourra donc être réalisée sans changer de version de JRE.

Swing Application framework

Il s'agit d'un petit ensemble de classes destinées à faciliter la programmation d'applications Swing. Il se fonde sur deux classes fondamentales : la classe Application et la classe ApplicationContext. Toute instance de Application contient un ApplicationContext qui donne accès aux fonctionnalités du framework. Ce package n'était pas encore présent dans la version de prévisualisation mais est livré à partir de la version 6 de l'IDE Netbeans.

Il permet tout d'abord de simplifier le cycle de vie d'une application. Une classe SingleFrameApplication encapsule le démarrage et l'arrêt d'une application. Ainsi le hello world devient :

public class SingeFrame extends SingleFrameApplication {   
public void startup(String[] args) {
        JLabel label = new JLabel("Hello World");
        show(label);    
  }
   public static void main(String[] args) {
        launch(SingleFrameExample1.class, args);    
  } 
}

Cela permet ensuite de conserver les données de contexte d'une application au travers d'un lieu de stockage et d'y accéder via l'instance de la classe ApplicationContext

La gestion des ressources a été simplifiée. Lorsque le développeur écrit

JLabel label = new JLabel(); 
label.setName("label");

Toutes les propriétés dans le fichier de propriété (label.text, label.foreground) seront fixée sur le libellé. Une annotation @Resource est également prévue.

Le pattern commande est rendu plus simple à utiliser grâce à l'annotation @Action. Toute méthode annotée par @Action est automatiquement stockée dans la carte des actions. Les méthodes annotées @Action peuvent également renvoyer Task afin d'avoir un comportement asynchrone. Task est une classe fille de SwingWorker introduite en java 6 et qui permet de définir une tâche s'exécutant en arrière plan. Elle ajoute de nouvelles fonctionnalités, comme la possibilité de bloquer uniquement certains composants de l'interface

public class LoadFileTask extends Task {
    @override protected void doInBackground() {
        //Load files    
}
   @override protected void succeeded() {
        //Update number of loaded files    
  } 
}

Beans Binding et Beans Validation

La JRS 295 permet d'assurer plus facilement la liaison entre les beans métiers et les composants de présentation. Par exemple, si nous souhaitons modifier le nom d'une personne, nous aurons à l'écran un composant JTextField et un POJO Person avec un champ Name. Le framework de BeanBinding permet de définir la liaison entre ces deux notions

Binding binding = new Binding(jtextfield, "text", person, "name"); 
binding.bind();

A partir de cette seule information, toute modification sur l'attribut text de l'objet jtextfield sera reportée sur l'attribut name de l'objet person et inversement. Par ailleurs, des API plus évoluées permettent de rajouter des conversions (par exemple d'une chaîne en date) et des validations dont nous parlerons juste après. Il est à noter que l'expression de binding utilise la syntaxe EL ("Expression Language") ce qui permet de cibler des champs profondément insérés dans un graph d'objets. Le comportement est récursif de sorte que le binding sur un conteneur déclenchera le binding sur tous ses fils.

La JSR 303 permet de définir des contraintes de validation sur les données métier. Elle est fortement inspirée du framework ''Hibernate Validators'' mais en étend la portée au delà de la couche de persistance. La définition d'annotations sur les objets métiers permet d'imposer des contraintes sur ceux-ci, de manière analogue aux contraintes d'une base de données. Ainsi sur l'objet Person on pourrait imaginer les contraintes suivantes : obligation de renseigner le champs et un maximum de 20 caractères.

public class Person {
    @NotNull    
    @Length(max=20)     
     public String getName() { return name; } 
}

De nombreuses règles de validation sont disponibles en standard @Length, @Max, @Min, @NotNull, @Past, @Future, @Patern,... etc. D'autres validations peuvent être définies de manière spécifique en définissant une annotation et une classe qui étend la classe Validator.

Aujourd'hui le framework Hibernate Validators permet de déclencher manuellement la validation et de la réaliser automatiquement en cas d'insertion dans hibernate. L'objectif de la JSR 303 est d'étendre ce concept à toutes les couches de l'application : depuis la validation de surface dans l'interface graphique jusqu'à cette insertion dans la base de données.

On pourra se référer pour plus de détail à la session T3 de l'université du SI : Java aussi productif que .NET pour le développement d'application graphique n-tiers, par S.Jaber.

Les packages correspondants ne sont pas disponibles dans la prévisualisation mais l'intégralité de ces deux JSR est intégrée dans la version 6 de NetBeans.

Java Media Components

Aucune information ne semble disponible sur ce sujet depuis plus d'un an maintenant. S'ils étaient inclus dans la version 7 de java ils apporteraient nativement à java la possibilité de lire des flux vidéo. Contrairement à la librairie JMF (Java Media Framework), ces composants se concentreraient uniquement sur la possibilité d'encapsuler les lecteurs natifs de la plateforme (Windows media player par exemple) dans un composant java.

JMX 2.0 et connecteurs web services pour JMX

La JSR 255 est une évolution de spécification JMX actuelle. Ses fonctionnalités sont :

  • Introduction d'une notion de namespace hiérarchique permettant d'organiser les MBeans et d'une API de requêtage
  • Fédération : il sera désormais possible de se connecter à un serveur maître qui relaiera les modifications et événements à des MBeans hébergés sur des serveurs distants
  • Nouveau mécanisme d'événements permettant notamment de s'abonner à un ensemble de MBeans sur un motif de nommage, de recevoir des notifications via JMS
  • Support de l'internationalisation
  • Possibilité de définir des MBean par annotation, de façon similaire à celle proposée par Spring
  • La possibilité d'exposer des types personnalisés dans l'interface d'un MXBean

La JSR 262 a pour objectif de fournir un connecteur web service pour JMX dans le JDK. Actuellement, il est possible d'accéder à JMX via un connecteur basé sur RMI en utilisant l'outil JConsole, ou bien en se basant sur un adaptateur HTTP. Un nouveau connecteur par webservice sera fourni dans le JDK 7, ce qui permettra de gérer une application java à partir de tout client respectant le standard WS-Management. JConsole pourra toujours être utilisé en précisant une URL du type service://ws://: Une implémentation est à l'essai au sein du projet ws-jmx-connector. Le package management a été mis à jour dans la version de prévisualisation.

Outils divers

Un outil d'étude des post-mortem a été proposé sous le code JSR-236. Cette proposition est tardive et il n'est vraiment pas certain qu'elle soit incluse dans la release de java 7. Cet outil aurait pour objectif de produire des traces de type HPROF, afin de diagnostiquer la cause de problèmes difficilement reproductibles.

La JSR-260 définit des améliorations à l'outil javadoc afin de générer une documentation plus moderne et d'ajouter des tags de commentaires. Cependant le déficit d'information sur internet semble montrer que cette JSR a peu de chance d'être incluse dans la prochaine version de java.

En somme nous avons déjà vu à travers ces exemples que les nouvelles librairies apportées par java 7 sont riches en nouveaux concepts. Les ajouts sont largement basés sur l'expérience de frameworks réels, ce qui est un gage de qualité et d'intérêt des concepts importés. Même si certains aspects peuvent paraître très techniques, ils constituent la base des prochaines évolutions dans le monde java. Dans un second article introductif nous nous intéresserons cette fois aux nouveautés apportées au langage et la JVM par java 7.

Par David Rousselie et Marc Bojoly