Extensions de Petals ESB : LifeCycleListener

Petals ESB offre des possibilités d’extensions assez impressionnantes venant du fait que l’on utilise le Framework à composants Fractal (bon c’est pas Spring mais on peut faire des choses intéressantes avec des produits issus de la recherche). Dans cet article je vais m’intéresser à la customisation du démarrage et de l’arrêt de Petals en proposant une extension sympa et ce sans modifier énormément le code du kernel de Petals (Ca peut aussi servir de tutorial Fractal pour les débutants…).
Pourquoi? Parce-que actuellement pour moi Petals ESB est le framework SOA que j’étends pour lui ajouter des fonctionnalités qui ne sont pas fournies par défaut. La fonctionnalité du jour doit me permettre d’attendre que tous les composants par défaut soit démarrés pour démarrer les miens : Je dois installer des composants JBI (ne marchera pas si je laisse Fractal les installer trop tôt), lier des services (ne marchera pas si mes composants ne sont pas installés et si le service de management de Petals n’est pas démarré), lancer des tâches de fond, etc…

l’API du kernel de Petals propose l’interface org.ow2.petals.kernel.api.server.PetalsStateListener :

package org.ow2.petals.kernel.api.server;

/**
 * Listener notified when a Petals container finish to start or to stop.
 */
public interface PetalsStateListener {

    /**
     * Callback method on PEtALS start up
     */
    void onPetalsStarted();

    /**
     * Callback method on PEtALS stop
     *
     * @param success
     *            {@code true} if PEtALS has stopped successfully
     * @param exception
     *            the exception if the stop has failed, {@code null} otherwise
     */
    void onPetalsStopped(boolean success, Exception exception);
}
<pre>

Cette interface n’est malheureusement utilisée que statiquement et une seule fois pour informer le ‘launcher’ que le kernel est bien démarré. On a pourtant org.ow2.petals.kernel.api.server.PetalsServer qui définit la méthode addPetalsStateListener(PetalsStateListener). je vais donc utiliser cette API et modifier simplement l’implémentation de org.ow2.petals.kernel.api.server.PetalsServer pour ajouter des listeners qui seront appelés lors du démarrage et de l’arrêt de Petals.

Pour ce faire deux approches sont possibles :

  1. Rechercher tous les composants Fractal qui implémentent org.ow2.petals.kernel.api.server.PetalsStateListener
  2. Créer un manager qui contient une liste ordonnée de tous les org.ow2.petals.kernel.api.server.PetalsStateListener

On va ici se pencher sur la solution 2 car la solution 1 ne permet d’ordonner facilement les listeners à appeler (ca peut valoir le coup de les appeler dans un sens bien définit!).

1/ Le manager de listeners

Simplement un composant Fractal qui détient la liste des listeners définis. Ces listeners sont définis par configuration dans les fichiers ADL de Petals. Son interface :

package foo.bar.petals.kernel.listener;

import java.util.Set;

import org.ow2.petals.kernel.api.server.PetalsStateListener;

/**
 * @author chamerling - eBM WebSourcing
 *
 */
public interface LifeCycleListenerManager {

    /**
     * The binding prefix of the component which is listening petals state
     */
    static final String PREFIX = "petals-state-listener-";

    /**
     * Get an ordered set of listeners
     *
     * @return
     */
    Set<PetalsStateListener> getListeners();

}

et son implémentation :

package foo.bar.petals.kernel.listener;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.objectweb.fractal.fraclet.annotation.annotations.FractalComponent;
import org.objectweb.fractal.fraclet.annotation.annotations.Interface;
import org.objectweb.fractal.fraclet.annotation.annotations.LifeCycle;
import org.objectweb.fractal.fraclet.annotation.annotations.Monolog;
import org.objectweb.fractal.fraclet.annotation.annotations.Provides;
import org.objectweb.fractal.fraclet.annotation.annotations.Requires;
import org.objectweb.fractal.fraclet.annotation.annotations.type.Cardinality;
import org.objectweb.fractal.fraclet.annotation.annotations.type.Contingency;
import org.objectweb.fractal.fraclet.annotation.annotations.type.LifeCycleType;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.petals.kernel.api.server.PetalsStateListener;
import org.ow2.petals.util.LoggingUtil;

/**
 * @author chamerling
 *
 */
