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)

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)

JSF + Tiles, complément

J'avais déjà parlé de coupler les avantages de JSF et Tiles, je reviens aujourd'hui rajouter un petit complément.

J'avais en effet parlé d'un inconvénient majeur, l'impossibilité de lier directement le rendu d'une page à une définition de Tiles.

J'ai découvert aujourd'hui, en lisant le tutoriel sur le site Tiles, que l'API de Tiles fournissait une Servlet qui liait les URL de type *.tiles vers les définitions correspondantes. Par exemple, grâce à cette Servlet, il est par exemple possible d'accéder à une définition nommée index via l'URL /index.tiles.

Après quelques tests, il est possible de coupler cette Servlet avec JSF.

Première étape : Déclarer la Servlet de Tiles

Dans web.xml :
<servlet>
    <servlet-name>Tiles Dispatch Servlet</servlet-name>
    <servlet-class>org.apache.tiles.web.util.TilesDispatchServlet</servlet-class>
</servlet>
...
<servlet-mapping>
    <servlet-name>Tiles Dispatch Servlet</servlet-name>
    <url-pattern>*.tiles</url-pattern>
</servlet-mapping>

Ainsi, toutes les URL *.tiles seront liées aux définitions correspondantes.

Deuxième étape : Configurer JSF pour utiliser les URL *.tiles

La seconde étape va consister à modifier légèrement le comportement de JSF. Par défaut, pour générer la réponse d'une requête index.jsf, le framework va chercher la page index.jsp. Nous allons maintenant lui dire d'aller chercher index.tiles à la place. Requête à laquelle Tiles répondra en introduisant une définition (la définition nommée index dans notre cas).

Il faut pour cela modifier une propriété propre à JSF :
<context-param>
    <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
    <param-value>.tiles</param-value>
</context-param>


Et voilà. Il ne reste plus qu'à essayer d'accéder à index.jsf. Le FacesContext est chargé, puis la main est passée à Tiles pour le rendu. Il suffit de déclarer nos JSP et nos définitions comme je l'ai précisé dans la première version.

Permalink  |  Commentaires (0)

JPA : Utilisation en dehors du module EJB

Récemment, j'ai eu à utiliser JPA pour un site Web. Pour plus de simplicité, je me suis tourné vers un conteneur EJB. Seulement, j'ai eu aussi besoin de créer une multitude de Stateful / Stateless Session Beans, alors que je n'avais vraiment pas besoin de ça. Une application Web toute simple me suffisait largement dans ce cas là.

J'ai alors entrepris de convertir mon application d'entreprise en application Web. Seulement, un problème est apparu assez rapidement : la gestion des transactions de JPA ! En effet, les transactions dont JPA à besoin étaient jusqu'à lors gérées par le conteneur EJB. QUe je n'utilisais plus maintenant.

Je vais donc entreprendre dans cet article un rapide "howto" de l'utilisation de JPA en dehors du module EJB.

1. Mise en place des entités

La première partie est commune à nos deux modes de fonctionnement. Il s'agit de créer nos entités JPA et de créer notre unité de persistance.

On commence avec une entité :
package com.aperigeek.jpa.entity;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    // Getters and setters

}

Puis, on passe à la création de notre unité de persistance (dans notre fichier persistence.xml) :
<persistence version="1.0" 
    xmlns="http://java.sun.com/xml/ns/persistence" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="AperigeekPU" transaction-type="RESOURCE_LOCAL">
    <provider>oracle.toplink.essentials.PersistenceProvider</provider>
    <non-jta-data-source>jdbc/aperigeek</non-jta-data-source>
    <class>com.aperigeek.jpa.entity.User</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <property name="toplink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

La différence par rapport à une unité de persistance gérée par le conteneur EJB est que nous n'allons par utiliser JTA pour la gestion des transactions. Les changements sont :
  • Le transaction-type de notre unité de persistance devient "RESOURCE_LOCAL", pour indiquer que nous allons gérer nous même nos transactions.
  • La DataSource est déclarée grâce aux balises <non-jta-data-source />,. Les DataSources que nous allons utiliser ne seront pas gérées par JTA.


À noter que j'utilise dans cet exemple Toplink (le gestionnaire de persistence par défaut de GlassFish). Les valeurs de la balise <provider> et de la propriété toplink.ddl-generation sont donc spécifique à Toplink.

2. Récupération d'un EntityManager

Étant donné que nous ne sommes plus dans un contexte géré par le conteneur, nous allons devoir récupérer nos EntityManagers à la main. Le code pour récupérer un EntityManager est assez simple. Il suffit de passer par un EntityManagerFactory, qui nous est fourni directement par une méthode statique de la classe Persistence.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("AperigeekPU");
EntityManager em = emf.createEntityManager();

Autre étape très importante : tous les EntityManagerFactory et EntityManager doivent être fermés ! Cette étape de fermeture des ressources est d'habitude laissée au conteneur. Dans notre cas, nous allons devoir le faire nous même :
em.close();
emf.close();

Inconvénient propre à cette technique : une fois tous les EntityManagerFactory et EntityManager fermés, le gestionnaire de persistance s'arrête, pour redémarrer à la prochaine création d'un EntityManagerFactory. Je suis donc parti dans la création d'une Servlet, qui aura pour mission de créer un EntityManager au démarrage de l'application, et de le libérer à l'arrêt de l'application.

3. Servlet de chargement, et classe PersistenceManager

Avant de créer une Servlet, j'ai d'abord créé une classe, PersistenceManager, chargée de créer des EntityManagerFactory et EntityManager, et de les fermer ensuite.

