Cours 3 Architectures logicielles dynamiquement adaptables II

Réflexivité comportementale en Java Réflexivité (Fractal)
ALASCA — Architecture logicielles avancées
pour les systèmes complexes auto-adaptables
c
2014–2015 Jacques Malenfant
Master informatique, spécialité STL – UFR 919 Ingénierie
Université Pierre et Marie Curie
1 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Cours 3
Architectures logicielles
dynamiquement adaptables II
2 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Objectifs pédagogiques du cours 3
Comprendre et apprendre à utiliser la réflexivité comportementale
de Java pour réaliser des opérations d’adaptation dynamique.
Comprendre et apprendre à utiliser la réflexivité architecturale du
modèle à composants Fractal pour réaliser des opérations
d’adaptation dynamique.
3 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Plan
1Réflexivité comportementale en Java
2Réflexivité architecturale en Fractal
4 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
De la réflexion de structure à la réflexion de comportement
L’API java.lang.reflect fournit essentiellement une forme
d’introspection des classes qui s’arrête au code des méthodes.
La classe java.lang.reflect.Method permet de connaître la
signature des méthodes, mais pas leur code.
La limite apparaît au niveau de la méthode invoke de la classe
Method qui permet d’exécuter un objet méthode, mais comme
une boîte noire.
La réflexion de comportement demande la possibilité de
manipuler dynamiquement le code et l’état d’exécution.
Nous allons maintenant voir comment la communauté Java s’est
dotée de moyens de manipuler le code.
5 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Comment donner accès au code ?
Comme toute implantation de langage, la machine virtuelle Java
a bien sûr accès à une représentation du code : le contenu des
fichiers .class est chargé dans la machine virtuelle via des
chargeurs (« loaders »).
Le contenu d’un fichier .class est standardisé : il s’agit du code
octal, plus des entêtes donnant des informations utiles à la JVM
pour l’exécution, le tout présenté selon un format précis et connu.
Plusieurs outils ont donc été conçus et implantés pour donner un
accès au code des classes via son fichier .class.
Ces outils s’interposent généralement entre la machine virtuelle
et les fichiers .class pour donner aux programmes la possibilité
de modifier le code des classes lors de leur chargement.
6 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
BCEL, ASM et Javassist I
Les bibliothèques BCEL et ASM, orientées JVM, partagent le
même objectif général : lire et représenter le contenu des fichiers
.class et en permettre la manipulation par le programme
lui-même.
Toutes les deux offrent une API sur cette représentation en
mémoire manipulant la classe en termes de code octaux JVM (et
non en source Java).
La principale différence entre les deux bibliothèques tient à la
représentation adoptée :
BCEL transforme la version sérialisée du fichier .class en un
graphe d’objets, ce qui peut générer un grand nombre d’objets
que l’on parcourt ensuite relativement lentement en suivant les
liens entre ces objets.
7 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
BCEL, ASM et Javassist II
ASM adopte une représentation plus directe de type tableau
monolithique (pour une classe), tout en offrant cependant des
itérateurs masquant complètement cette représentation pour
donner l’illusion d’une représentation par objets.
Javassist est une autre bibliothèque similaire, mais qui propose
une interface d’introspection des fichiers .class qui est très
similaire à celle de l’API réflexion de Java, et qui permet de
modifier le code sous une forme source Java plutôt que du code
octal de sa machine virtuelle.
8 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Modifications autorisées I
Le scénario le plus courant pour l’utilisation des bibliothèques
comme BCEL, ASM et Javassist consite à s’interposer entre la
machine virtuelle et les fichiers .class, au moment du
chargement pas le chargeur de classes, pour donner aux
programmes la possibilité de modifier leur code avant leur mise
en mémoire dans la machine virtuelle.
Un scénario devenu possible depuis la version 6 de Java et son
API java.lang.instrumentation, consiste à lire le fichier
.class depuis la mémoire de la machine virtuelle, de le modifier,
puis de le réintroduire dans la machine virtuelle pour se
substituer à l’ancienne version.
9 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Modifications autorisées II
Dans ces deux types de scénario, Java impose à la nouvelle
version de la classe de se conformer à sa signature initiale, de
manière à ne pas remettre en cause la compilation des autres
classes.
Il est donc possible d’ajouter du code ou de modifier le contenu
des méthodes déjà présentes, mais pas de modifier la signature
des méthodes déjà présentes ni a fortiori de les supprimer.
10 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Javassist en quelques mots
Javassist est un bibliothèque permettant de manipuler les classes
sous leur forme compilée (.class).
Javassist se caractérise par le fait que les manipulations
autorisées se font au moment du chargement :
Les documents sur Javassist parlent de « compile-time »,
c’est-à-dire à la compilation.
C’est en fait un abus de langage, puisque la classe est déjà
compilée ; il s’agit plutôt de réflexion au chargement
load-time »), voir dynamique depuis Java 6 (« run-time »).
Javassist offre une interface de haut niveau permettant de
manipuler le code sous forme de source Java et non de code
octal JVM.
11 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Principes généraux
Lors du (re)chargement des classes, il est possible d’intervenir
sous la forme d’un filtre entre le contenu des « fichiers » .class
et la définition de la classe dans la machine virtuelle, selon un
patron de conception Observateur.
Une fois la classe chargée, Javassist la représente en mémoire à
l’aide d’une bibliothèque réifiant leur contenu sous forme d’objets,
bibliothèque qui a été conçue pour ressembler autant que
possible à la bibliothèque standard java.lang.reflect.
Chaque classe est représentée par un unique objet, instance de
la classe CtClass, pour « compile-time class », version au
chargement de la classe Class de Java.
Les manipulations sont autorisées tant que la classe n’est pas
mise à disposition de la machine virtuelle ; ensuite, la classe est
gelée et rendue inaccessible pour Javassist1.
12 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
La notion de ClassPool
Les instances de CtClass sont créées par le truchement d’un
collecteur de classes instance de ClassPool.
Le collecteur de classe permet de créer des instances de
CtClass en accédant à des fichiers .class ou directement à
des tableaux de codes octaux respectant le même format.
Le principal rôle du collecteur est d’assurer l’unicité de l’instance
de CtClass représentant une classe donnée, de manière à
maintenir la consistence des transformations de la classe faites
par intercession.
13 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Création d’un collecteur
ClassPool pool = ClassPool . getDefault (); // patron singleton
CtClass cc = pool . get ( " test . Rectangle" );
cc . setSuperclass ( pool . get ( " te st . Point " ) ) ; // change la superclasse
cc . w ri teFi le ( ) ; // ecriture de la classe modifiee
La méthode toBytecode() retourne un tableau de codes octaux
plutôt que d’écrire le fichier de classe.
14 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Définition d’une nouvelle classe
On utilise makeClass. Pour créer une classe Point sans
membres, on fait :
ClassPool pool = ClassPool . getDefault ();
CtClass cc = pool .makeClass( " Point " );
Il est également possible de lire une classe puis de la renommer
pour former une nouvelle classe :
ClassPool pool = ClassPool . getDefault ();
CtClass cc = pool . get ( " Point " ) ;
CtClass cc1 = pool . get ( " Point " ); // cc1 est identique a cc.
cc .setName( " Pair " );
CtClass cc2 = pool . get ( " Pair " ); // cc2 est identique a cc.
CtClass cc3 = pool . get ( " Point " );
// cc3 nest pas identique a cc.
15 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Les ClassPool sont des espaces de nommages
Ils peuvent être imbriqués de pools parents à pools fils.
ClassPool parent = ClassPool . getDefault ( );
ClassPool child = new ClassPool ( parent ) ;
Si on charge beaucoup de classes, on peut les écrire puis les
retirer du pool avec detach :
CtClass cc = . . . ;
cc . w ri teFi le ( ) ;
cc . detach ( ) ;
16 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Relation entre ClassPool et ClassLoader
Si les classes à modifier sont connues à l’avance, la méthode la
plus simple consiste à écrire un programme avec Javassist qui va
faire ces modifications. Ce programme va :
1Récupérer un objet CtClass en appelant ClassPool.get().
2Modifier cet objet.
3Appeler writeFile() sur cet objet de manière à modifier le
fichier .class correspondant.
Le programme utilisant les classes modifiées peut ensuite être
lancé, indépendamment du programme de modification.
Cela se rapproche d’une réflexion à la compilation.
17 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Réflexion au chargement
Si les classes à modifier ne sont connues qu’au lancement ou au
cours de l’exécution du programme qui les utilise, le
programmeur doit faire collaborer les pools de Javassist et le
chargeur de classes de Java.
Dans un premier temps, seules les classes devant modifier les
classes du programme doivent être chargés par le chargeur Java
(le méta-niveau).
Les classes à modifier (le niveau de base) sont d’abord chargées
par un chargeur lié à Javassist ; ce n’est qu’après leur
modification qu’elles pourront être chargées par le chargeur Java
pour devenir exécutables, et alors elles sont gelées à toute
modification.
Cela amène une organisation où le programme utilisateur et le
programme d’instrumentation (modifiant les classes du
programme utilisateur) sont clairement séparés.
18 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
Le chargeur javassist.Loader
Pour faciliter les choses, Javassist offre son propre chargeur de
classes permettant de charger des classes à partir d’un pool
Javassist :
import javassist .;
import test .Rectangle;
public class Main {
public static void main ( String [ ] args ) throws Throwable {
ClassPool pool = ClassPool . getDefault ();
Loader cl = new Loader ( pool ); // chargeur Javassist
CtClass ct = pool .get( " test . Rectangle" );
ct . setSuperclass(pool. get( " test . Point" )); // modification
Class c = cl . loadClass ( " test . Rectangle " ); // chargement Java
Object rect = c.newInstance (); // utilisation
...
}
}
19 / 60
Réflexivité comportementale en Java Réflexivité (Fractal)
La méthode toClass() de CtClass
Après modification des classes, la méthode toClass() de la
classe CtClass fournit un autre moyen très simple pour convertir
une instance de CtClass en une instance de java.lang.Class.
L’effet de bord de cette opération est de charger la nouvelle
classe dans Java pour l’utilisation dans le programme de base.
Il existe une version de toClass prenant en paramètre un
chargeur instance de Loader qui force le chargement dans ce
chargeur.
20 / 60
1 / 15 100%

Cours 3 Architectures logicielles dynamiquement adaptables II

La catégorie de ce document est-elle correcte?
Merci pour votre participation!

Faire une suggestion

Avez-vous trouvé des erreurs dans linterface ou les textes ? Ou savez-vous comment améliorer linterface utilisateur de StudyLib ? Nhésitez pas à envoyer vos suggestions. Cest très important pour nous !