@FractalComponent
@Provides(interfaces = { @Interface(name = "service", signature = LifeCycleListenerManager.class) })
public class LifeCycleListenerManagerImpl implements LifeCycleListenerManager {

    @Monolog(name = "logger")
    private Logger logger;

    private LoggingUtil log;

    @Requires(name = PREFIX, signature = PetalsStateListener.class, cardinality = Cardinality.COLLECTION, contingency = Contingency.OPTIONAL)
    private final Map<String, Object> listeners = new Hashtable<String, Object>();

    @LifeCycle(on = LifeCycleType.START)
    protected void start() {
        this.log = new LoggingUtil(this.logger);
        this.log.debug("Starting...");
    }

    @LifeCycle(on = LifeCycleType.STOP)
    protected void stop() {
        this.log.debug("Stopping...");
    }

    /**
     * {@inheritDoc}
     */
    public Set<PetalsStateListener> getListeners() {
        HashSet<PetalsStateListener> result = new HashSet<PetalsStateListener>();
        if (this.listeners != null) {
            Iterator<String> iter = this.listeners.keySet().iterator();
            while (iter.hasNext()) {
                String key = iter.next();
                Object o = this.listeners.get(key);
                if (o instanceof PetalsStateListener) {
                    result.add((PetalsStateListener) o);
                } else {
                    // warning
                    this.log.warning(o + " is not an instance of "
                            + PetalsStateListener.class.getCanonicalName());
                }
            }
        }
        return result;
    }
}

A noter que la Map ‘listeners’ est remplie par Fractal au démarrage de Petals.

2/ Modification de PetalsServerImpl

Dans org.ow2.petals.kernel.server.PetalsServerImpl je rajoute la méthode initListeners() :

   private void initListeners() {
        // get the component which owns the listeners
        Component listenerManagerComponent = FractalHelper.getRecursiveComponentByName(
                this.petalsContentController, Constants.FRACTAL_LISTENERS_MANAGER);
        if (listenerManagerComponent != null) {
            try {
                LifeCycleListenerManager manager = (LifeCycleListenerManager) listenerManagerComponent
                        .getFcInterface("service");

                if (manager != null) {
                    Set<PetalsStateListener> listeners = manager.getListeners();
                    for (PetalsStateListener petalsStateListener : listeners) {
                        this.addPetalsStateListener(petalsStateListener);
                    }
                } else {
                }
            } catch (Exception e) {
            }
        }

    }

Ce code permet de retrouver mon ‘listener manager’ (puisque PetalsServerImpl n’est pas ‘Fractalisé’) et de remplir la liste des listeners. Allez maintenant, on va dire que j’appelle cette méthode d’initialisation juste avant d’appeler les listeners (dans start() de PetalsServerImpl):

    public void start() throws PetalsException {
        // get listeners which have been defined by configuration (Fractal)
        this.initListeners();

        // notify the listener if there is one
        for (PetalsStateListener petalsListener : this.petalsListeners) {
            petalsListener.onPetalsStarted();
        }
    }

3/ Configuration des listeners

Dans les fichiers de définition des composants Fractal, je peux maintenant instancier mes listeners et les ajouter à la liste des listeners gérée par le manager de listeners :

	<!-- lifeCycle Listeners -->
	<component definition="foo.bar.petals.kernel.FooService" name="Foo" />
	<component definition="foo.bar.petals.kernel.BarService" name="Bar" />

	<!-- Listeners manager -->
	<component definition="eu.soa4all.dsb.petals.kernel.listener.LifeCycleListenerManagerImpl" name="LifeCycleListenerManagerImpl" />

	<!-- Fill the map -->
	<binding client="LifeCycleListenerManagerImpl.petals-state-listener-foo" server="Foo.service" />
	<binding client="LifeCycleListenerManagerImpl.petals-state-listener-bar" server="Bar.service" />

Le composant foo.bar.petals.kernel.*Listener implémentent  org.ow2.petals.kernel.api.server.PetalsStateListener et sont ajoutés à la liste des listeners managés par LifeCycleListenerManagerImpl lors de la définition des liens (lignes ‘binding’). Tous les listeners ajoutés au manager doivent avoir une valeur de l’attribut client commençant obligatoirement par LifeCycleListenerManagerImpl.petals-state-listener- (remplissage de la Map par Fractal).

Facile et pratique, non?!

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s