Vidéos : Monitoring et Management de Petals ESB


Ou plutôt de distributions customisées et étendues de Petals ESB. Ces deux vidéos illustrent une petite partie de ce que je fais depuis quelque temps sur le projet SOA4All où j’utilise Petals ESB en tant que base pour une infrastructure de service largement distribuée.

Monitoring

Management

La suite au prochain épisode…

Sur l’utilité d’utiliser des standards « Internet-Friendly »…


… et aussi sur l’utilité de développer ses applications de facon générique. La preuve par l’exemple : L’administration du bus de service Petals ESB.

JMX c’est bien pour manager une application Java mais il faut savoir parler JMX et généralement, parler à une application JMX via Internet c’est pas forcément le pied et encore moins quand l’administrateur réseau ne veut pas qu’il y ait autre chose qui rentre chez lui que du bon vieux HTTP et être ce que j’appelle ici ‘Firewall-Friendly’.

Tentative de passage de Firewall

Pas Firewall-Friendly?

Bref, je milite ici sur l’utilité d’avoir une administration d’application générique qui peut être exposée dans la technologie et le protocole qui colle le mieux au contexte. Et la je parle principalement de ce que je connais le mieux : Petals ESB. Ce bus de service est fortement basé sur la spécification JBI. Il est tellement lié que l’administration du bus ne passe que par JMX, même pour effectuer des opérations internes au bus… L’administration ne doit donc pour moi être fournie que sous forme d’API et d’implémentation indépendante de toute technologie d’exposition. A charge du développeur de l’application de fournir les mécaniques adéquates pour exposer cette API en JMX, en Web Services, en REST, etc… Cela fera surement l’objet d’un article sur une mise en pratique de ce genre d’approche dans Petals.

Le but principal et original de cet article était de parler un peu de ce que l’on peut faire en terme d’administration et de monitoring du Bus Petals. Je travaille depuis quelques temps déjà sur un projet visant à créer une infrastructure de service largement distribuée sur Internet dans le cadre du projet de recherche SOA4All. Je viens de finir un prototype d’application Web qui permet de manager ces noeuds exposés sur Internet et d’effectuer quelques opérations qui ne sont pas disponibles dans une distribution standard de Petals (tout du moins l’API de base ne les fournies pas). Comment? Et bien forcément sans utiliser JMX (j’en suis assez allergique ce qui rajoute un allergène à ma liste déjà longue, en plus c’est le printemps…), mais plutôt en utilisant et en étendant l’API Web service que j’ai développé pour Petals 3.0. Cette application permet alors de se connecter à n’importe quelle instance de Petals (et à son réseau) exposée sur Internet via une API WS et ainsi de lier des services externes au bus, d’exposer des services internes, de voir ce qui se passe dans le bus et bien plus encore… Tout cela en utilisant GWT pour créer une application sympa avec des choses qui bougent car Petals à une activité qu’il faut pouvoir montrer en direct!

DSB Manager Portal

DSB Manager Portal

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?!

Outils Petals ESB : Librarie pour binder des services Web


Dans un précédent article, je décrivais comment ‘proxifier’ un Web service dans Petals ESB avec un plugin Maven maison. Ce plugin Maven utilise une librairie Java que j’ai développé pour lier et exposer des Web services dans/avec Petals ESB.

Le ‘problème’ actuel de Petals ESB est qu’il est pleinement basé sur la spécification JBI, cela veut dire que actuellement, si on veut exposer un service JBI en tant que Web service, il faut créer un fichier de configuration (le fameux JBI.xml), le packager dans une unité de service (Service Unit – SU), et packager cette SU dans un assemblage de services (Service Assembly – SA). Bien sur des outils comme le Petals Studio sont la pour aider les développeurs à intégrer leurs services, moi je me place plus dans la peau du ‘Core Developper’ et je pour le moment je n’ai pas eu l’utilité d’outils graphiques pour configurer Petals.

