Cours 2-3 Architectures réflexives

Transcription

Cours 2-3 Architectures réflexives
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN : Architectures logicielles pour
l’auto-adaptabilité dynamique
Cours 2-3
Architectures réflexives
c 2004-2011 Jacques Malenfant
�
Université Pierre et Marie Curie
UFR 919 Ingénierie
[email protected]
c 2004-2011 Jacques Malenfant
�
ALADYN
1
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
c 2004-2011 Jacques Malenfant
�
ALADYN
2
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Table des matières
1
De la réflexion structurelle à la réflexion comportementale
2
Réflexion structurelle et réification du code en Java
3
Réflexion de comportement en Java
c 2004-2011 Jacques Malenfant
�
ALADYN
« Simple things should be simple, complex things should be possible. »
Alan Kay [Kay93]
créateur de Smalltalk
3
c 2004-2011 Jacques Malenfant
�
ALADYN
4
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Formes de réflexion (rappel)
1
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Tout ce qui touche aux aspects plus statiques, comme le contenu du
programme ou les types de données abstraits du langage.
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Réflexion de comportement
2
Réflexion structurelle et réification du code en Java
3
Réflexion de comportement en Java
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
Tout ce qui touche aux aspects plus dynamiques, liés à l’état
d’exécution et à la façon d’exécuter les programmes.
5
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
6
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Principaux aspects
1
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle
Réification du code du programme.
Réflexion structurelle dans les langages à objets
Réification des entités manipulées dans un programme,
c’est-à-dire tous les types de données abstraits fournis par le
langage (flottants, tableaux, listes, etc.).
Réflexion de comportement
Réflexion de comportement dans les langages à objets
2
Réflexion structurelle et réification du code en Java
3
Réflexion de comportement en Java
c 2004-2011 Jacques Malenfant
�
ALADYN
Exemples :
Pouvoir accéder à la classe d’un objet à l’exécution et l’examiner
pour connaître la signature, voire le code, des méthodes qui y
sont déclarées.
Si le langage fournit un type de données table de hachage,
pouvoir examiner les valeurs de ce type, et en modifier la
définition (représentation, méthodes, ...).
7
c 2004-2011 Jacques Malenfant
�
ALADYN
8
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
À quoi ça sert ? I
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
À quoi ça sert ? II
Manipulation du code du programme : examiner ou modifier le
code du programme pendant l’exécution.
Exemples :
Exemples :
Sérialisation/désérialisation.
Persistence sur disque.
Choix d’algorithmes de mise en oeuvre en fonction de profils
d’utilisation (exemple de l’abstraction fenêtre...).
etc.
Ajouter du code pour la sécurité.
Ajouter ou retirer du code pour vérifier des assertions.
Modifier des envois de message pour utiliser le RMI.
Ajouter du code pour générer des traces, ou des informations sur
l’exécution
etc.
Manipuler des types de données : examiner les valeurs des types
complexes dans leur représentation interne.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
9
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
10
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Réflexion structurelle et langages à objets
1
De la réflexion structurelle à la réflexion comportementale
Les langages à objets se prêtent particulièrement bien à la
réflexion.
Réflexion structurelle
La notion même d’objet est réificatrice.
Réflexion structurelle dans les langages à objets
Dans tout langage à objets, un objet est une entité de plein droit.
L’analyse et le développement par objets procède de l’idée même
de rendre manipulable lors de l’exécution les concepts et entités
du domaine d’application, en en faisant des classes et des objets
(Personne, Voiture, ...).
Dans la plupart des langages à objets, l’objet est l’entité de base à
partir de laquelle sont construites toutes les valeurs des autres
types de données manipulées dans le langage.
Il y a des exceptions notables, comme les types primitifs et les
tableaux en Java, qui ne sont que partiellement réifiés (i.e. ils
restent, au moins partiellement, des boîtes noires), même avec la
conversion automatique depuis Java 5.0.
Réflexion de comportement
Réflexion de comportement dans les langages à objets
2
Réflexion structurelle et réification du code en Java
3
Réflexion de comportement en Java
c 2004-2011 Jacques Malenfant
�
ALADYN
11
c 2004-2011 Jacques Malenfant
�
ALADYN
12
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle et réification
Classes comme entités de plein droit
Structure de données à manipuler : les objets.
Faire en sorte que les classes soient représentées par des objets
à l’exécution assure :
Objets sont décrits par des classes.
de pouvoir utiliser dynamiquement le lien instance-de de tout objet
pour retrouver l’objet représentant sa classe d’instantiation ;
Exemple (Java) :
Donc, pour manipuler sans contrainte les objets à l’exécution, il
faut aussi pouvoir manipuler, et donc réifier, les classes.
Solution : représenter les classes comme des objets donc
accessibles à l’exécution.
⇒ les classes deviennent elles aussi de entités de plein droit.
Point p = new Point(0.0, 0.0) ;
Class<?> point = p.getClass() ;
de pouvoir utiliser les classes comme n’importe quel autre objet,
dont leur envoyer des messages.
Smalltalk, CLOS, Java (mais limité, voir plus loin), beaucoup de
langages expérimentaux.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
13
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des classes
ALADYN
14
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Forme affaiblie de réflexion structurelle : RTTI de C++
Mais quelle est la classe d’une classe lorsqu’elle est vue comme
un objet ?
C++ offre une forme limitée de réflexion structurelle par sa
bibliothèque RTTI (pour Run-Time Type Information d’information
sur les types à l’exécution.
Pour réifier complètement les classes, il faut aussi réifier leur
description sous forme de classe également.
Les classes ne sont pas des objets.
C’est ainsi qu’on obtient une métaclasse, c’est-à-dire une classe
de classes, vues comme des objets.
Le compilateur construit un ensemble de données accessibles
(en lecture) à l’exécution pour introspecter le contenu des
classes.
Avant d’explorer toutes les conséquences de ce concept,
étudions des formes plus limitées de réflexion de structure en
langages à objets : cas de C++ et, puis plus longuement de Java.
c 2004-2011 Jacques Malenfant
�
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
ALADYN
15
c 2004-2011 Jacques Malenfant
�
ALADYN
16
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Information dynamique sur les types en C++
La structure type_info standard
class type_info {
private :
type_info ( const type_info &) ;
type_info& operator =( const type_info &) ;
public :
v i r t u a l ~ type_info ( ) ;
i n t operator ==( const type_info &) const ;
i n t operator ! = ( const type_info &) const ;
i n t b e f o r e ( const type_info &) const ;
const char ∗ name ( ) const ;
}
L’opérateur typeid permet d’identifier le type exact d’un objet à
l’exécution.
Exemple :
if (typeid(r) == typeid(Circle)) { ... }
La structure type_info agit comme un crochet (hook ) pour avoir
des informations dynamiques complémentaires sur un type.
Une implantation peut ajouter d’autres informations par une
extension appelée extended_type_info.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
17
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
18
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Domaine de définition
1
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Tout ce qui touche aux aspects plus dynamiques, liés à l’état
d’exécution et à la façon d’exécuter les programmes.
Réflexion de comportement
Principaux éléments à réifier :
Réflexion de comportement dans les langages à objets
2
Réflexion structurelle et réification du code en Java
3
Réflexion de comportement en Java
c 2004-2011 Jacques Malenfant
�
ALADYN
les structures de données de l’évaluateur : liaison des variables,
pile d’exécution, mémoire (gc), etc.
l’évaluateur lui-même : procédures pour l’évaluation des
programmes.
19
c 2004-2011 Jacques Malenfant
�
ALADYN
20
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Utilisations classiques
1
Réflexion structurelle
Description de l’implantation : allocation des entités, gc, extensions
incrémentales, ré-implantation du système en entier.
Réflexion structurelle dans les langages à objets
Surveillance : inspection, analyse et modification dynamique du code
dans le langage lui-même.
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Interfaçage et déverminage : implantation d’outils dans le langage
lui-même, interopérabilité avec des programmes écrits
dans d’autres langages.
Auto-réorganisation : capacité à apprendre à partir de l’exécution du
programme, de manière à améliorer sa cohérence et
ses performances.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
De la réflexion structurelle à la réflexion comportementale
21
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
2
Réflexion structurelle et réification du code en Java
3
Réflexion de comportement en Java
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la structure au comportement pour les objets I
ALADYN
22
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
De la structure au comportement pour les objets II
En POO, outre les aspects déjà vus dans les langages
classiques, la réflexion structurelle doit prendre en compte les
objets et, en cela, elle est maîtrisée par les modèles de
métaclasses, classes et instances.
Pour introduire de la réflexion structurelle, il faut que la
description des objets, les classes, soient accessibles à
l’exécution, et que ces deux opérations, allocation et initialisation,
soient modifiables par le programme.
En réalité, la réflexion structurelle maîtrise l’équation :
Le modèle d’ObjVLisp réifie ces deux opérations sous forme de
méthodes, organise leur définition par les entités du noyau et leur
redéfinition par les métaclasses et classes définies par le
programmeur.
new = allocate ◦ initialize
c’est-à-dire qu’un objet est le résultat d’une allocation suivie
d’une initialisation de ses champs.
c 2004-2011 Jacques Malenfant
�
ALADYN
23
c 2004-2011 Jacques Malenfant
�
ALADYN
24
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification de l’envoi de message
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Trois modèles principaux
La réflexion de comportement cherche à maîtriser la réification
de l’appel de méthode par l’équation :
Réflexion de comportement utilise trois modèles principaux :
method call = lookup ◦ apply
1
c’est-à-dire qu’un appel est la composition de la recherche de la
méthode dans la classe du receveur suivie de l’application de la
méthode trouvée sur le receveur.
2
appel de méthode (envoi de message) = composition de recherche
de méthode puis application ;
réification de ces deux opérations sous forme de méthodes.
Bien que la réflexion de comportement en POO soit fondée sur
les mêmes principes généraux que ceux de l’approche
procédurale, une grande part des travaux s’est fondée sur un
équivalent des procédures réflexives visant à réifier l’envoi de
message comme fondement de l’exécution des programmes à
objets.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
3
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Application de fonction générique, elle-même réifiée comme une
fonction générique (CLOS).
c 2004-2011 Jacques Malenfant
�
25
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Qui définit ces méthodes ?
ALADYN
26
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Avantages et inconvénients, première solution
Une fois réifiées, il faut décider quelle entité va les définir pour un
objet donné.
Classe
Les méthodes réifiées sont alors définies par la métaclasse (qui définit
les méthodes de la classe).
Dans le contexte d’un langage réflexif, la réflexion structurelle
nous offre déjà deux entités possibles : la classe de l’objet ou la
métaclasse de celle-ci.
o.m(a1, ..., an) ⇒ o.class.handleMsgOn(o, m, [a1, ..., an])
Maes [Mae87] a proposé d’associer à chaque objet une nouvelle
entité appelée méta-objet, qui peut alors définir les méthodes.
Toutes les instances d’une classe partage les mêmes méthodes
réifiées, ce qui empêche de les particulariser à un objet donné.
Une troisième possibilité consiste à réifier le message lui-même,
et que ce soit l’objet message qui définisse les méthodes.
c 2004-2011 Jacques Malenfant
�
Opération de traitement de message réifiée sous forme d’une
méthode (handleMsg).
Modèle rechercher ◦ appliquer (lookup ◦ apply )
ALADYN
27
c 2004-2011 Jacques Malenfant
�
ALADYN
28
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Avantages et inconvénients, seconde solution
Avantages et inconvénients, troisième solution
Méta-objet
Message
Un méta-objet est alors associé à chaque objet et les méthodes
réifiées sont définies par la classe du méta-objet.
Chaque message est représenté par un objet et les méthodes réifiées
sont définies par la classe de cet objet message.
o.m(a1, ..., an) ⇒ o.metaobject.handleMsgOn(o, m, [a1, ..., an])
o.m(a1, ..., an) ⇒ (new Message(m, [a1, ..., an])).handleOn(o)
L’association étant à l’objet, il devient possible de les particulariser
pour chaque objet, ce qui peut être vu comme allant à l’encontre du
modèle à classe où toutes les instances d’une
classe ont le même comportement.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
On peut alors particulariser les méthodes pour chaque type de
messages, mais rien n’est dit sur le comportement des objets qui
perdent alors le contrôle sur la façon de traiter les messages.
29
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
ALADYN
30
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Modèles plus complexes
Les approches précédentes sont minimales, pour montrer les
principaux choix de conception.
On sépare le traitement du message en deux parties : la
recherche de la méthode à appliquer, puis l’application de la
méthode trouvée.
En pratique, concevoir un langage réflexif implique la réification
d’un grand nombre d’entités qui coopèrent pour la mise en œuvre
du méta-niveau par des envois de messages.
Règle de réécriture :
L’organisation du méta-niveau d’une manière claire et
compréhensible s’est avérée un défi difficile à réussir.
o.m(a1, ..., an) ⇒ o.<meta>.lookup(m).applyTo(o, [a1, ..., an])
Ici, <meta> peut être un accès à la classe ou au métaobjet.
ALADYN
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Le modèle rechercher ◦ appliquer
c 2004-2011 Jacques Malenfant
�
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Cette difficulté a mené à la notion de protocole de méta-objets
pour définir le « frameworks » réalisant le méta-niveau d’un
langage ou d’un outil.
31
c 2004-2011 Jacques Malenfant
�
ALADYN
32
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Protocoles de méta-objets
En terme de langages à objets
« In a language based upon our metaobject protocols, the
language implementation itself is structured as an object-oriented
program. This allows us to exploit the power of object-oriented
programming to make language implementation adjustable and
flexible. In effect, the resulting implementation does not represent a
single point in the overall space of language designs, but rather an
entire region within that space. »
« Metaobject protocols are interfaces to the language that give
users the ability to incrementally modify the languages behavior and
implementation, as well as the ability to write programs within the
language.
Language that incorporate metaobject protocols blur the distinction
between language designer and language user. »
Kiczales, des Rivières et Bobrow [KRB91]
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
Kiczales, des Rivières et Bobrow [KRB91]
33
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
L’ancêtre conceptuel : CLOS
ALADYN
34
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
Le concept de MOP aujourd’hui
CLOS = Common Lisp Object Language.
Tous les langages et systèmes réflexifs à base d’objets offrent un
protocole de méta-objets.
Standardisation de l’extension objet de Common Lisp fondée sur
le concept central de fonction générique ou multi-méthode.
La notion de MOP a surtout eu comme avantage de mettre au
devant de la scène la nécessité de concevoir le méta-niveau
selon des principes architecturaux clairs, réguliers et accessibles
aux programmeurs voulant faire de la réflexion dans leurs
programmes. (Le MOP de Smalltalk....)
multi-méthode : méthode comportant plusieurs définitions et
dont la sélection s’opére de manière à trouver la
définition la plus spécifique aux types de tous les
paramètres réels, contrairement aux méthodes où
la sélection se fait sur le type du receveur
uniquement.
Par extension, on parle aujourd’hui du MOP offert par des outils
spécifiques, comme un compilateur, un chargeur, etc.
Exemple : Javassist, un MOP pour la réflexion de comportement
au chargement des classes en Java.
La définition de sa sémantique a été faite en produisant une
implantation de CLOS en CLOS, et ainsi définir ce qu’il
est maintenant convenu d’appeler le MOP de CLOS.
c 2004-2011 Jacques Malenfant
�
Réflexion structurelle
Réflexion structurelle dans les langages à objets
Réflexion de comportement
Réflexion de comportement dans les langages à objets
ALADYN
35
c 2004-2011 Jacques Malenfant
�
ALADYN
36
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Manipulation du code en Java
Étude de cas : Javassist
1
De la réflexion structurelle à la réflexion comportementale
1
De la réflexion structurelle à la réflexion comportementale
2
Réflexion structurelle et réification du code en Java
2
Réflexion structurelle et réification du code en Java
3
Manipulation du code en Java
Manipulation du code en Java
Étude de cas : Javassist
Étude de cas : Javassist
Réflexion de comportement en Java
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
3
37
ALADYN
38
Manipulation du code en Java
Étude de cas : Javassist
Comment rendre un accès au code
Comme toute implantation de langage, la machine virtuelle 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 »).
Les classes sont représentées par des objets de plein droit, mais
ces objets ne sont pas pour autant des classes mais plutôt des
objets représentant des classes sans s’identifier à elles.
L’objet obtenu par Class.class est lui-même une instance de
Class.
Le contenu d’un fichier .class est standardisé. Il est du même
niveau que la représentation utilisée par Smalltalk : du code octal
d’une machine virtuelle.
À la base, plutôt un système de représentation des types à
l’exécution, analogue au RTTI de C++, mais en beaucoup plus
complet.
Plusieurs outils ont donc été conçus et implantés pour donner un
accès au code des classes via son fichier .class.
Une des limitations importantes de l’API Reflection est de ne pas
fournir un accès au code des méthodes, ce qui est une partie
intégrale de la réflexion structurelle.
ALADYN
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Forme de réflexion structurelle offerte par Java
c 2004-2011 Jacques Malenfant
�
Réflexion de comportement en Java
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.
39
c 2004-2011 Jacques Malenfant
�
ALADYN
40
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
BCEL et ASM
Réflexion au chargement
Les bibliothèques BCEL et ASM partagent le même objectif
général : représenter le contenu des fichiers .class et en
permettre la manipulation par le programme lui-même.
Outre BCEL et ASM, il existe d’autres bibliothèques, comme
Javassist, qui partagent le principe de modification lors du
chargement, ce qui a donné naissance au concept de réflexion
au chargement.
Toutes les deux offrent une API en termes de code octaux pour
cette manipulation.
Une approche un peu différente consiste à donner accès au
processus de traduction du code octal vers le langage du
processeur physique, réalisé par le compilateur juste-à-temps.
La principale différence entre les deux bibliothèques tient à la
représentation adoptée :
BCEL utilise une représentation par objets pure, ce qui peut
générer un grand nombre d’objets que l’on parcourt ensuite
relativement lentement en suivant les identificateurs d’objets.
ASM adopte une représentation de type tableau, tout en offrant
cependant des itérateurs masquant complètement cette
représentation pour donner l’illusion d’une représentation par
objets.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
La niveau d’abstraction dans les modifications est alors
cependant très bas.
La limitation générale reste le fait que peu de modifications sont
possibles après la phase de chargement de l’application.
41
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
API JPDA, et tutti quanti I
ALADYN
42
Manipulation du code en Java
Étude de cas : Javassist
API JPDA, et tutti quanti II
L’API java.lang.instrumentation de Java 6.0 accepte le
changement (rechargement ou la transformation) à chaud des
classes, ce qui étend les limites de ce qu’on peut faire
dynamiquement. Cependant, il reste plusieurs limitations
importantes aux transformations possibles, qui peuvent changer
l’implantation des méthodes, mais ne peuvent
L’API JPDA (« debugging ») introduit de nouvelles possibilités,
comme le rechargement à chaud du code des classes et la
récupération par événements des informations de la pile
d’exécution.
L’inconvénient tient au fait qu’on suit une philosophie debugging
avec interruption du programme en cours et qu’il faut lancer la
machine virtuelle dans un mode ouvert et moins efficace.
ni changer les membres de la classes (champs ou méthodes),
ni la signature des méthodes,
ni l’héritage de la classe.
D’autres API arrivent avec un même principe de récupération
d’information par événements, comme l’API JMX (« Java
Management Extensions ») et le nouveau « package »
java.lang.instrumentation de Java 5.0.
c 2004-2011 Jacques Malenfant
�
Manipulation du code en Java
Étude de cas : Javassist
ALADYN
Cette nouvelle API promet des modifications de classes à chaud
plus efficaces que le « hotswap » de JPDA.
43
c 2004-2011 Jacques Malenfant
�
ALADYN
44
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Manipulation du code en Java
Étude de cas : Javassist
Une bibliothèque pour manipuler le code octal I
1
2
De la réflexion structurelle à la réflexion comportementale
Comme plusieurs autres outils du même genre (BCEL, ASM),
Javassist est un bibliothèque permettant de manipuler les classes
sous leur forme compilée.
Réflexion structurelle et réification du code en Java
Manipulation du code en Java
Le format manipulé est celui des fichiers .class de Java.
Étude de cas : Javassist
3
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 »).
Réflexion de comportement en Java
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
45
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Une bibliothèque pour manipuler le code octal II
ALADYN
46
Manipulation du code en Java
Étude de cas : Javassist
Principes généraux
Lors du 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.
Javassist offre une interface de haut niveau permettant de
manipuler le code sous forme de source et non de code octal.
Javassist propose une bibliothèque pour réifier le contenu des
fichiers .class sous forme d’objets.
Initialement, Javassist se limitait à des manipulations au
chargement des classes, mais il peut être utilisé conjointement
avec l’API JPDA et la nouvelle API
java.lang.instrumentation pour modifier les classes
dynamiquement.
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 Javassist 2 .
2. Sauf utilisation des API JPDA ou java.lang.instrumentation en Java 6.0.
c 2004-2011 Jacques Malenfant
�
ALADYN
47
c 2004-2011 Jacques Malenfant
�
ALADYN
48
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
La notion de ClassPool
Création d’un collecteur
Les instances de CtClass sont créées par le truchement d’un
collecteur de classes instance de ClassPool.
ClassPool p o o l = ClassPool . g e t D e f a u l t ( ) ; / / p a t r o n s i n g l e t o n
CtClass cc = p o o l . g e t ( " t e s t . Rectangle " ) ;
cc . s e t S u p e r c l a s s ( p o o l . g e t ( " t e s t . P o i n t " ) ) ; / / change l a s u p e r c l a s s e
cc . w r i t e F i l e ( ) ; / / e c r i t u r e de l a c l a s s e m o d i f i e e
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.
La méthode toBytecode() retourne un tableau de codes octaux
plutôt que d’écrire le fichier de classe.
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.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
ALADYN
49
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Chemin de classes I
ALADYN
50
Manipulation du code en Java
Étude de cas : Javassist
Chemin de classes II
La méthode insertClassPath permet d’ajouter au chemin des
classes utiliser par un ClassPool pour charger les classes par
défaut il utilise le chemin de la machine virtuelle sous-jacente :
La méthode makeClass permet de gérer directement l’accès à
un fichier .class connu :
ClassPool p o o l = ClassPool . g e t D e f a u l t ( ) ;
pool . insertClassPath ( " / usr / l o c a l / j a v a l i b " ) ;
ClassPool cp = ClassPool . g e t D e f a u l t ( ) ;
InputStream i n s = an i n p u t stream f o r r e a d i n g a class f i l e ;
CtClass cc = cp . makeClass ( i n s ) ;
Il est également possible de fournir directement une classe
compilée sous la forme d’un tableau d’octets :
ClassPool cp = ClassPool . g e t D e f a u l t ( ) ;
byte [ ] b = <a byte a r r a y > ;
S t r i n g name = <class name> ;
cp . i n s e r t C l a s s P a t h (new ByteArrayClassPath ( name , b ) ) ;
CtClass cc = cp . g e t ( name ) ;
c 2004-2011 Jacques Malenfant
�
ALADYN
51
c 2004-2011 Jacques Malenfant
�
ALADYN
52
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Définition d’une nouvelle classe
Les ClassPool sont des espaces de nommages
On utilise makeClass. Pour créer une classe Point sans
membres, on fait :
Ils peuvent être imbriqués de pools parents à pools fils.
ClassPool p o o l = ClassPool . g e t D e f a u l t ( ) ;
CtClass cc = p o o l . makeClass ( " P o i n t " ) ;
ClassPool p a r e n t = ClassPool . g e t D e f a u l t ( ) ;
ClassPool c h i l d = new ClassPool ( p a r e n t ) ;
Il est également possible de lire une classe puis de la renommer
pour former une nouvelle classe :
ClassPool p o o l = ClassPool . g e t D e f a u l t ( ) ;
CtClass cc = p o o l . g e t ( " P o i n t " ) ;
CtClass cc1 = p o o l . g e t ( " P o i n t " ) ;
/ / cc1 e s t i d e n t i q u e a cc .
cc . setName ( " P a i r " ) ;
CtClass cc2 = p o o l . g e t ( " P a i r " ) ;
/ / cc2 e s t i d e n t i q u e a cc .
CtClass cc3 = p o o l . g e t ( " P o i n t " ) ;
/ / cc3 n ’ e s t pas i d e n t i q u e a cc .
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
53
CtClass cc = . . .
cc . w r i t e F i l e ( ) ;
cc . detach ( ) ;
;
c 2004-2011 Jacques Malenfant
�
ALADYN
54
Manipulation du code en Java
Étude de cas : Javassist
Réflexion au chargement I
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 :
1
Récupérer un objet CtClass en appelant ClassPool.get().
3
Si on charge beaucoup de classes, on peut les écrire puis les
retirer du pool avec detach :
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Relation entre ClassPool et ClassLoader
2
Manipulation du code en Java
Étude de cas : Javassist
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.
Modifier cet objet.
Appeler writeFile() sur cet objet de manière à modifier le
fichier .class correspondant.
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).
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.
c 2004-2011 Jacques Malenfant
�
ALADYN
55
c 2004-2011 Jacques Malenfant
�
ALADYN
56
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Réflexion au chargement II
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 :
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.
import j a v a s s i s t . ∗ ;
import t e s t . Rectangle ;
public class Main {
public s t a t i c void main ( S t r i n g [ ] args ) throws Throwable {
ClassPool p o o l = ClassPool . g e t D e f a u l t ( ) ;
Loader c l = new Loader ( p o o l ) ;
/ / chargeur J a v a s s i s t
CtClass c t = p o o l . g e t ( " t e s t . Rectangle " ) ;
c t . setSuperclass ( pool . get ( " t e s t . Point " ) ) ; / / m o d i f i c a t i o n
Class c = c l . l o a d C l a s s ( " t e s t . Rectangle " ) ; / / chargement Java
O b j e c t r e c t = c . newInstance ( ) ; / / u t i l i s a t i o n
...
}
}
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.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
57
ALADYN
58
Manipulation du code en Java
Étude de cas : Javassist
Interception au chargement
Javassist prévoit également un mécanisme basé sur un patron
Observateur pour s’interposer entre les fichiers .class et la
machine virtuelle de manière à réaliser des transformations au
chargement.
Un transformateur est un objet dont la classe implante l’interface
Translator.
Cette interface déclare la méthode onLoad qui est appelée avec
le collecteur et le nom de la classe avant d’aller lire le fichier
.class.
Il faut donc définir une classe de transformation qui réalise les
modifications désirées à la classe dans sa méthode onLoad, puis
attacher une instance de cette classe au chargeur de classes de
Javassist. À partir de là, cette méthode onLoad sera appelée à
chaque fois qu’une classe est chargée.
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.
ALADYN
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
La méthode toClass() de CtClass
c 2004-2011 Jacques Malenfant
�
Manipulation du code en Java
Étude de cas : Javassist
59
c 2004-2011 Jacques Malenfant
�
ALADYN
60
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
L’interface Translator
Une transformation simple : rendre publique
public class M a k e P u b l i c T r a n s l a t o r implements T r a n s l a t o r {
void s t a r t ( ClassPool p o o l )
throws NotFoundException , CannotCompileException { }
void onLoad ( ClassPool pool , S t r i n g classname )
throws NotFoundException , CannotCompileException
{
CtClass cc = p o o l . g e t ( classname ) ;
cc . s e t M o d i f i e r s ( M o d i f i e r . PUBLIC ) ;
}
}
public i n t e r f a c e T r a n s l a t o r {
public void s t a r t ( ClassPool p o o l )
throws NotFoundException , CannotCompileException ;
public void onLoad ( ClassPool pool , S t r i n g classname )
throws NotFoundException , CannotCompileException ;
}
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
ALADYN
62
Manipulation du code en Java
Étude de cas : Javassist
Rechargement d’une classe à l’exécution I
import j a v a s s i s t . ∗ ;
public class Main {
public s t a t i c void main ( S t r i n g [ ] args ) throws Throwable {
T r a n s l a t o r t = new M a k e P u b l i c T r a n s l a t o r ( ) ;
ClassPool p o o l = ClassPool . g e t D e f a u l t ( ) ;
Loader c l = new Loader ( ) ;
c l . a d d T r a n s l a t o r ( pool , t ) ;
c l . run ( " MyApp " , args ) ;
}
}
Il existe deux manières de faire :
1
2
Utiliser le mécanisme de « hotswap » prévu par l’API JPDA.
Utiliser l’API java.lang.instrumentation (que nous ne
détaillerons pas ici).
Pour l’utilisation du « hotswap » , une classe
javassist.tools.Hotswapper est fournie par Javassist pour
emballer de manière simple le rechargement.
Exemple (venant des échantillons de programmes de Javassist) :
Notez ici l’utilisation de deux chargeurs (le chargeur initial et celui
donné par cl). L’existence de ces deux chargeurs peut faire qu’une
même classe soit chargée dans les deux et alors considérées comme
différentes. Dans ce cas, on peut devoir forcer le chargement de
classes à se faire ensuite dans un chargeur spécifique pour préserver
l’unicité de la classe chargée.
ALADYN
c 2004-2011 Jacques Malenfant
�
61
Installation de la transformation
c 2004-2011 Jacques Malenfant
�
Manipulation du code en Java
Étude de cas : Javassist
63
c 2004-2011 Jacques Malenfant
�
ALADYN
64
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Rechargement d’une classe à l’exécution II
Rechargement d’une classe à l’exécution III
import j a v a . i o . ∗ ;
import j a v a s s i s t . u t i l . HotSwapper ;
new H e l l o W o r l d ( ) . p r i n t ( ) ;
n e w f i l e = new F i l e ( " H e l l o W o r l d . c l a s s " ) ;
b y t e s = new byte [ ( i n t ) n e w f i l e . l e n g t h ( ) ] ;
new F i l e I n p u t S t r e a m ( n e w f i l e ) . read ( b y t e s ) ;
System . o u t . p r i n t l n ( " ∗∗ r e l o a d t h e o r i g i n a l v e r s i o n " ) ;
public class Test {
public s t a t i c void main ( S t r i n g [ ] args ) throws E x c e p ti o n {
/ / 8000 e s t l e numero de p o r t pour se c o n n e c t e r a l a JVM
HotSwapper hs = new HotSwapper ( 8 0 0 0 ) ;
new H e l l o W o r l d ( ) . p r i n t ( ) ;
hs . r e l o a d ( " H e l l o W o r l d " , b y t e s ) ;
new H e l l o W o r l d ( ) . p r i n t ( ) ;
F i l e n e w f i l e = new F i l e ( " l o g g i n g / H e l l o W o r l d . c l a s s " ) ;
byte [ ] b y t e s = new byte [ ( i n t ) n e w f i l e . l e n g t h ( ) ] ;
new F i l e I n p u t S t r e a m ( n e w f i l e ) . read ( b y t e s ) ;
System . o u t . p r i n t l n ( " ∗∗ r e l o a d a l o g g i n g v e r s i o n " ) ;
}
}
Le lancement de java se fait alors par la commande (voir la
documentation de Javassist pour plus de détails) :
hs . r e l o a d ( " H e l l o W o r l d " , b y t e s ) ;
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
Manipulation du code en Java
Étude de cas : Javassist
65
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Rechargement d’une classe à l’exécution IV
ALADYN
66
Manipulation du code en Java
Étude de cas : Javassist
Introspection et intercession avec Javassist
Les méthodes pour l’introspection sur les classes sont définies
par CtClass d’une manière compatible avec l’API Reflection de
Java.
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
En plus de ces méthodes « standards », CtClass offrent
également un ensemble de méthodes pour l’intercession : ajout
de nouvelles méthodes, de nouveaux champs et de nouveaux
constructeurs.
Contrairement à la réflexion en Java, il est également possible en
Javassist d’accéder et d’altérer le corps des méthodes existantes
ou d’en définir de nouvelles.
Javassist ne permet cependant pas de retirer des champs ou des
méthodes, ni même de changer la signature des méthodes.
c 2004-2011 Jacques Malenfant
�
ALADYN
67
c 2004-2011 Jacques Malenfant
�
ALADYN
68
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Les méthodes
Contraintes sur le code source
Les méthodes sont représentées par des instances de
CtMethod.
Javassist inclut un compilateur Java pour traduire ce code source
en code octal qui est ensuite introduit dans les méthodes.
Les constructeurs sont représentés par des instances de
CtConstructor.
Le fait que le code source à ajouter doivent être introduit et
compilé dans le contexte d’une méthode en code octal pose
certaines contraintes.
Ces classes définissent des méthodes insertBefore(),
insertAfter(), and addCatch() pour ajouter du code source
dans le corps des méthodes. La méthode insertAt() permet
d’introduire du code à une ligne donnée de la méthode, à la
condition que cette dernière ait été compilée avec l’option -g («
debugging » ou déverminage) laissant plus d’information dans le
fichier .class sur les numéros de lignes et les noms de variables
locales.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
ALADYN
En particulier, des noms de variables spéciaux sont définis,
comme $0, $1, $2, pour représenter le pseudo-argument this et
les arguments de la méthode, ou encore $sig pour accéder à un
tableau de type Class des types des paramètres formels ou
encore $type pour accéder à l’objet de type Class représentant
le type du résultat formel.
c 2004-2011 Jacques Malenfant
�
69
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Exemple I
ALADYN
70
Manipulation du code en Java
Étude de cas : Javassist
Exemple II
Soit la classe Point suivante :
Pour donner l’équivalent de :
class P o i n t {
int x , y ;
void move ( i n t dx , i n t dy ) { x += dx ; y += dy ; }
}
class P o i n t {
int x , y ;
void move ( i n t dx , i n t dy ) {
{ System . o u t . p r i n t l n ( dx ) ; System . o u t . p r i n t l n ( dy ) ; }
x += dx ; y += dy ;
}
}
On ajoute une trace de move par :
ClassPool p o o l = ClassPool . g e t D e f a u l t ( ) ;
CtClass cc = p o o l . g e t ( " P o i n t " ) ;
CtMethod m = cc . getDeclaredMethod ( " move " ) ;
m. i n s e r t B e f o r e (
" { System . o u t . p r i n t l n ( $1 ) ; System . o u t . p r i n t l n ( $2 ) ; } " ) ;
cc . w r i t e F i l e ( ) ;
c 2004-2011 Jacques Malenfant
�
ALADYN
71
c 2004-2011 Jacques Malenfant
�
ALADYN
72
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Ajout d’une nouvelle méthode
Méthodes mutuellement récursives I
CtClass p o i n t = ClassPool . g e t D e f a u l t ( ) . g e t ( " P o i n t " ) ;
CtMethod m = CtNewMethod . make (
" p u b l i c i n t xmove ( i n t dx ) { x += dx ; } " ,
point ) ;
p o i n t . addMethod (m) ;
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
Javassist ne peut compiler une méthode appelant une autre
méthode qui n’existe pas encore dans la classe.
Pour les méthodes mutuellement récursives, il est possible de
déconnecter la création de la signature de celle du corps de la
méthode :
73
ALADYN
74
Manipulation du code en Java
Étude de cas : Javassist
Ajout d’un champ
CtClass cc = . . . ;
CtMethod m =
CtNewMethod . make ( " p u b l i c a b s t r a c t i n t m( i n t i ) ; " , cc ) ;
CtMethod n =
CtNewMethod . make ( " p u b l i c a b s t r a c t i n t n ( i n t i ) ; " , cc ) ;
cc . addMethod (m) ;
cc . addMethod ( n ) ;
m. setBody ( " { r e t u r n ( $1 <= 0 ) ? 1 : ( n ( $1 − 1 ) ∗ $1 ) ; } " ) ;
n . setBody ( " { r e t u r n m( $1 ) ; } " ) ;
cc . s e t M o d i f i e r s ( cc . g e t M o d i f i e r s ( )
& ~ M o d i f i e r . ABSTRACT ) ;
ALADYN
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Méthodes mutuellement récursives II
c 2004-2011 Jacques Malenfant
�
Manipulation du code en Java
Étude de cas : Javassist
Préparation :
CtClass p o i n t = ClassPool . g e t D e f a u l t ( ) . g e t ( " P o i n t " ) ;
C t F i e l d f = new C t F i e l d ( CtClass . i n t T y p e , " z " , p o i n t ) ;
puis, ajout sans initialisation :
point . addField ( f ) ;
ou encore avec initialisation :
point . addField ( f , " 0 " ) ;
75
c 2004-2011 Jacques Malenfant
�
/ / la valeur i n i t i a l e est 0.
ALADYN
76
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Modification du corps des méthodes I
Modification du corps des méthodes II
La méthode instrument de la classe CtMethod implante un
patron visiteur qui va parcourir le code de la méthode et appliquer
les éditions demandées par une instance d’ExprEditor qui lui
est passée en paramètre.
Les classes CtMethod et CtConstructor permettent de
remplacer le corps de la méthode ou du constructeur représenté
avec la méthode setBody.
ExprEditor est une classe définissant une série de méthode
edit qui sont chargées d’appliquer les modifications en fonction
Pour des modifications plus complexes que de simples ajouts ou
un remplacement total, Javassist fournit par la classe
javassist.expr.ExprEditor et les autres classes du même
paquetage le moyen de remplacer différentes expressions dans
le corps d’une méthode.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
Manipulation du code en Java
Étude de cas : Javassist
du type d’expressions rencontrées.
Les types d’expressions identifiables sont les transtypages («
cast »), les appels de constructeurs, les accès à des champs, les
clauses catch, les expressions instanceof, les appels de
méthodes et les expressions de création d’objets et de tableaux
(«new »).
c 2004-2011 Jacques Malenfant
�
77
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Manipulation du code en Java
Étude de cas : Javassist
Exemple I
ALADYN
78
Manipulation du code en Java
Étude de cas : Javassist
Exemple II
Notez que le code de substitution passé à la méthode replace
n’étant pas une expression mais un bloc. Il doit donc
explicitement prévoir le résultat à retourner, ce qui est fait en
affectation une valeur à la pseudo-variable $_.
CtMethod cm = . . . ;
cm . i n s t r u m e n t (
new E x p r E d i t o r ( ) {
public void e d i t ( MethodCall m) throws CannotCompileException
{
i f (m. getClassName ( ) . equals ( " P o i n t " )
&& m. getMethodName ( ) . equals ( " move " ) )
m. r e p l a c e ( " { $1 = 0 ; $_ = $proceed ( $$ ) ; } " ) ;
}
});
c 2004-2011 Jacques Malenfant
�
ALADYN
La notation $proceed($$) exécute l’appel de méthode tel qu’il
était avant la modification avec la même séquence de paramètres
réels (désignée par la pseudo-variable $$.
Voir la documentation Javassist pour les autres formes d’édition
possibles et leurs pseudo-variables de contexte disponibles.
79
c 2004-2011 Jacques Malenfant
�
ALADYN
80
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
Réification des appels et interception
Réflexion de comportement et modification du code
Progression historique
1
De la réflexion structurelle à la réflexion comportementale
Java ne fut pas à l’origine un langage réflexif.
2
Réflexion structurelle et réification du code en Java
3
Réflexion de comportement en Java
La reflexion de structure de l’API java.lang.reflect est
apparue comme support à certaines opérations de haut niveau,
comme l’envoi de message à distance (RMI).
Son utilisation s’est ensuite propagée à de nombreuses API.
Réification des appels et interception
Le réflexion de comportement reste cependant largement en
retrait, bien que de plus en plus d’API standards et d’outils
apparaissent qui offre une certaine réflexion de comportement
sans jamais cependant y faire explicitement référence.
Réflexion de comportement et modification du code
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
81
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
ALADYN
82
Réification des appels et interception
Réflexion de comportement et modification du code
Réflexion comportementale faible I
1
De la réflexion structurelle à la réflexion comportementale
2
Réflexion structurelle et réification du code en Java
3
Réflexion de comportement en Java
L’objectif de la réflexion comportementale est de modifier la façon
d’exécuter du code pour ajouter des choses à faire ou changer la
façon de les faire.
Une part significative des applications de la réflexion
comportementale peuvent être réalisées simplement en ajoutant
du code à exécuter avant et après l’exécution de la méthode.
Réification des appels et interception
C’est ce qu’on appelle les pré- et post-méthodes, c’est-à-dire des
espèces de crochets parfois inclus directement dans le langage
(en CLOS, le MOP en prévoit).
Réflexion de comportement et modification du code
On peut aussi obtenir de tels effets en donnant un mécanisme
plus faible, dit d’interception, où :
c 2004-2011 Jacques Malenfant
�
ALADYN
83
c 2004-2011 Jacques Malenfant
�
ALADYN
84
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
Réflexion comportementale faible II
Réification des appels et interception
Réflexion de comportement et modification du code
Interception des appels de méthodes
Les méta-objets définissant lookup et apply fournissent un tel
mécanisme, l’appel de lookup interceptant l’appel de méthodes
et l’invocation de apply sur l’objet méthode réalisant l’invocation
de la méthode du programme.
on réifie l’appel de méthode en donnant un mécanisme aux
programmeurs permettant d’intercepter les appels, eet
on donne la possibilité d’activer la méthode elle-même, telle
qu’elle apparaît dans le programme.
Plusieurs langages de programmation offrent aussi de tels
mécanismes d’interception, parfois « inconsciemment », comme
la ruse du doesNotUnderstand en Smalltalk.
En Java, les classes Proxy servent exactement à cela.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
85
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
Les proxys en Java
ALADYN
86
Réification des appels et interception
Réflexion de comportement et modification du code
La classe Proxy
public class Proxy implements j a v a . i o . S e r i a l i z a b l e {
...
public s t a t i c Class getProxyClass ( ClassLoader c l ,
Class < ? > . . . i n t e r f a c e s )
throws I l l e g a l A r g u m e n t E x c e p t i o n ;
public s t a t i c O b j e c t newProxyInstance ( ClassLoader c l ,
Class [ ] i n t e r f a c e s ,
InvocationHandler h )
throws I l l e g a l A r g u m e n t E x c e p t i o n ;
public s t a t i c boolean i s P r o x y C l a s s ( Class <?> c ) ;
public s t a t i c I n v o c a t i o n H a n d l e r g e t I n v o c a t i o n H a n d l e r ( O b j e c t proxy )
throws I l l e g a l A r g u m e n t E x c e p t i o n ;
}
La classe java.lang.reflect.Proxy est en gros un
générateur de classes permettant de créer des objets
intercepteurs.
Un objet intercepteur est un objet qui implante toutes les
interfaces d’une classe C donnée, et qui va pouvoir intercepter
tous les appels de méthodes sur une instance donnée de C.
Lors de l’interception, l’objet intercepteur (proxy ) peut faire
intervenir un objet de traitement de l’invocation (« invocation
handler ») qui va dire quoi faire avec cet appel de méthode.
L’interception fait intervenir l’objet méthode de la classe C qui est
invoqué, et donc le traitement de l’invocation inclut la plupart du
temps l’invocation de cet objet méthode pour l’exécuter.
L’intercepteur peut aussi faire plein de choses pour entourer cette
invocation.
c 2004-2011 Jacques Malenfant
�
c 2004-2011 Jacques Malenfant
�
ALADYN
87
c 2004-2011 Jacques Malenfant
�
ALADYN
88
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
L’interface InvocationHandler
Réification des appels et interception
Réflexion de comportement et modification du code
Un proxy pour la trace des méthodes I
L’interface InvocationHandler définit une méthode invoke qui
intercepte tout appel de méthode faite à l’objet proxy :
import j a v a . l a n g . r e f l e c t . ∗ ;
import j a v a . i o . P r i n t W r i t e r ;
public i n t e r f a c e I n v o c a t i o n H a n d l e r {
public O b j e c t i n v o k e ( O b j e c t proxy , Method m, O b j e c t [ ] args )
throws Throwable ;
}
public class T r a c i n g I H implements I n v o c a t i o n H a n d l e r {
public s t a t i c O b j e c t c r e a t e P r o x y ( O b j e c t o , P r i n t W r i t e r o u t ) {
r e t u r n Proxy . newProxyInstance ( o . g e t C l a s s ( ) . getClassLoader ( ) ,
o . g e t C l a s s ( ) . g e t I n t e r f a c e s ( ) , new T r a c i n g I H ( o , o u t ) ) ;
}
protected O b j e c t t a r g e t ;
protected P r i n t W r i t e r o u t ;
Une méthode invoke qui ne fait rien s’écrit de la manière suivante :
public O b j e c t i n v o k e ( O b j e c t proxy , Method m, O b j e c t [ ] args )
throws Throwable {
r e t u r n m. i n v o k e ( t a r g e t , args ) ;
}
public T r a c i n g I H ( O b j e c t o , P r i n t W r i t e r o u t ) {
this . t a r g e t = o ; this . out = out ;
}
où target référence l’objet réel pour lequel le proxy agit.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
c 2004-2011 Jacques Malenfant
�
89
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
ALADYN
90
Réification des appels et interception
Réflexion de comportement et modification du code
Un proxy pour la trace des méthodes II
public O b j e c t i n v o k e ( O b j e c t proxy , Method m, O b j e c t [ ] args )
throws Throwable {
Object r e s u l t = null ;
try {
o u t . p r i n t l n (m. getName ( ) + " c a l l e d . " ) ;
r e s u l t = m. i n v o k e ( t a r g e t , args ) ;
} catch ( I n v o c a t i o n T a r g e t E x c e p t i o n e ) {
o u t . p r i n t l n (m. getName ( ) + " throws " + e . getCause ( ) ) ;
throw e . getCause ( ) ;
} / / end t r y / c a t c h
o u t . p r i n t l n (m. getName ( ) + " r e t u r n s . " ) ;
return r e s u l t ;
}
1
De la réflexion structurelle à la réflexion comportementale
2
Réflexion structurelle et réification du code en Java
3
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
}
c 2004-2011 Jacques Malenfant
�
ALADYN
91
c 2004-2011 Jacques Malenfant
�
ALADYN
92
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
Réflexion de comportement et modification du code I
Réification des appels et interception
Réflexion de comportement et modification du code
Réflexion de comportement et modification du code II
Nous avons vu que la réflexion de comportement doit permettre
de modifier le code de l’interprète, ce qui peut être réalisé en
ajoutant du code à cet interprète via le programme. C’est ce que
permet 3-Lisp, en interdisant cependant de modifier les fonctions
existantes de l’interprète.
Une forme limité de réflexion comportementale peut donc être
obtenue s’il est possible de modifier dynamiquement le code du
programme. C’est cette approche que prennent la plupart des
gens qui cherchent à faire de la réflexion de comportement en
Java.
En Java, il est très difficile de modifier l’interprète, puisque c’est la
machine virtuelle elle-même qui joue ce rôle.
Il est souvent possible d’obtenir un effet similaire à celui des
procédures réflexives en introduisant dans le code du programme
le code réflexif qui aurait dû être exécuté par la procédure
réflexive.
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
93
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
Exemple : introduction de métaobjets avec Javassist
94
Réification des appels et interception
Réflexion de comportement et modification du code
Classe originale :
L’idée est de transformer le code d’une classe pour intercepter à
la fois les appels de méthodes et les accès aux champs des
objets d’une classe rendue réflexive.
class Person {
public i n t f ( i n t i ) { r e t u r n i + 1 ; }
public i n t v a l u e ;
}
La classe Reflection est la classe principale implantant cette
forme de réflexion.
Classe après transformation :
Les classes Loader et Compiler offrent des outils permettant de
réaliser cette forme de réflexion au chargement ou alors par
modification permanente des fichiers .class sur le système de
fichiers.
ALADYN
ALADYN
Transformation réalisée I
Le « package » javassist.tools.reflect implante une forme
de réflexion par interception utilisant le modèle des méta-objets
décrit plus tôt.
c 2004-2011 Jacques Malenfant
�
c 2004-2011 Jacques Malenfant
�
95
c 2004-2011 Jacques Malenfant
�
ALADYN
96
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
Transformation réalisée II
Réification des appels et interception
Réflexion de comportement et modification du code
Transformation réalisée III
class Person implements M e t a l e v e l {
public i n t _ o r i g i n a l _ f ( i n t i ) { r e t u r n i + 1 ; }
public i n t f ( i n t i ) { d e l e g a t e t o t h e m e ta o b j ec t }
boolean m a k e R e f l e c t i v e ( j a v a . l a n g . Class c l a z z ,
j a v a . l a n g . Class metaobject ,
j a v a . l a n g . Class metaclass ) ;
public i n t v a l u e ;
public i n t _ r _ v a l u e ( ) { read " v a l u e " }
public void _w_value ( i n t v ) { w r i t e " v a l u e " }
boolean m a k e R e f l e c t i v e ( CtClass c l a z z , CtClass metaobject ,
CtClass metaclass ) ;
boolean m a k e R e f l e c t i v e ( j a v a . l a n g . S t r i n g classname ,
j a v a . l a n g . S t r i n g metaobject ,
j a v a . l a n g . S t r i n g metaclass )
public C la ss Me t a o b j e c t _getClass ( ) { r e t u r n a class m e t a o b j e ct }
public M e t a o b j e c t _ g e t M e t a o b j e c t ( ) { r e t u r n a m et a o b j ec t }
public void _ s e t M e t a o b j e c t ( M e ta o b j e ct m) { change a m e ta o b j e c t }
}
Les paramètres de ces méthodes réprésentent :
la classe à transformer
la classe des méta-objets qui seront associés aux instances
réflexives (doit être sous-classe de Metaobject
Les classes Metaobject et ClassMetaobject sont des
paramètres de la transformation implantée par les méthodes
makeReflective de Reflection :
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
97
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
Transformation réalisée IV
ALADYN
98
Réification des appels et interception
Réflexion de comportement et modification du code
Transformation réalisée V
la classe de l’objet représentant la classe des instances réflexives
(doit être sous-classe de ClassMetaobject
Les classes Metaobject et ClassMetaobject définissent le
comportement minial des métaobjets et des classes des objets
réflexifs respectivement. En particulier, ces classes définissent
les méthodes _trapFieldRead, _trapFieldWrite et
_trapMethodCall utilisées dans le protocole d’interception.
Metaobject permet de créer des objets qui interceptent ces
actions sur les objets alors que ClassMetaobject permet de
créer des représentations des classes qui interceptent ces
actions sur les champs et méthodes statiques de la classe. Voir la
documentation de Javassist pour plus de détails.
c 2004-2011 Jacques Malenfant
�
ALADYN
Bien sûr, une application donne généralement ses propres
classes de métaobjets et de classes d’objets réflexifs réalisant
l’intention du programmeur dans son utilisation de cette forme de
réflexion.
99
c 2004-2011 Jacques Malenfant
�
ALADYN
100
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
Réification des appels et interception
Réflexion de comportement et modification du code
L’exemple du méta-objet verbeux... I
Réification des appels et interception
Réflexion de comportement et modification du code
L’exemple du méta-objet verbeux... II
package sample . r e f l e c t ;
L’appel à la transformation :
import j a v a s s i s t . t o o l s . r e f l e c t . ∗ ;
public class Main {
public s t a t i c void main ( S t r i n g [ ] args ) throws Throwable {
Loader c l = ( Loader ) Main . class . getClassLoader ( ) ;
c l . m a k e R e f l e c t i v e ( " sample . r e f l e c t . Person " ,
" sample . r e f l e c t . VerboseMetaobj " ,
" j a v a s s i s t . t o o l s . r e f l e c t . C la ss M et ao bj ec t " ) ;
public class VerboseMetaobj extends M e ta o b j e ct {
public VerboseMetaobj ( O b j e c t s e l f , O b j e c t [ ] args ) {
super ( s e l f , args ) ;
System . o u t . p r i n t l n ( " ∗∗ c o n s t r u c t e d : " +
s e l f . g e t C l a s s ( ) . getName ( ) ) ;
}
c l . run ( " sample . r e f l e c t . Person " , args ) ;
public O b j e c t t r a p F i e l d R e a d ( S t r i n g name ) {
System . o u t . p r i n t l n ( " ∗∗ f i e l d read : " + name ) ;
r e t u r n super . t r a p F i e l d R e a d ( name ) ;
}
}
}
et la classe des méta-objets verbeux :
public void t r a p F i e l d W r i t e ( S t r i n g name , O b j e c t v a l u e ) {
c 2004-2011 Jacques Malenfant
�
De la réflexion structurelle à la réflexion comportementale
Réflexion structurelle et réification du code en Java
Réflexion de comportement en Java
ALADYN
101
Réification des appels et interception
Réflexion de comportement et modification du code
L’exemple du méta-objet verbeux... III
System . o u t . p r i n t l n ( " ∗∗ f i e l d w r i t e : " + name ) ;
super . t r a p F i e l d W r i t e ( name , v a l u e ) ;
}
public O b j e c t t r a p M e t h o d c a l l ( i n t i d e n t i f i e r , O b j e c t [ ] args )
throws Throwable {
System . o u t . p r i n t l n ( " ∗∗ t r a p : " + getMethodName ( i d e n t i f i e r ) +
" ( ) i n " + g e t C l a s s M e t a o b j e c t ( ) . getName ( ) ) ;
r e t u r n super . t r a p M e t h o d c a l l ( i d e n t i f i e r , args ) ;
}
}
c 2004-2011 Jacques Malenfant
�
ALADYN
103
c 2004-2011 Jacques Malenfant
�
ALADYN
102