Migrer des EJB 2.x vers des EJB 3.1

EJB3

Introduction

Cet article a pour objectif de fournir un guide permettant de migrer des EJB 2.x vers des EJB 3.1 qui est le standard Java EE 6 (JSR 318).

Ce guide part du principe que votre projet contenant l’EJB est sous maven. La migration se fait d’un EJB en version 2.1 vers un EJB en version 3.1.

Historique et Nouveautés

Jusqu'à la version 2.1, les EJB bénéficiaient d'une mauvaise réputation à cause de leur complexité. Depuis la version 3.0, leur mise en place est grandement simplifiée : plus besoin de descripteur de déploiement, plus de home interface, plus besoin de faire un lookup via JNDI pour récupérer des EJBs. Il suffit de passer par l'annotation @EJB pour injecter l’EJB comme on pourrait le faire pour un service…

EJB 1.1 publié en décembre 1999, intégré dans J2EE 1.2 :

  • Session beans (stateless/stateful)
  • Entity Beans (CMP / BMP)
  • Interface Remote uniquement

EJB 2.0 publié en septembre 2001, intégré à J2EE 1.3 :

  • Message-Driven Beans
  • Entity 2.x reposant sur EJB QL
  • Interface Local pour améliorer les performances des appels dans la même JVM

EJB 2.1 publié en novembre 2003, intégré à J2EE 1.4 :

  • EJB Timer Service
  • EJB Web Service Endpoints via JAX-RPC
  • Amélioration du langage EJB QL

EJB 3.0, intégré à Java EE 5 :

  • Utilisation de POJO et POJI, plus d'interface Home
  • Utilisation des annotations, le descripteur de déploiement est optionnel
  • Utilisation de JPA pour les beans de type entity

EJB 3.1, intégré à Java EE 6

  • Il n'est plus nécessaire d'implémenter une interface locale pour écrire des sessions beans
  • Nouvelle annotation @Schedule (EJB Timer)
  • Les méthodes des sessions beans peuvent aussi être appelées de façon asynchrone (@Asynchronous)
  • EJB Singleton
  • Pour faciliter le déploiement, il est maintenant possible de packager / déployer des composants EJB 3 dans une archive WAR. Il n'est plus nécessaire de créer une archive JAR et une archive EAR, tout peut être contenu dans une unique archive WAR.
  • EJB Lite

Migrer un EJB 2.x vers un EJB 3.1

Cette migration vers un EJB 3.1 se fera avec l’utilisation d’un descripteur de déploiement XML et non en full annotations. Afin de pouvoir déployer cet EJB sous JBoss EAP 6.4.4, l’écriture du fichier de mapping jboss-ejb3.xml sera nécessaire.

Ajout du plugin maven au pom.xml : maven-ejb-plugin

 

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
           <plugin>
        <artifactId>maven-ejb-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
            <!-- Tell Maven we are using EJB 3.1 -->
            <ejbVersion>3.1</ejbVersion>
        </configuration>
           </plugin>
    </plugins>
</build>

 

Ajout d’une dépendance vers la javaee-api 6.0 (en provided)

Cette dépendance permettra de pouvoir accéder aux annotations, notament à «@EJB» pour pouvoir injecter son EJB.

<dependency>
	<groupId>javax</groupId>
	<artifactId>javaee-api</artifactId>
	<version>6.0</version>
	<scope>provided</scope>
</dependency>

 

Mise à jour du descripteur de déploiement xml : ejb-jar.xml

Mise à jour du XSD

<ejb-jar id="ejb-jar_ID"
      version="2.1"
      xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation=" http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd ">
      ...
</ejb-jar>
EJB en version 2.1
<ejb-jar id="ejb-jar_ID"
      version="3.1"
      xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
      ...
</ejb-jar>
Mise à jour - EJB version 3.1

Mise à jour du contenu du fichier XML

<enterprise-beans>
<session id="MessageProducerEJB">
		<ejb-name>MessageProducerEJB</ejb-name>
		<local-home>fr.bdf.nae.routage.services.MessageProducerEJBLocalHome</local-home>
		<local>fr.bdf.nae.routage.services.MessageProducerEJBLocal</local>
		<ejb-class>fr.bdf.nae.routage.services.MessageProducerEJBBean</ejb-class>
		<session-type>Stateless</session-type>
		<transaction-type>Container</transaction-type>
		...
		<resource-ref>
			<res-ref-name>jms/NAE_CF</res-ref-name>
			<res-type>javax.jms.QueueConnectionFactory</res-type>
			<res-auth>Container</res-auth>
			<res-sharing-scope>Shareable</res-sharing-scope>
		</resource-ref>
		...
	</session>	
</enterprise-beans>
EJB en version 2.1
<enterprise-beans>
	<session id="MessageProducerEJB">
		<ejb-name>MessageProducerEJB</ejb-name>
		<local>fr.bdf.nae.routage.services.MessageProducerEJBLocal</local>
		<ejb-class>fr.bdf.nae.routage.services.MessageProducerEJBBean</ejb-class>
		<session-type>Stateless</session-type>
		<transaction-type>Container</transaction-type>
		...
		<resource-ref>
			<res-ref-name>jms/NAE_CF</res-ref-name>
			<res-type>javax.jms.QueueConnectionFactory</res-type>
			<res-auth>Container</res-auth>
			<res-sharing-scope>Shareable</res-sharing-scope>
		</resource-ref>
		...
	</session>	
</enterprise-beans>
Mise à jour - EJB version 3.1

 

Mise à jour du session bean

public class MessageProducerEJBBean implements javax.ejb.SessionBean {

    private static final long serialVersionUID = 1L;

    private RoutageLogger logger = null;

