JMX pour les nuls... - Les connecteurs - Partie 8

left

Ce huitième et dernier article sur JMX clôture cette petite série de posts sur JMX (cf. introduction, partie 1 portant sur les généralités, partie 2 portant sur les différents MBeans et le concept de Notification, partie 3 sur les agents JMX, partie 4 sur les classes de base, partie 5 sur le MBeanServer, partie 6 sur le chargement dynamique des MBeans et partie 7 sur les services JMX). Il abordera succinctement la notion de connecteur.

Table des matières

Les connecteurs

La spécification JMX défnit la notion de connecteurs : un connecteur est attaché à l’API JMX d’un MBean Server et le rend accessible de clients Java distants, le connecteur client possèdant une interface similaire à celle du MBean Server.

Un connecteur est donc constitué d’une partie cliente et d’une partie serveur :

  • Le connecteur serveur est attaché au MBean Server et écoute les requêtes de connexion des clients
  • Le connecteur client a pour rôle de trouver le server et d’étable une connexion avec ce dernier. Un connecteur client se trouve généralement dans une JVM différente du connecteur serveur et s’exécutera même généralement sur une machine différente.

Il est à noter que contrairement à un connecteur serveur, le connecteur client ne pourra se connecter qu’à un et un seul connecteur serveur (mais une application cliente pourra posséder plusieurs connecteurs clients).

JMX permet d’avoir de multiples connecteurs qui peuvent s’appuyer sur différents protocoles de communication entre le client et le serveur. Cependant, il définit le protocole standard et obligatoire sur tous serveurs JMX s’appuyant sur RMI (Remote Method Invocation) et le protocole optionel s’appuyant sur des sockets TCP et appelé JMXMP (JMX Messaging Protocol).

Les connecteurs distinguent deux notions distinctes qui sont la session et la connexion. Un connecteur client voit une session qui peut, tout au long de son cycle de vie de la session, avoir plusieurs connexions successives à un connecteur serveur. Il est à noter que, dans certains cas, il est possible d’avoir une connexion par requête (comme cela peut être le cas avec le protocole UDP ou JMS).

Une session possède un état sur le client mais pas nécessairement sur le connecteur serveur. Un connecteur, ne possède, quant à lui, pas forcément d’état.

Pour se connecter au connecteur serveur, le connecteur client peut utiliser une adresse du type service:jmx:jmxmp://host1:9876. Si la requête de connexion aboutie, le point d’accès client du connecteur client est retourné en réponse.

medium

Du point de vue de la connexion cliente, le code de l’utilisateur peut obtenir un objet qui implémente l’interface MBeanServerConnection. Cette interface étant similaire à l’interface MBeanServer, il devient transparent pour le code de l’application cliente d’interagir avec un MBean Server ne se trouvant pas dans la même JVM.

En fait, l’interface MBeanServerConnection est étendue par l’interface MBeanServer et contient donc toutes les méthodes principales sauf celles qui n’ont de sens que pour une utilisation locale.

medium

Lors de l'enregistrement d'un écouteur auprès du MBeanServerConnection, le connecteur s'assure de la transmission de la notification de la partie serveur du connecteur à la partie cliente, puis à l'écouteur.
Les détails de la mise en œuvre de la transmission de la notification sont dépendants du protocole.

Lors de l'utilisation d'un filtre de notifications, le filtrage des notifications reçues peut être effectué soit par la partie cliente du connecteur, soit par le MBean Server suite à la transmission du filtre.
Il est, bien sûr, à noter que le filtrage des notifications coté serveur évite la transmission des notifications sur le réseau.
Cependant, il est conseillé de rendre les filtres indépendants de leur lieu d'exécution (pour rappel, l'interface NotificationFilter étend l'interface Serializable).

Toutes les implémentations des connecteurs coté serveur doivent posséder un buffer de notifications qui correspond à la liste de toutes notifications émises par les MBeans à destination du MBean Server. Il est à noter que les connecteurs doivent supporter les requêtes concurrentes pour des raisons évidentes. Concernant l’adresse du serveur connecteur, il est conseillé, pour la générer, de passer par la classe JMXServiceURL.

