ALASCA — Architecture logicielles avancées pour les systèmes

Transcription

ALASCA — Architecture logicielles avancées pour les systèmes
Statistiques par proxy
Statistiques par réflexion au chargement
ALASCA — Architecture logicielles avancées
pour les systèmes complexes auto-adaptables
c 2014–2015 Jacques Malenfant
Master informatique, spécialité STL – UFR 919 Ingénierie
Université Pierre et Marie Curie
[email protected]
1 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Plan
1
Statistiques par proxy
2
Statistiques par réflexion au chargement
2 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Énoncé du problème
Définir une classe Statistics permettant de modifier des objets pour recueillir des
statistiques sur le nombre d’appels aux différentes méthodes de toutes les interfaces
implantées. La classe Statistics propose une méthode :
public static Object toggleStatsOn(Object o) ;
qui retourne pour l’objet o une nouvelle référence à utiliser pour appeler o mais qui
assure que les statistiques seront collectées.
L’objet retourné par cette méhode implante l’interface suivante :
public interface ProvideStatistics {
public String getCurrentStatistics() ;
public void printCurrentStatistics() ;
}
La première méthode retournant une chaîne résumant les statistiques collectées et la
seconde imprimant ces statistiques au terminal. La forme de cette chaîne devrait être
la suivante :
<signature de la méthode 1> : <nombre> appels
...
<signature de la méthode n> : <nombre> appels
3 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java I
public class Statistics {
public static Object toggleStatsOn(Object o) {
Object proxy = null ;
ClassLoader cl = o.getClass().getClassLoader() ;
Class<?>[] interfaces = o.getClass().getInterfaces() ;
Class<?>[] newInterfaces = new Class[interfaces.length + 1] ;
for (int i = 0; i < interfaces.length; i++) {
newInterfaces[i] = interfaces[i] ;
}
newInterfaces[interfaces.length] = ProvideStatistics.class ;
proxy = java.lang.reflect.Proxy.newProxyInstance(
cl, newInterfaces, new StatisticsIH(o));
return proxy ;
}
}
import java.lang.reflect.*;
public class StatisticsIH implements InvocationHandler {
protected Object target ;
protected StatisticGatherer sg ;
public StatisticsIH(Object target) {
super();
this.target = target;
4 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java II
this.sg = new StatisticGatherer() ;
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
String name = m.getName() ;
if ("getCurrentStatistics".equals(name)) {
return sg.getCurrentStatistics() ;
} else if ("printCurrentStatistics".equals(name)) {
sg.printCurrentStatistics() ;
return null ;
} else {
sg.addCallToMethod(m.toGenericString()) ;
return m.invoke(target, args) ;
}
}
}
import java.util.*;
public class StatisticGatherer implements ProvideStatistics {
protected Hashtable<String, Integer> methodCallStatistics ;
public StatisticGatherer() {
super();
this.methodCallStatistics = new Hashtable<String, Integer>() ;
5 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java III
}
public void addCallToMethod(String m) {
int numberOfCalls = 0 ;
if (methodCallStatistics.containsKey(m)) {
numberOfCalls = methodCallStatistics.get(m) ;
methodCallStatistics.remove(m) ;
}
methodCallStatistics.put(m, numberOfCalls + 1) ;
}
public String getCurrentStatistics() {
String res = "" ;
int maxLength = 0 ;
for (Enumeration<String> e = methodCallStatistics.keys();
e.hasMoreElements(); ) {
String element = (String) e.nextElement();
int l = element.length() ;
if (l > maxLength) {
maxLength = l ;
}
}
for (Iterator<Entry<String,Integer>> iter = ((Set<Entry<String,Integer>>)
methodCallStatistics.entrySet()).iterator(); iter.hasNext();) {
6 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java IV
Entry<String,Integer> element = (Entry<String,Integer>) iter.next();
String signature = ((String)element.getKey()) ;
for (int i = signature.length(); i < maxLength; i++) {
signature = signature + " " ;
}
res = res + signature + " : " + element.getValue() + " calls\n" ;
}
return res ;
}
public void printCurrentStatistics() {
System.out.println(this.getCurrentStatistics()) ;
}
}
import fr.upmc.alasca.statistics.ProvideStatistics;
import fr.upmc.alasca.statistics.Statistics;
public class Test {
public static void main(String[] args) {
C c = new C() ;
IC proxy = (IC)Statistics.toggleStatsOn(c) ;
ProvideStatistics proxyStats = (ProvideStatistics)proxy ;
for (int i = 0; i < 10 ; i++) {
proxy.m1() ;
7 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java V
}
for (int i = 0; i < 5 ; i++) {
proxy.m2() ;
}
proxyStats.printCurrentStatistics() ;
}
}
> java fr.upmc.alasaca.statistics.tests.Test
m1
...
// 10 fois
m2
...
// 5 fois
fr.upmc.alasca.statistics.tests.C2.m2() : 5 calls
fr.upmc.alasca.statistics.tests.C2.m1() : 10 calls
8 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Plan
1
Statistiques par proxy
2
Statistiques par réflexion au chargement
9 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Énoncé du problème
Éviter de forcer l’utilisateur àutiliser la référence retournée par toggleStatsOn pour
que les statistiques soient accumulées (les appels hors de cette référence ne sont
pas pris en compte). Utiliser la transformation de classe au chargement pour que
toute classe qui est annotée par l’annotation suivante :
public interface@ ProvideStatisticsAnnotation { }
crée des objets permettant d’activer et de désactiver la prise de statistiques. On leur
demandant d’implanter l’interface suivante :
public interface ProvideStatistics2 extends ProvideStatistics {
// Starts taking call statistics
public Object toggleStatsOn() ;
// Stops taking call statistics
public void toggleStatsOff() ;
// Resets to 0 all call statistics
public Object resetStatistics() ;
}
10 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java I
import javassist.*;
public class StatisticsTransformation {
public static void main(String[] args) {
Translator t = new StatisticsTranslator() ;
ClassPool pool = ClassPool.getDefault() ;
Loader cl = new Loader() ;
String application = args[0] ;
String[] newArgs = new String[args.length - 1] ;
for (int i = 1; i < args.length; i++) {
newArgs[i - 1] = args[i] ;
}
try {
cl.addTranslator(pool, t) ;
cl.run(application, newArgs) ;
} catch (Throwable e) {
e.printStackTrace();
}
}
}
import javassist.*;
public class StatisticsTranslator implements Translator {
public void onLoad(ClassPool pool, String classname)
11 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java II
throws NotFoundException, CannotCompileException {
CtClass cc = pool.get(classname) ;
if (this.hasStatisticsAnnotation(cc)) {
System.out.println(cc.getName() + " modified.") ;
pool.get("fr.upmc.alasca.statistics.ProvideStatistics2") ;
pool.get("fr.upmc.alasca.statistics.StatisticGatherer2") ;
cc.addField(CtField.make("fr.upmc.alasca.statistics.StatisticGatherer2 sg ;",
CtConstructor[] constructors = cc.getConstructors() ;
for (int i = 0; i < constructors.length; i++) {
constructors[i].insertAfter(
"{ this.sg = new fr.upmc.alasca.statistics.StatisticGatherer2() ; }") ;
} // end for
if (constructors.length == 0) {
cc.addConstructor(CtNewConstructor.make(
"public " + cc.getName() + "() {" +
" this.sg = new fr.upmc.alasca.statistics.StatisticGatherer2() ; }",
cc)) ;
}
cc.addMethod(CtNewMethod.make(
"public String getCurrentStatistics() {" +
"
return this.sg.getCurrentStatistics() ; }",
cc)) ;
cc.addMethod(CtNewMethod.make(
"public void printCurrentStatistics() {" +
12 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java III
"
this.sg.printCurrentStatistics() ; }",
cc)) ;
cc.addMethod(CtNewMethod.make(
"public Object toggleStatsOn() {" +
"
this.sg.toggleStatsOn() ; return this ; }",
cc)) ;
cc.addMethod(CtNewMethod.make(
"public void toggleStatsOff() { this.sg.toggleStatsOff() ; }",
cc)) ;
cc.addMethod(CtNewMethod.make(
"public Object resetStatistics() {" +
"
this.sg.resetStatistics() ; return this ; }",
cc)) ;
CtClass[] interfaces = cc.getInterfaces() ;
cc.addInterface(
pool.get("fr.upmc.alasca.statistics.ProvideStatistics2")) ;
for (int i = 0 ; i < interfaces.length ; i++) {
CtMethod[] imethods = interfaces[i].getMethods() ;
for (int j = 0 ; j < imethods.length ; j++) {
try {
CtMethod m = cc.getDeclaredMethod(
imethods[j].getName(),
imethods[j].getExceptionTypes()) ;
m.insertBefore("this.sg.addCallToMethod(\"" +
13 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java IV
m.getLongName() + "\") ;") ;
} catch(NotFoundException e) {
;
}
}
}
}
}
protected boolean hasStatisticsAnnotation(CtClass cc) {
Object[] annotations = new Object[0];
try {
annotations = cc.getAnnotations();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
boolean res = false ;
for (int i = 0; !res && i < annotations.length; i++) {
if (annotations[i] instanceof ProvideStatisticsAnnotation) {
res = true ;
}
}
return res ;
}
14 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java V
public void start(ClassPool arg0) throws NotFoundException,
CannotCompileException {
}
}
import java.util.Hashtable;
public class StatisticGatherer2 extends StatisticGatherer
implements ProvideStatistics2 {
protected boolean statsOn ;
public StatisticGatherer2() {
this.methodCallStatistics = null ;
this.statsOn = false ;
}
public Object resetStatistics() {
this.methodCallStatistics = new Hashtable<String, Integer>() ;
this.statsOn = true ;
return null ;
}
public void toggleStatsOff() { this.statsOn = false ; }
public Object toggleStatsOn() {
this.methodCallStatistics = new Hashtable<String, Integer>() ;
this.statsOn = true ;
return null ;
}
15 / 16
Statistiques par proxy
Statistiques par réflexion au chargement
Code Java VI
public boolean hasStatsOn() { return this.statsOn ; }
@Override
public void addCallToMethod(String m) {
if (this.hasStatsOn()) {
super.addCallToMethod(m);
}
}
}
> java fr.upmc.alasaca.statistics.StatisticsTransformation
fr.upmc.alasaca.statistics.tests.test2
fr.upmc.alasca.statistics.tests.C2 modified.
m1
...
// 10 fois
m2
...
// 5 fois
fr.upmc.alasca.statistics.tests.C2.m2() : 5 calls
fr.upmc.alasca.statistics.tests.C2.m1() : 10 calls
16 / 16