Dans une de mes taches actuelles, je suis en train d’étendre Petals pour le faire communiquer sur Internet et je développe ce que j’appelle l’API de Monitoring & Management (M&M API) qui est exposée (pour le moment seulement) en Web service. La partie Management de l’API doit permettre à un administrateur de la plateforme, de ‘binder’ des services (Web, REST, …) sans se soucier de la partie JBI.
L’API fournit donc une opération du style bind(URI wsdl). Coté serveur, j’utilise :

  1. Une librairie maison de génération de SA
  2. Je déploie localement cette SA pour ‘binder’ un Web service au bus.
  3. J’expose ce service de management avec la feature expliquée dans ce post

Un exemple d’utilisation de la librairie est donné dans le listing suivant (binder au bus, le Service Web Version de Axis2) :


package org.ow2.petals.tools.generator.jbi;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import org.ow2.petals.tools.generator.jbi.api.JBIGenerationException;
import org.ow2.petals.tools.generator.jbi.ws2jbi.WS2Jbi;

public class BindAxis2Version {

public static void main(String[] args) throws JBIGenerationException, URISyntaxException {

String wsdl = "http://localhost:8080/axis2/services/Version?wsdl";
Map<String, String> map = new HashMap<String, String>();

// let's say that we want to generate SA for SOAP component 4.0
map.put(org.ow2.petals.tools.generator.commons.Constants.COMPONENT_VERSION, "4.0");

URI wsdlURI = new URI(wsdl);

File f = new WS2Jbi(wsdlURI, map).generate();

System.out.println("Service Assembly is available at " + f.getAbsolutePath());

}
}

Pour utiliser WS2Jbi, utilisez dans votre projet Maven, la dépendance :


<dependency>

<groupId>org.ow2.petals.sandbox</groupId>

<artifactId>petals-ws2jbi</artifactId>

<version>1.0-SNAPSHOT</version>

</dependency>

Celle ci est normalement disponible est mise a jour régulièrement sur le repository Maven d’OW2 http://maven.ow2.org/maven2-snapshot/. Les sources sont disponibles dans ma Sandbox Petals.

PEtALS ESB Live Monitoring with WSDM and GWT


In one of my previous posts (Adding Registry Listener in PEtALS), I spoke about adding a registry listener in PEtALS ESB. In the current article, I want to introduce how I used this feature to implement a live monitoring Web application.

Here are the different modules which are used in this Live Monitoring Tool :

  1. PEtALS ESB. The standard behaviour has been customized by adding a registry listener and a routing module (to be detailled below).
  2. Monitoring layer. This layer is an independant process which embeds a WS-Notification engine.
  3. A WS-notification subscriber. This is the module which will receive the notifications from the monitoring layer.
  4. GWT based Web application used to display monitoring data.

PEtALS ESB Extensions

Registry Listener
The role of the registry listener is to register a new monitoring endpoint into the monitoring layer when a new endpoint is available within PEtALS.

Routing Module
Since modules can be added dynamically inside the PEtALS message router, we have created a module which timestamp the messages. Once the message exchange is complete, a message exchange report is sent to the monitoring layer.

Monitoring Layer

The monitoring is in charge of creating monitoring endpoints through a management API. Once a monitoring endpoint is created, it is also exposed as a Web service. This newly created Web service exposes a WS-notification subscribe operation.
Another role of the monitoring layer is to receive raw reports from the PEtALS ESB, to process the report in order to generate a WSDM payload which will be send to subscribers.

WS-Notification subscriber

The subscriber subscribes, receives and stores notifications from the Monitoring layer. That’s all for that module ;o)

GWT Based Web application

The GWT Web application uses comet in order to display live service response time. The data used to display response time is the one received and stored into the database and of course the server part of the Web application have access to this database.

As a result, we have a really nice live Web application (live means that the chart gives real time result and is updated automatically when messages are exchanged within PEtALS Service Bus). Here are some screenshots :

Live Response Time

Live Response Time

SLA

SLA Violation