medium

Afin de créer la partie cliente d’un connecteur, il existe deux possibilités :

  • si l’adresse du serveur est connue, la méthode connect(JMXServiceURL) de la classe statique JMXConnectorFactory peut être utilisée,
  • sinon, il est possible d’interroger le serveur pour obtenir un stub de JMXConnector.

Du point de vue de la connexion serveur, la connexion serveur est représentée par une sous-classe de JMXConnectorServer qui peut être obtenue :

  • soit à l’aide de la classe statique JMXConnectorServerFactory en lui indiquant le paramètre de type JMXServiceURL qui lui permet de connaitre les classes à instancier,
  • soit en instanciant directement la sous-classe de JMXConnectorServer.

Le connecteur serveur doit être attaché à un MBean Server (ce qui ne signifie pas qu’il doit s’enregistré en son sein) et doit être actif pour être utilisable. Il peut être attaché auprès du MBean Server de deux manières :

  • Le MBean Server auquel est attaché le connecteur serveur lui est spécifié lors de sa construction
  • Le connecteur serveur est enregistré auprès du MBean Server comme s’il s’agissait d’un MBean

medium

medium

Exemple de création et d’attachement d’un connecteur serveur auprès d’un MBean Server

MBeanServer mbs = MBeanServerFactory.createMBeanServer();
JMXServiceURL addr = new JMXServiceURL("jmxmp", null, 0);
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(addr, null, mbs);
cs.start();

Exemple de création et d’enregistrement d’un connecteur serveur dans un MBean Server

MBeanServer mbs = MBeanServerFactory.createMBeanServer();
JMXServiceURL addr = new JMXServiceURL("jmxmp", null, 0);
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(addr, null, null);
ObjectName csName = new ObjectName(":type=cserver,name=mycserver");
mbs.registerMBean(cs, csName);
cs.start();

JMX définit également comment un agent peut publier son serveur de connexion dans une infrastructure de recherche et de découverte ; ce qui permet à l’API cliente distante de savoir où trouver un serveur et ainsi de pouvoir si connecter. Cela couvre :

  • SLP (Service Location Protocol) en décrivant comme un agent enregistre le service URL d’un connecteur serveur avec SLP
  • Jini en décrivant comment un agent enregistre un stub du connecteur serveur dans le service de recherche Jini
  • JNDI (Java Naming and Directory Interface) en décrivant un agent enregistre le service URL d’un connecteur dans un annuaire LDAP
Un connecteur client peut spécifier un class loader par défaut lorsqu'il effectue sa connexion au serveur. Ce class loader est alors utilisé lors de la déserialisation des objets reçus du serveur que ce soit les valeurs retournées, les exceptions ou les notifications.
Le class loader par défaut est positionné par la valeur de l'attribut jmx.remote.default.class.loader définie par l'environnement JMXConnector, sinon, il s'agit du class loader du contexte.

