Cours 2 Architectures logicielles dynamiquement adaptables

Transcription

Cours 2 Architectures logicielles dynamiquement adaptables
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
ALASCA — Architecture logicielles avancées
pour les systèmes complexes auto-adaptables
Cours 2
c 2014–2017 Jacques Malenfant
Architectures logicielles
Master informatique, spécialité STL – UFR 919 Ingénierie
dynamiquement adaptables
Université Pierre et Marie Curie
[email protected]
1 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Objectifs pédagogiques du cours 2
2 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Plan
Introduire la notion de réflexivité logicielle pour répondre à une
partie du cahier des charges de la fonction d’auto-adaptabilité.
1
Adaptabilité logicielle
Comprendre et apprendre à utiliser la réflexivité structurelle de
Java pour réaliser des opérations d’adaptation dynamique.
2
Réflexivité structurelle en Java
Introduire la notion de réflexivité comportementale et sa
déclinaison en Java.
3
Exemple : connecteur de composants par proxy
Comprendre et apprendre à utiliser la réflexivité comportementale
de Java pour réaliser des opérations d’adaptation dynamique.
4
Réflexivité comportementale en Java et Javassist
5
Exemple : connecteur généré avec Javassist
6
Réflexivité en BCM
Comprendre et apprendre à utiliser la réflexivité architecturale
dans les modèles à composants comme BCM pour réaliser des
opérations d’adaptation dynamique.
3 / 60
4 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Mécanismes pour l’adaptabilité dynamique (rappel) I
2
3
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Mécanismes pour l’adaptabilité dynamique (rappel) II
Pour un système informatique, l’adaptation peut se décliner en
quatre grandes familles de mécanismes :
1
— Adapt. logicielle Réfl. structurelle
4
paramétriques : changement de paramètres de configuration
n’impliquant pas un impact significatif sur les ressources
consommées ni sur celles nécessaires pour l’adaptation.
Exemples : taille de tampons bornés, nombre de threads, etc.
ressources : changement dans la quantité de ressources mises à
la disposition de l’entité ayant un impact significatif à la fois sur la
QdS et sur le coût d’opération.
Exemples : allocation de machines virtuelles dans un centre de
calcul, variation de la fréquence de processeurs, etc.
fonctionnelles : modification au contenu des programmes
entraînant un changement dans ses fonctionnalités et la QdS
associée.
Exemples : ajout/retrait/modification de code, recompilation et
optimisation de code, etc.
architecturaux : modification à la structure des programmes
entraînant aussi un changement dans ses fonctionnalités et la
QdS associée, et pouvant demander de longs délais pour être
réalisées dans le cas des systèmes répartis.
Exemples : remplacement de bibliothèques dynamiques,
modification de l’assemblage de composants, déconnexion et
reconnexion dynamique de services web, etc.
Familles qui ont un recouvrement entre elles, et donc des
frontières floues.
Caractéristiques essentielles : impact sur la difficulté de réaliser
les adaptations, leur coût, les contraintes qu’elles imposent, la
prédictibilité de leurs effets.
5 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Classes et portée des mécanismes I
6 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Classes et portée des mécanismes II
Outre les familles, on peut considérer un autre axe de
classification des mécanismes d’adaptation :
Comme le suggère la nomenclature précédente, la description et
l’exécution d’adaptations à tout ou partie d’un programme,
éventuellement réparti, est lourde et suggère des langages et de
moteur d’exécution dédiés à ces opérations (DSL...).
les opérations élémentaires, insécables et non-interruptibles,
comme la modification d’un paramètre, l’allocation d’une quantité
donnée d’une ressource monolithique ;
les opérations composites classiques : séquentielles,
conditionnelles, répétitives, etc.
les opérations composites temporisées et réparties, comme des
plans exécutés sur différentes entités, déployées sur différents
ordinateurs et l’expression de leurs dépendances temporelles,
temporisées et fonctionnelles ;
les opérations coordonnées, à petite, moyenne et grande échelle
nécessitant des mécanismes de propagation, de synchronisation
et de gestion des dépendences de types vagues de mises à jour
et front d’adaptation dans un réseau à grande échelle, par
exemple.
Nous allons maintenant nous concentrer sur certaines opérations
élémentaires d’adaptations fonctionnelles et architecturales qui
ont été développées depuis une trentaine d’années dans le
domaine des langages de programmation et des modèles à
composants.
7 / 60
8 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Architectures réflexives et programmation
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Réflexion et architectures à base de composants
Concept lié à la modification dynamique des programmes, une
pratique aussi ancienne que l’informatique elle-même.
Des modèles de composants, comme BCM et Fractal, prévoient
des interfaces d’introspection et d’intro-action permettant de
découvrir et modifier les interfaces requises et offertes par les
composants, les interconnexions entre composants, ainsi que
d’autres informations sur l’achitecture de l’application.
Conceptualisée en tant que discipline par Brian Smith au début
des années 1980.
Exemple : réflexion en Java
Java définit une grande partie des éléments constitutifs d’un
programme sous la forme d’objets, comme les classes, les
méthodes, etc. (java.lang.reflect).
Java prévoit le chargement dynamique de code ainsi que des
moyens pour récupérer et remplacer le code (classes) chargé
dans sa machine virtuelle (java.lang.instrumentation).
Des bibliothèques externes permettent de modifier
dynamiquement le code compilé des classes (Javassist).
Bien que relativement élémentaires, Java offre des moyens
standardisés pour observer des informations sur l’exécution des
programmes (java.lang.management et JMX).
La réflexion dans une architecture à base de composants doit
donc permettre de modifier cette architecture dynamiquement, en
ajoutant, retranchant et remplaçant des composants pendant
l’exécution, pour adapter un système à base de composants à
son état d’exécution courant.
9 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Plan
10 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Réifier = décrire + accéder
1
Adaptabilité logicielle
2
Réflexivité structurelle en Java
3
Exemple : connecteur de composants par proxy
4
Réflexivité comportementale en Java et Javassist
5
Exemple : connecteur généré avec Javassist
6
Réflexivité en BCM
Le langage Java possède une API pour la réflexion structurelle
fournie par les classes Object et Class de même que le
paquetage java.lang.reflect, que nous allons maintenant
rapidement découvrir.
Pour réifier des entités, il faut d’abord les décrire, ou en donner
une représentation : c’est le rôle des classes !
Mais pour avoir accès à la description des objets, il faut pouvoir
manipuler dynamiquement les classes elle-mêmes.
) Comment faire ?
) représenter les classes comme des objets.
) mais alors, quelle sera la classe qui va décrire ces
objets qui sont en réalité des classes ?
Métaclasse : classe dont les instances (objets) sont des classes.
Concept introduit en Smalltalk (1976), puis étudié par Cointe et
Briot (1984-87) dont le problème de clôture de la régression
potentiellement infinie.
11 / 60
12 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
La métaclasse java.lang.Class<T>
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Trois façons de récupérer un objet classe
C’est la pseudo-métaclasse1 unique de tous les objets
représentant des classes en Java, donc la méta-métaclasse de
toutes les classes.
Par la méthode getClass de la classe Object :
Point p = new Point(0.0, 0.0) ;
Class<Point> point = p.getClass() ;
La variable de type T représente le type des objets instances de
cette classe, donc la classe elle-même.
Exemple : Class<Point> = métaclasse de Point.
Par la méthode statique forName de la classe Class :
Class<?> maClasse ;
try {
maClasse = Class.forName("Point") ;
} catch(ClassNotFoundException e) {
System.out.println("La classe Point n’existe pas.") ;
throw e ;
}
C’est une classe finale, donc non-héritable.
Tous les objets « classes » sont ainsi des « instances » de
l’unique « pseudo-métaclasse » : java.lang.Class<T>.
) pas de nouvelles formes de métaclasses.
) pas de modification de la réprésentation ou du
Par la variable statique class associée à chaque classe :
Class<Point> uneAutreReferenceSurPoint = Point.class ;
comportement des classes.
Exemple : classes singleton qui connaissent leur unique instance.
1 C’est-à-dire, pas une métaclasse de plein droit car non-remplaçable et non-extensible.
13 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Principales méthodes de java.lang.Class<T> I
14 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Principales méthodes de java.lang.Class<T> II
<A extends Annotation> getAnnotation(Class<A> annotationClass) Returns this
Field[] getDeclaredFields() : Returns an array of Field objects reflecting all the fields
element’s annotation for the specified type if such an annotation is present,
else null.
declared by the class or interface represented by this Class object.
Method getDeclaredMethod(String name, Class... parameterTypes) : Returns a
ClassLoader getClassLoader() : Returns the class loader for the class.
Method object that reflects the specified declared method of the class or
interface represented by this Class object.
Constructor<T> getConstructor(Class... parameterTypes) : Returns a Constructor
object that reflects the specified public constructor of the class represented
by this Class object.
Method[] getDeclaredMethods() : Returns an array of Method objects reflecting all the
methods declared by the class or interface represented by this Class object.
Constructor[] getConstructors() : Returns an array containing Constructor objects
Field getField(String name) : Returns a Field object that reflects the specified public
reflecting all the public constructors of the class represented by this Class
object.
member field of the class or interface represented by this Class object.
Field[] getFields() : Returns an array containing Field objects reflecting all the accessible
Constructor getDeclaredConstructor(Class... parameterTypes) : Returns a
public fields of the class or interface represented by this Class object.
Constructor object that reflects the specified constructor of the class or
interface represented by this Class object.
Class[] getInterfaces() : Determines the interfaces implemented by the class or interface
represented by this object.
Constructor[] getDeclaredConstructors() : Returns an array of Constructor objects
Method getMethod(String name, Class... parameterTypes) : Returns a Method
reflecting all the constructors declared by the class represented by this Class
object.
object that reflects the specified public member method of the class or
interface represented by this Class object.
Field getDeclaredField(String name) : Returns a Field object that reflects the specified
declared field of the class or interface represented by this Class object.
15 / 60
16 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Principales méthodes de java.lang.Class<T> III
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Le « package » java.lang.reflect
java.lang.Object
java.lang.reflect.AccessibleObject (implements java.lang.reflect.AnnotatedElement)
java.lang.reflect.Constructor<T> (implements java.lang.reflect.GenericDeclaration,
java.lang.reflect.Member)
java.lang.reflect.Field (implements java.lang.reflect.Member)
java.lang.reflect.Method (implements java.lang.reflect.GenericDeclaration,
java.lang.reflect.Member)
java.lang.reflect.Array
java.lang.reflect.Modifier
java.security.Permission (implements java.security.Guard, java.io.Serializable)
java.security.BasicPermission (implements java.io.Serializable)
java.lang.reflect.ReflectPermission
java.lang.reflect.Proxy (implements java.io.Serializable)
java.lang.Throwable (implements java.io.Serializable)
java.lang.Error
java.lang.LinkageError
java.lang.ClassFormatError
java.lang.reflect.GenericSignatureFormatError
java.lang.Exception
java.lang.reflect.InvocationTargetException
java.lang.RuntimeException
java.lang.reflect.MalformedParameterizedTypeException
java.lang.reflect.UndeclaredThrowableException
Method[] getMethods() : Returns an array containing Method objects reflecting all the public
member methods of the class or interface represented by this Class object,
including those declared by the class or interface and and those inherited
from superclasses and superinterfaces.
String getName() : Returns the name of the entity (class, interface, array class, primitive
type, or void) represented by this Class object, as a String.
Class<? super T> getSuperclass() : Returns the Class representing the superclass of
the entity (class, interface, primitive type or void) represented by this Class.
boolean isAssignableFrom(Class<?> cls) : Determines if the class or interface
represented by this Class object is either the same as, or is a superclass or
superinterface of, the class or interface represented by the specified Class
parameter.
T newInstance() : Creates a new instance of the class represented by this Class object.
17 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
La classe java.lang.reflect.Field I
18 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
La classe java.lang.reflect.Field II
Classe finale dont les instances représentent les champs.
Champs : variables, constantes.
Peuvent être de classe (statiques) ou d’instance.
String getName() : Returns the name of the field represented by this Field object.
Principales méthodes :
Class<?> getType() : Returns a Class object that identifies the declared type for the field
represented by this Field object.
Object get(Object obj) : Returns the value of the field represented by this Field, on the
void set(Object obj, Object value) : Sets the field represented by this Field object on
specified object.
the specified object argument to the specified new value.
<T extends Annotation> getAnnotation(Class<T> annotationClass) : Returns this
element’s annotation for the specified type if such an annotation is present,
else null.
Class<?> getDeclaringClass() : Returns the Class object representing the class or
interface that declares the field represented by this Field object.
int getModifiers() : Returns the Java language modifiers for the field represented by this
Field object, as an integer.
19 / 60
20 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
La classe java.lang.reflect.Method I
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
La classe java.lang.reflect.Method II
Class<?>[] getExceptionTypes() : Returns an array of Class objects that represent the
Classe finale dont les instances représentent les méthodes.
types of the exceptions declared to be thrown by the underlying method
represented by this Method object.
Ne permet pas de manipuler le code des méthodes, mais plutôt
en découvrir la signature et informations associées.
int getModifiers() : Returns the Java language modifiers for the method represented by
this Method object, as an integer.
String getName() : Returns the name of the method represented by this Method object, as a
Elle offre une méthode invoke pour appliquer la méthode sur un
objet avec des paramètres réels donnés.
) on touche ici à la frontière avec la réflexion de comportement !
) c’est aussi la limite entre ce qui est visible à propos des
méthodes en Java et ce qui ne l’est pas...
String.
Annotation[][] getParameterAnnotations() : Returns an array of arrays that represent
the annotations on the formal parameters, in declaration order, of the method
represented by this Method object.
Class<?>[] getParameterTypes() : Returns an array of Class objects that represent the
formal parameter types, in declaration order, of the method represented by
this Method object.
Principales méthodes :
Class<?> getReturnType() : Returns a Class object that represents the formal return type
<T extends Annotation> getAnnotation(Class<T> annotationClass) : Returns this
of the method represented by this Method object.
element’s annotation for the specified type if such an annotation is present,
else null.
Object invoke(Object obj, Object... args) : Invokes the underlying method
represented by this Method object, on the specified object with the specified
parameters.
Class<?> getDeclaringClass() : Returns the Class object representing the class or
interface that declares the method represented by this Method object.
21 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
La méthode invoke
22 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Forme de réflexion offerte par Java
En résumé :
La méthode invoke de la classe Method permet donc de faire
s’exécuter une méthode à partir de l’instance de la classe
Method qui la représente réflexivement.
Exemple :
De la réflexion structurelle limitée à l’introspection.
L’objet obtenu par Class.class est lui-même une instance de
Class, ce qui clôt la régression potentiellement infinie de niveaux
de métaclasses.
try {
Method m = String.class.getMethod("length", new Class<?>[]{}) ;
System.out.println(m.invoke("toto", new Object[]{})) ;
} catch (Exception e) { e.printStackTrace(); }
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égrante de la réflexion structurelle.
Cela montre la puissance, mais aussi la limite de l’API réflexion
de Java : le code des méthodes peut être invoqué, mais il ne peut
pas être introspecté et encore moins modifié.
Pour pallier à ces limitations, Java offre des mécanismes plus
restreints, comme l’interception des appels de méthodes par des
proxys.
23 / 60
24 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Plan
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Mécanisme de proxy dynamique de Java
1
Adaptabilité logicielle
Mécanisme général d’interception des appels : introduire un objet
entre un client et un fournisseur qui va intercepter « tous » les
appels du premier au second.
2
Réflexivité structurelle en Java
En Java, il s’agit :
3
Exemple : connecteur de composants par proxy
1
2
4
Réflexivité comportementale en Java et Javassist
5
Exemple : connecteur généré avec Javassist
6
Réflexivité en BCM
3
de créer un intercepteur (proxy ) à partir des interfaces implantées
par le fournisseur ;
de lier à cet intercepteur un objet dit « invocation handler » qui va
effectivement traiter les appels reçus par l’intercepteur ;
pour ce faire, cet « invocation handler » implante une interface
standard, contenant une méthode invoke.
La classe java.lang.Proxy, superclasse de toutes les classes
de proxy créées dynamiquement, propose des méthodes
statiques pour créer des classes de proxy ou directement des
objets proxy (dont la classe est créée implicitement).
25 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Énoncé du problème
26 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Interface offerte et requise
Dans le modèle à composants BCM, les composants exposent et
requièrent des services par leurs ports entrants et sortants
respectivement, reliés par des connecteurs implantant l’interface
requise et appelant le port entrant sur l’interface offerte.
public interface CalculatorServicesI
extends OfferedI
{
public double add(double x, double y) throws Exception;
public double subtract(double x, double y) throws Exception ;
}
public interface SummingServiceI
extends RequiredI
{
public double sum(double x, double y) throws Exception ;
}
La solution la plus évidente consiste à créer chaque connecteur
manuellement, en les programmant.
Une solution plus satisfaisante serait de créer les connecteurs
automatiquement à partir des interfaces requise et offerte.
La méthode requise sum est réalisée par la méthode offerte add, ce
qui donne le connecteur suivant :
Dans certains cas, c’est possible de le faire en utilisant les proxys
dynamiques de Java.
public class Connector
extends AbstractConnector
implements SummingServiceI
{
@Override
public double sum(double x, double y) throws Exception
{
return ((CalculatorServicesI)this.offering).add(x, y) ;
}
}
Illustrons cela sur un exemple d’un composant Calculator
offrant une interface CalculatorServicesI appelé par un
composant VectorSummer via une interface requise
SummingServiceI.
27 / 60
28 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Comment procéder ?
1
Le proxy va jouer le rôle de connecteur ; il est donc créé sur les
interfaces ConnectorI et SummingServiceI.
2
C’est l’invocation handler qui va traiter les appels, et donc il faut
lui fournir une correspondance entre les méthodes de l’interface
requise et celles de l’interface offerte ; par simplicité, faisons cette
correspondance uniquement sur les noms en supposant que le
nombre, les types et l’ordre des paramètres sont les mêmes.
3
4
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
L’invocation handler I
public class ConnectorIH extends AbstractConnector implements InvocationHandler
{
protected HashMap<String, String> methodNamesMap ;
protected ConnectorI
proxy ;
public ConnectorIH(HashMap<String, String> methodNamesMap) {
this.methodNamesMap = methodNamesMap ;
}
public void setProxy(ConnectorI proxy) { this.proxy = proxy ; }
@Override
protected ConnectorI thisConnector() { return this.proxy ; }
En tant que connecteur, chaque méthode exécutée par
l’invocation handler doit toujours considérer que c’est le proxy qui
joue le rôle de l’objet connecteur.
protected boolean isConnectorMethod(String methodName) {
Method[] connectorMethods = ConnectorI.class.getMethods() ;
boolean ret = false ;
for(int i = 0 ; !ret && i < connectorMethods.length ; i++ ) {
ret = connectorMethods[i].getName().equals(methodName) ;
}
return ret ;
}
L’invocation handler doit distinguer les appels qui le concerne en
tant que connecteur des appels qu’il doit relayer au port entrant
et parmi ces derniers ceux qui font partie de l’interface offerte de
ceux qui concernent le port entrant en tant que port.
29 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
L’invocation handler II
30 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Création du proxy et séquence de connexion
public Object makeConnectorProxy(
Class<?> requiredInterface,
HashMap<String, String> methodNamesMap
) throws Exception
{
ClassLoader cl = this.getClass().getClassLoader() ;
ConnectorIH ih = new ConnectorIH(methodNamesMap) ;
ConnectorI proxy =
(ConnectorI) Proxy.newProxyInstance(
cl,
new Class<?>[]{ConnectorI.class, requiredInterface},
ih) ;
ih.setProxy(proxy) ;
return proxy ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
Class<?>[] pt = null ;
if (args != null) {
pt = new Class<?>[args.length] ;
for (int i = 0 ; i < args.length ; i++) {
pt[i] = args[i].getClass() ;
}
}
if (this.isConnectorMethod(method.getName())) {
return method.invoke(this, args) ;
} else {
String methodName = this.methodNamesMap.get(method.getName()) ;
if (methodName == null) { methodName = method.getName() ; }
Method m = this.offering.getClass().getMethod(methodName, pt) ;
return m.invoke(this.offering, args) ;
}
HashMap<String, String> methodNamesMap = new HashMap<String, String>() ;
methodNamesMap.put("sum", "add") ;
ConnectorI connector =
(ConnectorI) this.makeConnectorProxy(SummingServiceI.class,
methodNamesMap) ;
p.doConnection(ServerPortURI, connector) ;
}
}
31 / 60
32 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Plan
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
De la réflexion de structure à la réflexion de comportement
1
Adaptabilité logicielle
2
Réflexivité structurelle en Java
3
Exemple : connecteur de composants par proxy
4
Réflexivité comportementale en Java et Javassist
5
Exemple : connecteur généré avec Javassist
6
Réflexivité en BCM
La limite de la réflexion en Java 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.
Comment donner accès au code ?
La machine virtuelle Java accède au code par le contenu des
fichiers .class chargé dans sa mémoire par des chargeurs de
classes (« class 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, selon un format précis.
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.
33 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
BCEL, ASM et Javassist
34 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Principes généraux de Javassist
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 ) version sérialisée du fichier .class en un graphe
La JVM exécute les programmes en chargeant, grâce à des « class loaders »,
une représentation de ces dernières générée par le compilateur Java dans les
fichiers .class.
Cette représentation totalement connue permet soit de forger ex nihilo des
classes, soit de modifier des classes existantes avant de les mettre dans la
JVM.
Javassist réifie le contenu des classes compilées sous forme d’objets d’une
bibliothèque conçue pour ressembler autant que possible à
java.lang.Class<?> et java.lang.reflect.
Les manipulations sont possibles tant que la classe n’est pas mise à disposition
de la machine virtuelle ; ensuite, la classe est gelée et rendue inaccessible.
d’objets.
ASM ) représentation en tableau monolithique (pour une classe).
Lors du chargement des classes par Java, 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.
Javassist est une autre bibliothèque similaire, mais qui propose
une interface d’introspection des fichiers .class 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.
Grâce aux API JPDA depuis Java 4.0 et java.lang.instrumentation depuis
Java 6.0, on peut récupérer une classe dans la JVM, la modifier, puis la
remettre dans la JVM.
35 / 60
36 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
La notion de ClassPool
Pour créer une nouvelle classe, on utilise la méthode makeClass.
Pour créer une classe Point vide, on fait :
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 " ) ;
Il est également possible de lire une classe puis de la renommer
pour former une nouvelle classe :
même format,
d’assure l’unicité de l’instance de CtClass pour une classe
donnée et maintient la consistence de ses transformations.
Obtention d’un collecteur et réification d’une classe :
/ / exemple de m o d i f i c a t i o n
/ / é c r i t u r e s u r disque
Note : la méthode toBytecode() retourne un tableau d’octets (codes octaux)
plutôt que d’écrire le fichier de classe.
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Définition d’une nouvelle classe ex nihilo
Chaque classe est représentée par une unique instance de la
classe CtClass (pour « compile-time class »), version Javassist
de la classe Class de Java.
Ces instances 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 d’octets respectant le
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 ( " 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 " ) ) ;
cc . w r i t e F i l e ( ) ;
— Adapt. logicielle Réfl. structurelle
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 .
37 / 60
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Le chargeur javassist.Loader
38 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
La méthode toClass() de CtClass
Javassist offre donc son propre chargeur de classes permettant
de charger des classes à partir d’un pool Javassist :
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 en Java
...
}
}
39 / 60
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,
la mettant au passage à la disposition de la JVM.
Cela remplace le loadClass de l’exemple précédent par :
ct.toClass() ;
L’effet de bord de cette opération est de mettre la nouvelle classe
dans la JVM pour utilisation dans l’exécution du 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.
40 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Interception au chargement I
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Interception au chargement II
L’intérêt du chargeur Javassist est de fournir un mécanisme
d’interception basé sur un patron Observateur pour s’interposer
entre la lecture des fichiers .class et la mise en place dans la
machine virtuelle de manière à réaliser des transformations au
chargement.
La méthode onLoad est appelée par le chargeur de classe
Javassist avec le collecteur et le nom de la classe *avant* de 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.
Un transformateur est une instance d’une classe implantant
l’interface Translator.
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 ;
}
À partir de là, cette méthode onLoad sera appelée à chaque fois
qu’une classe est chargée.
41 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Une transformation simple : rendre publique
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Les méthodes
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 . . . { }
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 ) ;
}
}
Les méthodes sont représentées par des instances de
CtMethod.
Les constructeurs sont représentés par des instances de
CtConstructor.
Ces classes définissent des méthodes :
insertBefore() and insertAfter() pour ajouter du code
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 ) ;
}
}
Notez ici l’utilisation de deux chargeurs (le chargeur initial implicite 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.
42 / 60
source dans le corps des méthodes,
addCatch() pour ajouter des tratements d’exception sur
l’ensemble du corps de la méthode, et
La méthode insertAt() permet d’introduire du code à une ligne
donnée de la méthode.
Condition : la méthode doit avoir é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.
43 / 60
44 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Contraintes sur le code source
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Exemple
Soit la classe Point suivante :
class P o i n t {
int x , y ;
void move ( i n t dx , i n t dy ) { x += dx ; y += dy ; }
}
Javassist inclut un compilateur Java pour traduire ce code source
en code octal qui est ensuite introduit dans les méthodes.
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.
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 ( ) ;
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.
Pour donner l’équivalent de :
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 ;
}
}
45 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Ajout de méthodes et de champs
46 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Plan
Ajout d’une méthode :
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 ; } " , p o i n t ) ;
p o i n t . addMethod (m) ;
1
Adaptabilité logicielle
2
Réflexivité structurelle en Java
3
Exemple : connecteur de composants par proxy
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 ) ;
4
Réflexivité comportementale en Java et Javassist
puis, ajout sans initialisation :
5
Exemple : connecteur généré avec Javassist
6
Réflexivité en BCM
Ajout d’un champ :
Préparation :
point . addField ( f ) ;
ou encore avec initialisation :
point . addField ( f , " 0 " ) ;
/ / la valeur i n i t i a l e est 0.
47 / 60
48 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Énoncé du problème
1
Reprenons l’exemple précédent : comment éviter de programmer
manuellement le connecteur entre deux ports de composants ?
2
Ici, la solution sera conceptuellement plus simple, puisqu’il s’agit
de créer la classe définissant le connecteur dynamiquement,
mais avec le même code que la classe créée manuellement.
3
Pour créer une classe de connecteur, il faut avoir :
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Génération de la classe de connecteur I
public Class<?> makeConnectorClassJavassist(String connectorCanonicalClassName,
Class<?> connectorSuperclass,
Class<?> connectorImplementedInterface,
Class<?> offeredInterface,
HashMap<String,String> methodNamesMap
) throws Exception
{
ClassPool pool = ClassPool.getDefault() ;
CtClass cs = pool.get(connectorSuperclass.getCanonicalName()) ;
CtClass cii = pool.get(connectorImplementedInterface.getCanonicalName()) ;
CtClass oi = pool.get(offeredInterface.getCanonicalName()) ;
CtClass connectorCtClass = pool.makeClass(connectorCanonicalClassName) ;
connectorCtClass.setSuperclass(cs) ;
Method[] methodsToImplement = connectorImplementedInterface.getDeclaredMethods() ;
for (int i = 0 ; i < methodsToImplement.length ; i++) {
String source = "public " ;
source += methodsToImplement[i].getReturnType().getName() + " " ;
source += methodsToImplement[i].getName() + "(" ;
Class<?>[] pt = methodsToImplement[i].getParameterTypes() ;
String callParam = "" ;
for (int j = 0 ; j < pt.length ; j++) {
String pName = "aaa" + j ;
source += pt[j].getCanonicalName() + " " + pName ;
callParam += pName ;
if (j < pt.length - 1) {
source += ", " ;
callParam += ", " ;
}
}
source += ")" ;
Class<?>[] et = methodsToImplement[i].getExceptionTypes() ;
if (et != null && et.length > 0) {
source += " throws " ;
le nom de la classe à créer,
la superclasse de connecteur à utiliser,
l’interface requise devant être implantée par le connecteur,
l’interface offerte et implantée par le port entrant, et
la correspondance entre les méthodes de l’interface requise et
celles de l’interface offerte.
4
— Adapt. logicielle Réfl. structurelle
Les classes et les interfaces sont représentées par des instances
de la classe java.lang.Class.
49 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Génération de la classe de connecteur II
50 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Séquence de création de la classe et de connexion
for (int z = 0 ; z < et.length ; z++) {
source += et[z].getCanonicalName() ;
if (z < et.length - 1) {
source += "," ;
}
}
}
source += "\n{
return ((" ;
source += offeredInterface.getCanonicalName() + ")this.offering)." ;
source += methodNamesMap.get(methodsToImplement[i].getName()) ;
source += "(" + callParam + ") ;\n}" ;
CtMethod theCtMethod = CtMethod.make(source, connectorCtClass) ;
connectorCtClass.addMethod(theCtMethod) ;
HashMap<String, String> methodNamesMap = new HashMap<String, String>() ;
methodNamesMap.put("sum", "add") ;
Class<?> connectorClass =
this.makeConnectorClassJavassist(
"fr.upmc.alasca.summing.assembly.GeneratedConnector",
AbstractConnector.class,
SummingServiceI.class,
CalculatorServicesI.class,
methodNamesMap) ;
p.doConnection(ServerPortURI,
connectorClass.getCanonicalName()) ;
}
connectorCtClass.setInterfaces(new CtClass[]{cii}) ;
cii.detach() ; cs.detach() ; oi.detach() ;
Class<?> ret = connectorCtClass.toClass() ;
connectorCtClass.detach() ;
return ret ;
}
Ce qui génére le code suivant pour la méthode sum :
public double sum(double aaa0, double aaa1) throws java.lang.Exception
{
return ((fr.upmc.alasca.summing.calculator.interfaces.CalculatorServicesI)
this.offering).add(aaa0, aaa1) ;
}
51 / 60
52 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Plan
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Réflexivité dans un modèle à composants
1
Adaptabilité logicielle
2
Réflexivité structurelle en Java
3
Exemple : connecteur de composants par proxy
4
Réflexivité comportementale en Java et Javassist
5
Exemple : connecteur généré avec Javassist
6
Réflexivité en BCM
Objectif : donner la capacité d’introspecter et d’intro-agir sur les
composants à l’exécution.
Qu’est-ce qu’on vise à gérer ?
des informations définissant le « type » de composants (interfaces
offertes et requises,
des informations statiques définissant les capacités du composant
(passif ou actif, capable d’ordonnancer des tâches ou non, ...) ;
des informations plus dynamiques sur l’état courant du composant
dans son cycle de vie (initialisé, démarré, arrêté, ...) ;
des informations de nature plus architecturales (les ports, leurs
états, leurs connexions, ...).
53 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
L’interface ReflectionI I
54 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
L’interface ReflectionI II
La réflexion en BCM est encore en développement.
// Internal behaviour requests
public boolean
isInStateAmong(ComponentStateI[] states) throws Exception ;
public boolean
notInStateAmong(ComponentStateI[] states) throws Exception ;
public boolean
isConcurrent() throws Exception ;
public boolean
canScheduleTasks() throws Exception ;
Pour l’instant, elle donne accès à des opérations de la machine
virtuelle sur les composants.
// Implemented interfaces management
public Class<?>[] getInterfaces() throws Exception ;
public Class<?>[] getRequiredInterfaces() throws Exception ;
public Class<?>[] getOfferedInterfaces() throws Exception ;
public void
addRequiredInterface(Class<?> inter) throws Exception ;
public void
removeRequiredInterface(Class<?> inter) throws Exception ;
public void
addOfferedInterface(Class<?> inter) throws Exception ;
public void
removeOfferedInterface(Class<?> inter) throws Exception ;
public boolean
isInterface(Class<?> inter) throws Exception ;
public boolean
isRequiredInterface(Class<?> inter) throws Exception ;
public boolean
isOfferedInterface(Class<?> inter) throws Exception ;
Elle permet de gérer les greffons, la journalisation et la trace ainsi
que les interfaces et les ports, mais ne permet que d’interroger
l’état dans le cycle de vie ou de gérer les threads.
public interface ReflectionI extends OfferedI, RequiredI
{
// Plug-ins facilities
public void
installPlugin(PluginI plugin) throws Exception ;
public boolean
hasInstalledPlugins() throws Exception ;
public void
uninstallPlugin(String pluginId) throws Exception ;
public boolean
isInstalled(String pluginId) throws Exception ;
// Port management
public String[]
findPortURIsFromInterface(Class<?> inter) throws Exception ;
public String[]
findInboundPortURIsFromInterface(Class<?> inter) throws Exception ;
public String[]
findOutboundPortURIsFromInterface(Class<?> inter) throws Exception ;
public Class<?>
getPortImplementedInterface(String portURI) throws Exception ;
public boolean
isPortConnected(String portURI) throws Exception ;
public void
doPortConnection(String portURI, String otherPortURI, String ccname) throws Exception ;
public void
doPortDisconnection(String portURI) throws Exception ;
// Logging facilities
public void
toggleLogging() throws Exception ;
public void
toggleTracing() throws Exception ;
public void
logMessage(String message) throws Exception ;
public boolean
isLogging() throws Exception ;
public boolean
isTracing() throws Exception ;
public void
printExecutionLog() throws Exception ;
public void
printExecutionLog(PrintStream out) throws Exception ;
public void
printExecutionLogOnFile(String fileName) throws Exception ;
}
55 / 60
56 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Intégration des capacités réflexives dans les composants
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Court exemple d’utilisation
// Code de déploiement
ReflectionOutboundPort rObp ;
AbstractComponent comp = new AbstractComponent(1, 0) {} ;
this.addDeployedComponent(comp) ;
this.rObp = new ReflectionOutboundPort(new AbstractComponent() {}) ;
this.rObp.publishPort() ;
String[] rpIbpURI =
comp.findInboundPortURIsFromInterface(ReflectionI.class) ;
this.rObp.doConnection(rpIbpURI[0],
ReflectionConnector.class.getCanonicalName());
Tous les composants définissent un port entrant (inbound port)
offrant l’interface ReflectionI.
Le constructeur le plus courant (int, int) génère une URI pour
ce port, mais un autre constructeur (String,int,int) permet
de fournir une URI explicitement.
// Code réflexif exécuté an sein d’un composant
String[] uris =
this.rObp.findInboundPortURIsFromInterface(ReflectionI.class) ;
System.out.println(uris[0]) ;
uris = this.rObp.findInboundPortURIsFromInterface(ComponentPluginI.class) ;
System.out.println(uris[0]) ;
L’interface ReflectionI est ajoutée automatiquement aux
interfaces offertes, et le port entrant créé et publié.
Ce qui donne le résultat suivant :
57 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Récapitulons...
starting...
fr.upmc.components.pre.reflection.interfaces.ReflectionI-4e9a2511-2a24-49fb-af39-7e0bb18227f9
fr.upmc.components.pre.plugins.interfaces.ComponentPluginI-7e8694a5-2eaf-417a-b260-31b981642e5
shutting down...
ending...
58 / 60
— Adapt. logicielle Réfl. structurelle
Connecteur proxy Réfl. comportementale Connecteur Javassist Réflexivité en BCM —
Pour aller plus loin : sélection de lectures recommandées
Lire l’article « Reflection in logic, functional and object-oriented programming : a
Short Comparative Study » disponible sur le site de l’UE.
1
L’auto-adaptabilité logicielle peut se décomposer en quatres grands types :
paramétrique, de ressources, fonctionnelle et architecturale.
2
La réflexivité logicielle est une approche permettant de mettre en œuvre des
opérations d’adaptation fonctionnelle et architecturale des entités logicielles.
3
Java est un langage offrant une forme de réflexion structurelle, en particulier
par son package java.lang.reflect mais également une forme limitée de
réflexion de comportement par son mécanisme de proxy dynamique.
Lire les tutoriaux suivants sur le WWW :
http://tutorials.jenkov.com/java-reflection/index.html
http://onjava.com/pub/a/onjava/2007/03/15/
reflections-on-java-reflection.html
4
Pour obtenir une forme de réflexion de comportement plus complète en
Java, il faut faire appel à des bibliothèques externes, comme Javassist,
permettant de créer du code dynamiquement et de l’intégrer à l’application.
Lire le tutoriel Javassist disponible avec la distribution du logiciel et la page
Javassist du projet JBoss à l’URL http://www.jboss.org/javassist et les
articles qui sont pointés par cette dernière.
5
Les principes de la réflexion s’appliquent également à la programmation par
composants, où les réflexions structurelle et de comportement sont complétées
par de la réflexion architecturale.
Regardez la fonctionnalité réflexion en BCM.
Examiner la Javadoc l’API Reflection de Java (dans la documentation standard
de J2SE).
Lire la description du modèle de composants Fractal dans The Fractal
Component Model, E. Bruneton, T. Coupaye et J.-B. Stefani, version 2.0-3,
2004, disponible sur le site de l’UE.
59 / 60
60 / 60