EJB 3.1, on y arrive !

Cela va mainteant faire 4 jours que GlassFish v3 prelude est sorti. Pour accompagner sa sortie, une vingtaine de présentations ont été faites sur GlassFish (via la chaine TheAquarium, sur Ustream.TV), mais aussi sur Java EE 6 et NetBeans 6.5.

Je viens d'en regarder quelques unes, et celle qui a le plus retenu mon attention est sans aucun doute la présentation des EJB 3.1, qui vient avec son lot d'améliorations et de nouvelles fonctionnalités qui méritent de s'y attarder :-)

On retrouve parmis ces fonctionnalités :
  • Les singletons
  • Une meilleure gestion des timers
  • Les noms JNDI globaux
  • Les Session Beans "sans interface"

Les singletons

Les singletons sont des beans qui ont la particularité de n'être instanciés qu'une seule et unique fois tout au long de la vie de notre application. Cette instance sera ensuite partagée entre les différents clients l'utilisant.

@Singleton
public class MySingleton {

    private int sharedValue;

    public int getSharedValue() {
        return (sharedValue);
    }

}

Dans cet exemple de code, notre bean MySingleton sera instancié une seule et unique fois, et chaque appel de méthode fera appel à la même instance, ce qui nous permettra, par exemple, de partager des données (comme la variable sharedValue de notre exemple).

Une meilleure gestion des timers

EJB 3.1 introduit également une nouvelle gestion des timers, qui se rapproche de la gestions des crons Unix : on peut spécifier des tâches à exécuter de manière très précise. Ces timers seront alors automatiquement créés et démarrés.

Voici un exemple, lançant une tâche tous les jours à 8 heures :
@Stateless
public class MyBean implements MyInterface {

    @Schedule(hour="8")
    void myTask() {
    }

}

Les noms JNDI globaux

Il s'agit sûrement d'une des plus grosses lacunes de la spécification EJB 3 qui se trouve ici comblée : l'absence de standardisation des noms JNDI associés aux Session Bean, ce qui entraînait des pertes de portabilité entre les différents serveurs d'application.

Par exemple, sous GlassFish, le nom par défaut d'un Session Bean est le nom complet de l'interface métier de notre bean (com.aperigeek.ejb.MyInterface). Sous JBoss, le nom est <nom du jar>/<nom du bean>/<visibilité>, ce qui donnerais par exemple aperigeek/MyBean/remote. Les noms sont donc très dépendants du serveur d'application.

La nouvelle spécification EJB 3.1 spécifie des noms standards à respecter lors du binding des beans : java:global[/<app-name>]/<module-name>/<ejb-name>, ce qui pourrait donner : java:global/aperigeek/aperigeek-ejb/MyBean. Cette fonctionnalité permet d'assurer la portabilité d'une application entre différents serveurs d'applications.

Les Session Beans "sans interface"

Dernière fonctionnalité dans la série des simplifications de la spécification : la possibilité de définir un Session Bean dans une seule classe, sans passer par une interface métier exposant les différentes méthodes.

@Stateless
public class MyBean {

    public void myMethod() {
    }

}

Dans ce cas de figure, toutes les méthodes publiques deviennent visibles.
@Stateless
public class MyOtherBean {

    @EJB
    private MyBean myBean;

    public void myOtherMethod() {
        myBean.myMethod();
    }

}



La nouvelle version des EJB, la version 3.1, apporte encore quelques améliorations à la version 3.0. L'optique de cette nouvelle version reste essentiellement la même : simplifier l'utilisation des EJBs. On retrouve quand même quelques nouvelles fonctionnalités intéressantes, comme les singletons ou les timers.

Toutes ces nouvelles fonctionnalités sont d'ores et déjà testables grâce à GlassFish v3 prelude, qui sert d'implémentation de référence à cette spécification.

Permalink  |  Commentaires (0)

Astuce Java #6 : Enum et toString

Depuis Java 5, le langage Java dispose d'une nouvelle fonctionnalité : les énumérations.