    public void produceMessage(String messageContent, String factoryName, String destinationName, String replydestinationName) throws RoutageException, JMSException {

        logger.debug("produceMessage" + destinationName + " ; " + replydestinationName);

        Connection connection = null;
        Session session = null;
        Destination destination = null;

        //Destination destinationreply = null;
        MessageProducer producer = null;

        try {
            ServiceLocatorMQ slMQ = ServiceLocatorMQ.getInstance();

            //  Récupération de la destination dans la HashMap JNDI du Service
            // Locator
            destination = slMQ.getDestination(destinationName);

            //	Récupération d'une connection via le pool
            connection = slMQ.getConnection(factoryName);

            //	Récupération d'une session sur la connection
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            //	Creation du producer
            producer = session.createProducer(destination);
            producer.setPriority(Message.DEFAULT_PRIORITY);
            producer.setDeliveryMode(Message.DEFAULT_DELIVERY_MODE);
            producer.setTimeToLive(Message.DEFAULT_TIME_TO_LIVE);

            //	Creation du message
            TextMessage message = session.createTextMessage();
            message.setText(messageContent);

            //	Envoi du message
            producer.send(message);

        } catch (NamingException e) {
            throw new RoutageException(e);
        } catch (SingletonException e) {
            throw new RoutageException(e);
        } finally {
            if (producer != null) {
                producer.close();
            }
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }

    }
    
    private javax.ejb.SessionContext mySessionCtx;

    public javax.ejb.SessionContext getSessionContext() {
        return mySessionCtx;
    }

    public void setSessionContext(javax.ejb.SessionContext ctx) {
        mySessionCtx = ctx;
    }

    public void ejbCreate() throws CreateException {
        logger = new RoutageLogger();
    }

    public void ejbActivate() {
    }

    public void ejbPassivate() {
    }

    public void ejbRemove() {
    }
}
EJB en version 2.1
public class MessageProducerEJBBean implements MessageProducerEJBLocal{

    public MessageProducerEJBBean() {
    }
    
    private RoutageLogger logger = null;

    @Resource(name = "jms/NAE_CF")
    Connection connection;
    
    @Resource(name = "jms/NAE_NAE_ATENA_PUT_D")
    Queue destination;

    @Override
    public void produceMessage(String messageContent, String factoryName, String destinationName, String replydestinationName) throws RoutageException, JMSException {

        logger.debug("produceMessage" + destinationName + " ; " + replydestinationName);

        Session session = null;

        //Destination destinationreply = null;
        MessageProducer producer = null;

        try {
            // ServiceLocatorMQ slMQ = ServiceLocatorMQ.getInstance();

            // Récupération de la destination dans la HashMap JNDI du Service Locator
            // destination = slMQ.getDestination(destinationName);

            // Récupération d'une connection via le pool
            // connection = slMQ.getConnection(factoryName);

            //	Récupération d'une session sur la connection
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            //	Creation du producer
            producer = session.createProducer(destination);
            producer.setPriority(Message.DEFAULT_PRIORITY);
            producer.setDeliveryMode(Message.DEFAULT_DELIVERY_MODE);
            producer.setTimeToLive(Message.DEFAULT_TIME_TO_LIVE);

            //	Creation du message
            TextMessage message = session.createTextMessage();
            message.setText(messageContent);

            //	Envoi du message
            producer.send(message);

        } finally {
            if (producer != null) {
                producer.close();
            }
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }

    }    
}
Mise à jour - EJB version 3.1

 

Création du fichier de mapping : jboss-ejb3.xml

Le fichier jboss-ejb3.xml est un descripteur de déploiement personnalisé pouvant être utilisé dans un JAR EJB ou des archives WAR. Il permet de faire le mapping entre les noms JNDI et les références des resources déclarées dans le fichier ejb-jar.xml.

Dans une archive JAR EJB, il doit être situé dans le répertoire META-INF/. Dans une archive WAR, il doit être situé dans le répertoire WEB-INF/.

Le format ressemble à ejb-jar.xml, qui utilise les mêmes espace-noms et qui fournit des espace-noms supplémentaires. Les contenus de jboss-ejb3.xml sont mergés avec les contenus du fichier ejb-jar.xml et les items jboss-ejb3.xml ont la priorité.

<?xml version="1.1" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd
                        http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
    version="3.1" 
    impl-version="2.0">
    <enterprise-beans>
        <session>
            <ejb-name>MessageProducerEJB</ejb-name>
            <resource-ref>
                <res-ref-name>jms/NAE_CF</res-ref-name>
                <jndi-name>java:jboss/jms/NAE_CF</jndi-name>
            </resource-ref>
            ...
            ...
        </session>
        ...
        ...
    </enterprise-beans>
</jboss:ejb-jar>

 

Injection des dépendances

L'EJB déclare les ressources dont il a besoin à l'aide d'annotations. Le conteneur va injecter ces ressources lorsqu'il va instancier l'EJB donc avant l'appel aux méthodes liées au cycle de vie du bean ou aux méthodes métiers. Ceci impose que l'injection de ressources se fasse sur des objets gérés par le conteneur.

Ces ressources peuvent être de diverses natures : référence vers un autre EJB, contexte de sécurité, contexte de persistance, contexte de transaction, ...

Plusieurs annotations sont définies pour mettre en oeuvre l'injection de dépendances :

  • L'annotation @EJB permet d'injecter une ressource de type EJB.
  • L'annotation @Resource permet d'injecter une ressource qui est obtenue par JNDI (EntityManager, UserTransaction, SessionContext, ...)
  • ...

L'utilisation de l'injection de dépendances remplace l'utilisation implicite de JNDI.
L'injection peut aussi être définie dans le descripteur de déploiement.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.