Le code de cette classe est assez simple :
public class PersistenceManager {
    
    private static EntityManagerFactory emf;
    
    private static EntityManager em;
    
    public static EntityManager getEntityManager() {
        return (em);
    }
    
    public static void loadEntityManager() {
        emf = Persistence.createEntityManagerFactory("AperigeekPU");
        em = emf.createEntityManager();
    }
    
    public static void releaseEntityManager() {
        em.close();
        emf.close();
    }

}

Nous allons maintenant créer une Serlvet, qui devra appeler les méthodes PersistenceManager.loadEntityManager() au chargement de l'application et PersistenceManager.releaseEntityManager() au déchargement :
public class PersistenceLoaderServlet extends GenericServlet {

    @Override
    public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void init() throws ServletException {
        PersistenceManager.loadEntityManager();
    }

    @Override
    public void destroy() {
        PersistenceManager.releaseEntityManager();
    }

}

Suivie d'une déclaration très simple dans web.xml :
<servlet>
    <servlet-name>PersistenceLoaderServlet</servlet-name>
    <servlet-class>com.aperigeek.jpa.servlet.PersistenceLoaderServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

Deux choses sont notables :
  • L'attribut load-on-startup est à 2, pour permettre à la Servlet d'être chargée au déploiement de l'application.
  • Il n'y a aucun mapping pour cette Servlet. Elle n'est en effet pas vouée à être appelée pour répondre à une requête. C'est pour cette même raison que la méthode service() de notre servlet lève une exception.

4. Utilisation de notre EntityManager

Et enfin, dernière étape, utilisation de notre EntityManager. Nous allons devoir :
  • Récupérer nos EntityManagers à la main. N'étant plus gérés par le conteneur, nous n'allons pas pouvoir les injecter en tant que dépendance dans nos beans.
  • Gérer les transactions. Pour la même raison que ci-dessus, les transactions devront être gérées à la main.

Un rapide exemple, pour stocket un User en base de données :
public void addUser(User user) {
    EntityManager em = PersistenceManager.getEntityManager();
    em.getTransaction().begin();
    em.persist(user);
    em.getTransaction().commit();
}

On récupère ici nos EntityManagers grâce à la méthode PersistenceManager.getEntityManager(). Ensuite, avant chaque opération en base de données, nous devons démarrer une transaction (em.getTransaction().begin();), puis commiter les modifications (em.getTransaction().commit();).


Voilà rapidement les étapes à suivre pour utiliser JPA dans un contexte non-géré par le conteneur. Les même étapes peuvent être suivies pour utiliser JPA dans une application Java SE, en modifiant la Servlet par un autre mécanisme permettant de charger notre EntityManager au lancement et de le libérer à la fermeture de l'application.

Permalink  |  Commentaires (0)

Glassfish et NetBeans : Présent et futur

Nous sommes dans une période de sortie ! Hier, NetBeans 6.1 pointait le bout de son nez. Aujourd'hui, c'est GlassFish v2ur2 qui fait son entrée. Période idéale pour faire un point sur ces deux nouvelles versions, puis pour regarder un peu en avant afin de voir ce que l'avenir nous réserve...

NetBeans

La version 6.1 apporte son lot de fonctionnalités. J'en avais déjà parlé, les améliorations sont au rendez vous.

Parmi toutes les nouvelles fonctionnalités, mes préférées :
  • Amélioration des performances
    • Démarrage (beaucoup) plus rapide
    • Consomme moins de mémoire
    • Beaucoup plus réactif
  • Support de MySQL
    • Administration de serveur MySQL intégré
  • Partage de projets
    • Les librairies sont directement inclues dans votre projet
    • Les chemins pour accéder aux librairies sont relatifs

La liste complète est disponible sur le site de NetBeans.

Quelques nouvelles fonctionnalités qui viendront après la version 6.1 sont déjà prévues :
  • Support de PHP
    • Auto-complétion, coloration syntaxique, ...
    • Débuggeur de JavaScript
  • Amélioration de l'éditeur SQL
    • L'éditeur SQL devrait fonctionner main dans la main avec l'éditeur PHP
    • L'auto-complétion de requêtes SQL devrait apparaître
  • Amélioration des performances (encore)

GlassFish

La version 2ur2 n'est qu'une mise à jour de maintenance. Les nouveautés ne sont donc pas très nombreuses.

Cependant, GlassFish 3 se prépare, et nous réserve d'ores et déjà de belles surprises.

La première amélioration, et pas des moindres, GlassFish v3 sera beaucoup plus rapide à lancer. Les premiers tests tendent vers un temps démarrage du serveur d'application inférieur à une seconde. Comme quoi beaucoup de progrès ont été faits.

De plus, GlassFish devrait être embarquable dans n'importe quelle autre application Java. Un petit exemple de ce qui pourrait être fait grâce à GlassFish v3 (via Bistro !) :
GlassFish glassfish = new GlassFish();
glassfish.minimallyConfigure(8080);

GFApplication app = glassfish.deploy(new File("mon_appli.war"));

// ...

app.undeploy();
glassfish.stop(); 

Une fonctionnalité assez pratique. Tomcat était déjà assez souvent utilisé en tant que conteneur de Servlet embarqué. C'est maintenant tout un serveur d'application qui pourra être embarqué dans une application.

Pour résumer, les futures version de NetBeans et de GlassFish tendent toutes les deux vers une amélioration des performances, mais réservent elles aussi leur lot de surprises.

Permalink  |  Commentaires (0)