Le class loader utilisé par le connecteur serveur pour déserialiser les paramètres reçus par le connecteur client est dépendant de l'opération : en effet, parfois, il peut s'agir de celui du MBean cible (si les types de paramètres ne sont pas définis par l'API JMX), parfois, de celui configuré pendant la création du connecteur server (s'il est prévu de l'utiliser dans une application de gestion particulière qui peut définir ses propres sous classes de types de paramètres de MBean ou NotificationFilter).
Le connecteur serveur possède un class loader par défaut qui est déterminé lors de son démarrage comme suit :
  • Si l'attribut jmx.remote.default.class.loader de l'environnement existe, sa valeur détermine le class loader à utiliser
  • Si l'attribut jmx.remote.default.class.loader.name de l'environnement existe, sa valeur détermine l'ObjectName du MBean à utiliser comme class loader par défaut. Cette fonctionnalité permet à un connecteur serveur d'être créé avec un class loader qui est un m-let dans le même MBean Server
  • Sinon, c'est le class loader du thread contextuel qui est utilisé
Pour plus d'informations sur l'utilisation de class loader étendu, il est recommandé de se référer au chapitre 13.11 Class Loading de la spécification JMX.

Afin d'éviter les difficultés avec le fonctionnement des class loader Java, il est conseillé de n'utiliser que les types standards Java ou fournis par l'API JMX ou les Open MBeans qui permettent des structures plus complexes. De même, pour les notifications, il est préconisé de n'utiliser que les notifcations suivantes :
  • NotificationFilterSupport
  • MBeanServerNotificationFilter
  • AttributeChangeNotificationFilter

Le connecteur serveur est, en outre, capable d’authentifier le client distant. Par exemple, pour le connecteur RMI, cela est fait fournissant une implémentation de l’interface JMXAuthenticator lors de la création du connecteur serveur. Dans le cas du connecteur JMXMP, cela est fait via SASL. Dans ces deux cas, le résultat de l’authentification renvoie un Subject JAAS qui représente l’identité authentifiée. Les requêtes alors reçues du client sont exécutées en utilisant cette identité. Avec JAAS, il est possible de définir les permissions liées à une identité. En particulié, il est possible de restreindre l’accès aux opérations du MBean Server en utilisant la classe MBeanPermission. Bien sûr, pour que cela fonctionne, il est nécessaire d’avoir un SecurityManager. Dans le cas où le connecteur serveur ne supporte pas l’authentification ou qu’il n’est pas configuré, les requêtes clientes sont exécutées en utilisant la même identité que celle qui a été créée avec le connecteur serveur. Comme alternative à JAAS, il est possible de controler l’accès aux opérations du MBean Server en utilisant un MBeanServerForwarder. Cet objet est une implémentation de l’interface MBeanServer et transmet les méthodes à un autre objet MBeanServer mais en ajoutant des opérations avant et après la transmission. Cela permet, en l’occurrence d’y faire des controles d’accès. Pour ajouter un MBeanServerForwarder, la méthode setMBeanServerForwarder peut être utilisée. De plus, pour chaque connexion à un connecteur serveur, au moins un Subject authentifié est néccessaire. Cela signifie que si un client effectue des opérations qui nécessitent plusieurs identités, des connexions différentes doivent être établies. Cependant, il est possible aux connecteurs (tels que les connecteurs RMI et JMXMP) d’utiliser la notion de délégation de Subject. Pour ce faire, avec chaque requête, le client spécifie un Subject. La requête est alors exécutée en utilisant une identité pour chaque requête si cela est possible. La permission a utilisé doit alors être de type SubjectDelegationPermission. Dans ce cas, pour chaque Subject délégué, le client obtient un MBeanServerConnection du JMXConnector pour un Subject authentifié. Les requêtes utilisées par le MBeanServerConnection sont émises avec un Subject délégué. Il est, de plus, à noter que ces objets de type MBeanServerConnection peuvent être obtenus du même JMXConnector et peuvent être utilisés simultanément. Ainsi, pour résumer, il est possible de configurer les permissions d’un connecteur serveur de deux manières :

  • toutes les permissions pour les opérations de chaque client distant doivent être configurées,
  • un SubjectDelegationPermission pour chaque Principal utilisé par les clients distants doit être configuré.
Le connecteur RMI est le seul connecteur qui doit être présent dans les implémentations de JMX. Il utilise l'infrastructure RMI (Remote Method Invocation) pour la communication entre le client et le serveur.
RMI définit deux couches de transport standard :
  • Java Remote Method Protocol (JRMP) qui est le protocole de transport par défaut
  • Internet Inter-ORB Protocol (IIOP) qui est le protocole défini par CORBA et qui permet l'interopérabilité avec un autre langage de programmation
Pour chaque serveur de connexion RMI, un objet distant implémentant l'interface distante RMIServer existe. Un client voulant communiquer avec le connecteur serveur doit obtenir une référence distante (ou stub) connecté à cet objet distant. Via RMI, chaque méthode appelée sur un stub sera transféré à l'objet distant. Ainsi, le client possédant un stub sur l'objet RMIServer pourra appeler obtenir le même résultat que s'il avait appelé l'objet du serveur.
De plus, il existe un objet distant pour chaque connexion cliente qui permet de créer un nouvel objet distant qui implémente l'interface distante RMIConnection.
Enfin, il est à noter que le code de l'utilisateur n'interagit généralement pas directement avec les objets du RMIServer ou du RMIConnection : en général, le stub RMIServer est obtenu au travers d'un objet RMIConnector qui implémente l'interface JMXConnector.
L'obtention de cet objet RMIConnector peut être fait de trois manières :
  • En fournissant un JMXServiceURL au JMXConnectorFactory qui spécifie le protocol RMI ou IIOP.
  • En obtenant le stub JMXConnector de " quelque part " (annuaire LDAP, Service de lookup JNDI, …).
  • En obtenant un stub RMIServer de " quelque part " et qui peut être utilisé comme paramètre du constructeur de RMIConnector.
Les notions de sécurité et de versionnement sont également décrites dans le spécification JMX (et plus particulièrement la JSR160). Cependant, ce document n'abordera pas ces points.

La spécification JMX (et plus précisément la JSR160) décrit comment implémenter un connecteur générique en précisant comment configurer par plugin son protocole de transport ainsi que la manière dont il encapsule les objets transitant du client au serveur et plus particulièrement sa gestion du classloader du MBean cible.
Cependant, ce document n'abordera pas ces points. Aussi, pour plus d'informations, il est conseillé de se référer à la JSR160 ou aux chapitres 15 - Generic Connector et 16 - Defining a new transport des spécifications agrégées JMX.

Le connecteur JMXMP est une configuration particulière du connecteur générique où le protocole de transport s’appuie sur TCP et l’encapsulation des objets est la sérialisation native Java.

La spécification JMX ne spécifie pas comment trouver l'adresse d'un serveur de connecteurs attaché à un agent connu ou comment découvrir les agents présents dans le système ainsi que ce qu'ils proposent. Cependant, la JSR160 propose trois infrastructures permettant de répondre à ces besoins :
  • SLP (Service Location Protocol)
  • JINI
  • JNDI (Java Naming and Discovery Interface) utilisé au travers un annuaire LDAP

Bien sûr, les APIs pour utiliser ces différentes technologies diffèrent cependant, les principes généraux restent identiques :
  • L'agent créé un ou plusieurs serveurs de connecteurs
  • Pour chacun des connecteurs à exposer, il expose soit le JMXServiceURL (dans le cas de SLP ou JNDI), soit le stub JMXConnector (dans le cas de JINI ou JNDI) qu'il enregistre dans le service de recherche (en spécifiant au besoin d'autres méta-données)
  • Le client interroge le service de recherche pour obtenir un ou plusieurs JMXServiceURL ou stub JMXConnector
  • Le client utilise soit le factory JMXConnectorFactory pour obtenir un JMXConnector connecté au serveur s'il possède un JMXServiceURL, soit se connecte directement au serveur en utilisant le stub JMXConnector
Ce document ne fournira pas plus d'informations sur ces points. Aussi, pour plus d'informations, il est conseillé de se référer à la JSR160 ou au chapitres 17 - Bindings to lookup services des spécifications agrégées JMX.

Le mot de la fin

Voilà, ce dernier article met fin à cette série. Si vous avez réussi à tenir jusque là et à tout lire, félicitation! Maintenant, vous devriez avoir (enfin, j’espère) une vision plus précise sur ce que contiennent les spécifications JMX. Bien sûr, cette série n’était qu’un condensée (qui vaut ce qu’il vaut) permettant de comprendre les concepts sans pour autant lire les 290 pages de spécification et ne se veut pas être suffisante pour tout maîtriser.

 Share!

 
comments powered by Disqus