Les énumérations sont des structures permettant de représenter une variable ayant un nombre fini de valeurs, comme par exemple le sexe d'une personne, qui sera soit "homme", soit "femme".

Les énumérations sont mises en place de la manière suivante :
enum Sex {
    MALE,
    FEMALE;
}

Il est ensuite possible de créer des variables du type de l'énumération, et de les utiliser :
Sex sex = Sex.MALE;
System.out.println(sex);

Et c'est là qu'apparaît notre problème : la console nous affiche "MALE". Certes, il s'agit de la bonne valeur, mais la présentation n'est pas très esthétique. Heureusement pour nous, en Java une énumération est également un objet. Il nous est possible de définir nos propres méthodes, et aussi de récrire certaines méthodes de la classe Object. Ainsi, on peut avoir le code suivant :
enum Sex {
    MALE,
    FEMALE;
    
    @Override
    public String toString() {
        switch (this) {
            case MALE:
                return ("Male");
            case FEMALE:
                return ("Female");
        }
        throw new RuntimeException("Invalid value for this");
    }
}

L'exécution du même code nous donne ainsi "Male" au lieu de "MALE". On obtient le résultat souhaité, à savoir une chaîne de caractères plus facilement lisible par l'utilisateur.

Le soucis que l'on rencontre maintenant viens du switch dans la méthode toString. Nous devons gérer toutes les valeurs possible dans cette méthode, sous peine de lancer une RuntimeException. Une des solutions possibles (que j'ai trouvé dans un commentaire d'un (très) vieux billet de blog), et que je trouve de loin ma préférée, est de fournir un constructeur à notre énumération. Ce constructeur prend en paramètre une chaîne de caractères qui sera utilisée en tant que valeur de retour de la méthode toString :
enum Sex {
    MALE("Male"),
    FEMALE("Female");
    
    private String value;
    
    private Sex(String value) {
        this.value = value;
    }
    
    @Override
    public String toString() {
        return (value);
    }
}

Ainsi, nous obtenons on comportement optimal :
  • Chaque valeur de l'énumération renverra une chaîne de caractères "esthétique" représentant sa valeur
  • Chaque valeur devra posséder une chaîne de caractères (au risque de générer une erreur de compilation)

Le problème majeur qui est ainsi rencontré concerne la conversion énumération -> chaîne de caractères -> énumération. La chaîne de caractères renvoyait de base par toString était le nom de la constante dans l'énumération (MALE ou FEMALE dans notre cas), qui pouvait ensuite être reconverti en énumération via la méthode valueOf. Dans notre cas, la conversion ne se ferra plus puisque toString ne renverra plus une valeur compréhensible par valueOf. Cependant, la méthode name, définie dans toute énumération, renvoie toujours le nom de la constante tel que définit dans le code.

Permalink  |  Commentaires (3)

Système d'inclusion en JSF

Pour éviter les erreurs, voici un rapide rappel pour faire de l'inclusion de pages en JSF. Cet article contient un exemple de code classique, puis quelques points à ne pas oublier pour que tout se passe bien.

Le but de cet exemple sera d'avoir une page index.jsp qui inclue une page content.jsp.

index.jsp

<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>

<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>JSP Page</title>
    </head>
    <body>
        <f:view>
            <f:subview id="content">
                <jsp:include page="content.jsp" flush="false" />
            </f:subview>
        </f:view>
    </body>
</html>

content.jsp

<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

<h:outputText value="Ceci est le contenu !" />

Points importants

Il existe quelques règles assez importantes pour que ce système d'inclusion de page marche correctement :
  • Dans la page de base, entourer l'inclusion avec une balise <f:subview />
  • Lors de l'inclusion, penser à mettre l'attribut flush à false. Cet attribut existe sur la plupart des balises permettant l'inclusion de pages.
  • Dans la page de destination, réutiliser les composants JSF directement, sans remettre de balise <f:view />
  • Les taglibs doivent quand à elles être réimportées.

Et voilà le résultat (en HTML) :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>JSP Page</title>
    </head>

    <body>
        Ceci est le contenu !
    </body>
</html>

Martin en parle également.

Permalink  |  Commentaires (0)

Optimisation : Utilisation d'un pool d'objets

J'ai lu récemment une série d'articles1 2 3 4 très intéressants ayant pour sujet l'optimisation des programmes Java. La dernière partie parlait de la réutilisation des objets. Un exemple montrait qu'il était plus performant de vider un ArrayList que de créer une nouvelle instance :

for (int i = 0; i < 10; i++) {
    ArrayList list = new ArrayList();
    // Do some stuff...
}
ArrayList list = new ArrayList();
for (int i = 0; i < 10; i++) {
    // Do some stuff...
    list.clear();
}

Dans notre cas, appeler un constructeur est plus coûteux en temps processeur que de simplement supprimer tous les éléments de notre liste (la raison a été discutée sur comp.lang.java.programmer). Le code de droite est donc plus rapide à l'exécution.

J'ai alors eu l'idée de me lancer dans la création d'un pool d'objets. Dans le cas des ArrayList, le but serait de vider notre instance et de la conserver en attendant de la réutiliser plus tard.

J'ai donc testé le code suivant :
public class ArrayListPool<T> {

    private Queue<ArrayList<T>> pool;
    
    public ArrayListPool() {
        pool = new LinkedList<ArrayList<T>>();
    }
    
    public ArrayList<T> getArrayList() {
        synchronized (pool) {
            if (!pool.isEmpty()) {
                return (pool.poll());
            }
        }
        return (new ArrayList<T>());
    }
    
    public void releaseArrayList(ArrayList<T> list) {
        list.clear();
        synchronized (pool) {
            pool.offer(list);
        }
    }
    
    public void clear() {
        synchronized (pool) {
            pool.clear();
        }
    }
}

Le but est d'appeler getArrayList pour obtenir une instance d'un ArrayList, et de la passer en paramètre à la méthode releaseArrayList pour la remettre dans le pool et la réutiliser au besoin.

Concernant les détails de fonctionnement, je me suis servi d'une LinkedList pour stocker les objets à réutiliser. Cette implémentation de Queue est basée sur une liste chaînée, ainsi les insertion et les suppressions d'éléments dans le pool sont très rapides.

Enfin, l'utilisation de ce pool. À gauche, le code de base, qui servira de benchmark par la suite. À droite, le code modifié pour utiliser le pool.
ThreadGroup group2 = new ThreadGroup("Group 2");
for (int i = 0; i < threadCount; i++) {
    Thread t = new Thread(group2, "Thread " + i) {
        @Override
        public void run() {
            for (int i = 0; i < listCount; i++) {
                ArrayList<Object> list = new ArrayList<Object>();
                for (int j = 0; j < objectCount; j++) {
                    list.add(new Object());
                }
            }
        }
    };
    t.start();
}
final ArrayListPool<Object> pool = new ArrayListPool<Object>();
ThreadGroup group1 = new ThreadGroup("Group 1");
for (int i = 0; i < threadCount; i++) {
    Thread t = new Thread(group1, "Thread " + i) {
        @Override
        public void run() {
            for (int i = 0; i < listCount; i++) {
                ArrayList<Object> list = pool.getArrayList();
                for (int j = 0; j < objectCount; j++) {
                    list.add(new Object());
                }
                pool.releaseArrayList(list);
            }
        }
    };
    t.start();
}

Après exécution de ce code en tant que benchmark, voici les résultats :

threadCount listCount objectCount Temps avec pool (ms) Temps sans pool (ms)
100 100 100 65 82
10 100000 100 1961 2051
100 100000 100 17484 24950


Le temps d'exécution est légèrement réduit. En contrepartie, après analyse de l'application de test grâce au Profiler de NetBeans, la consommation mémoire est d'environ 2 Mo supplémentaires pour 100 objets en attente dans le pool. Cette solution est donc intéressante à mettre en place pour des objets dont l'instanciation est très fréquente. Dans le cas où les instanciations se font plus rares, le gain en temps CPU est négligeable, et la consommation mémoire est plus étalée dans le temps.

Permalink  |  Commentaires (2)

Exécuter du JavaScript en Java

Suite à un sujet sur le forum comp.lang.java.programmer, j'ai fait quelque recherches sur l'exécution de scripts (en particulier JavaScript) dans un environnement Java SE.

L'exécution de scripts dans un environnement Java est fourni par le package javax.script (depuis Java 6). Un moteur de script est défini par l'interface ScriptEngine, dont l'implémentation nous est fournie par la classe ScriptEngineManager. Voici donc à quoi ressemble l'exécution de code JavaScript dans un environnement Java.

1. Exécution simple de script

Dans un premier temps, cet exemple montre comment exécuter un script simple. Le but est d'exécuter le code JS suivant :
function myFunction() {
    return (4 + 5);
}
myFunction();

Le but de ce code est de déclarer une méthode qui renvoie le résultat de 4 + 5, puis d'appeler cette méthode.

Voici maintenant l'exécution du même code en Java, en affichant le résultat sur la sortie standard :
public static void main(String[] args) {
    try {
        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine engine = mgr.getEngineByName("JavaScript");
        String myJSCode = new StringBuffer()
                .append("function myFunction() {")
                .append("return (4 + 5);")
                .append("}")
                .append("myFunction();").toString();
        System.out.println(engine.eval(myJSCode));
    } catch (ScriptException ex) {
        ex.printStackTrace();
    }
}

Il est également possible de séparer la déclaration de la méthode de l'appel, ce qui permet plus de modularité (par exemple, nous déclarons une méthode une seule fois pour l'appeler plusieurs fois) :
public static void main(String[] args) {
    try {
        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine engine = mgr.getEngineByName("JavaScript");
        String myJSCode = new StringBuffer()
                .append("var i = 12;")
                .append("function myFunction() {")
                .append("return (i++);")
                .append("}").toString();
        engine.eval(myJSCode);
        System.out.println(engine.eval("myFunction();"));
        System.out.println(engine.eval("myFunction();"));
        System.out.println(engine.eval("myFunction();"));
    } catch (ScriptException ex) {
        ex.printStackTrace();
    }
}

L'exécution de script est donc très simple.

2. Partage de variables

En plus d'être simple, l'exécution de scripts est également très puissante. Il est possible de partager des références vers des objets entre notre code Java et notre code JavaScript.

Un petit exemple simple, mettant en scène un StringBuffer dans lequel nous allons rajouter des données en JavaScript :
public static void main(String[] args) {
    try {
        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine engine = mgr.getEngineByName("JavaScript");
        StringBuffer s = new StringBuffer();
        engine.put("s", s);
        engine.eval("s.append('toto');");
        System.out.println(s.toString());
    } catch (ScriptException ex) {
        ex.printStackTrace();
    }
}

Il est donc possible de modifier des objets Java directement à l'intérieur de notre moteur de script.

Enfin, un dernier point concerne les objets immuables (comme la classe String, par exemple). En effet, une instance de String ne peut pas changer de contenu. Un nouvel objet doit être créé. Dans ce cas, il est possible de récupérer un objet créé dans notre script :
public static void main(String[] args) {
    try {
        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine engine = mgr.getEngineByName("JavaScript");
        String str = "toto";
        engine.put("str", str);
        engine.eval("str = str.concat(' tata');");
        str = (String) engine.get("str");
        System.out.println(str);
    } catch (ScriptException ex) {
        ex.printStackTrace();
    }
}


L'exécution de scripts est donc assez aisée dans un environnement Java 6. L'API permettant cette exécution est à la fois simple et puissante, puisqu'elle permet même la modification d'objets Java.

Seul bémol, sur ma machine (JVM 1.6.0_06-b02, sous Linux), il semblerais que le moteur de JavaScript soit le seul disponible...

Permalink  |  Commentaires (0)