Examen 2011 - Master informatique
Transcription
Examen 2011 - Master informatique
Master mention informatique
Spécialité STL
Architectures logicielles pour l’auto-adaptabilité dynamique (ALADYN)
Contrôle terminal
Vendredi, 18 novembre 2011, 10h45 – 12h45
Directives
1. Le contrôle dure 2h00.
2. Tous les documents sont autorisés.
3. Tous les appareils électroniques sont prohibés (y compris les téléphones portables, les assistants
numériques personnels et les agendas électroniques).
Question 1.
Validation de paramètres avec proxys.
La présentation de Gary Benattar nous a permis de voir qu’il est possible d’implanter des tests
simples sur les paramètres et le résultat des méthodes par des assertions qui vont donner lieu à
une vérification à l’exécution. Dans cette première question, vous devez proposer une implantation
en Java de telles validations en vous servant de l’approche des proxys.
Considérez l’interface suivante :
package f r . upmc . ala dyn . p a r a m e t e r V a l i d a t i o n . t e s t s ;
import f r . upmc . ala dyn . p a r a m e t e r V a l i d a t i o n . a n n o t a t i o n s . V a l i d a t i o n ;
import f r . upmc . ala dyn . p a r a m e t e r V a l i d a t i o n . a n n o t a t i o n s . V a l i d a t i o n C o n d i t i o n s ;
public i n t e r f a c e IC {
@ V a l i d a t i o n ( c o n d i t i o n=V a l i d a t i o n C o n d i t i o n s . P o s i t i v e )
public i n t
absolute (
@ V a l i d a t i o n ( c o n d i t i o n=V a l i d a t i o n C o n d i t i o n s . N e g a t i v e )
int i ) ;
@ V a l i d a t i o n ( c o n d i t i o n=V a l i d a t i o n C o n d i t i o n s . P o s i t i v e )
public i n t
absoluteWithBug (
@ V a l i d a t i o n ( c o n d i t i o n=V a l i d a t i o n C o n d i t i o n s . N e g a t i v e )
int i ) ;
@ V a l i d a t i o n ( c o n d i t i o n=V a l i d a t i o n C o n d i t i o n s . N e g a t i v e )
public i n t
inverse (
@ V a l i d a t i o n ( c o n d i t i o n=V a l i d a t i o n C o n d i t i o n s . P o s i t i v e )
int i ) ;
public void
printIt (
@ V a l i d a t i o n ( c o n d i t i o n=V a l i d a t i o n C o n d i t i o n s . NonNull )
Obj ect o ) ;
}
L’annotation Validation va servir à indiquer la condition à appliquer. Lorsqu’elle est posée
sur une méthode, elle impose une condition sur le résultat de la méthode, alors que lorsqu’elle
est posée sur un paramètre, elle impose une condition sur le paramètre concerné. Pour la méhode
absolute, par exemple, la condition sur la méthode exige que le résultat soit (strictement) positif,
alors que l’annontation sur le paramètre i exige qu’il soit (strictement) négatif.
Les différentes conditions imposables sont définies par un type énumération :
package f r . upmc . ala dyn . p a r a m e t e r V a l i d a t i o n . a n n o t a t i o n s ;
1
(10 points)
public enum V a l i d a t i o n C o n d i t i o n s {
NonNull ,
// an o b j e c t r e f e r e n c e
NonZero ,
// an i n t , l o n g , f l o a t
Positive ,
// an i n t , l o n g , f l o a t
Negative
// an i n t , l o n g , f l o a t
}
is
or
or
or
not n u l l
double v a l u e i s not zero
double value i s p o s i t i v e
double value i s negative
La classe suivante implante l’interface IC, implantation dans laquelle on constate que la méthode
absoluteWithBug illustre le fait que pour tester si la condition sur le résultat d’une méthode peut
être déclenchée, il faut volontairement écrire une méthode avec une erreur :
package f r . upmc . ala dyn . p a r a m e t e r V a l i d a t i o n . t e s t s ;
public c l a s s C implements IC {
@Override
public i n t a b s o l u t e ( i n t i )
@Override
public i n t absoluteWithBug ( i n t i )
@Override
public i n t i n v e r s e ( i n t i )
@Override
public void p r i n t I t ( Ob ject o )
}
{ return − i ; }
{ return −10 ; }
{ return − i ; }
{ System . out . p r i n t l n ( o . t o S t r i n g ( ) ) ; }
Finalement, la classe suivante montre mes tests JUnit sur ma solution :
package f r . upmc . ala dyn . p a r a m e t e r V a l i d a t i o n . t e s t s ;
import
import
import
import
import
org . j u n i t . Before ;
o r g . j u n i t . Test ;
j a v a . l a n g . r e f l e c t . Un de cl ar ed Th row ab le Ex ce pt io n ;
f r . upmc . ala dyn . p a r a m e t e r V a l i d a t i o n . e x c e p t i o n s . P a r a m e t e r V a l i d a t i o n E x c e p t i o n ;
f r . upmc . ala dyn . p a r a m e t e r V a l i d a t i o n . V a l i d a t o r F a c t o r y ;
public c l a s s
{
protected IC
MainTests
c , vc ;
@Before
public void
setUp ( ) {
c = new C( ) ;
vc = ( IC ) V a l i d a t o r F a c t o r y . c r e a t e ( c , IC . c l a s s ) ;
}
@Test
public void
testAbsoluteOK ( ) { vc . a b s o l u t e ( −5) ; }
@Test ( e x p e c t e d=P a r a m e t e r V a l i d a t i o n E x c e p t i o n . c l a s s )
public void
t e s t A b s o l u t e F a i l W i t h Z e r o ( ) throws Throwable {
try { vc . a b s o l u t e ( 0 ) ;
} catch ( Un de cl ar ed Th ro wa bl eE xc ep ti on e ) {
throw e . g e t U n d e c l a r e d T h r o w a b l e ( ) ;
}
}
@Test ( e x p e c t e d=P a r a m e t e r V a l i d a t i o n E x c e p t i o n . c l a s s )
public void
t e s t A b s o l u t e F a i l W i t h P o s ( ) throws Throwable {
try { vc . a b s o l u t e ( 5 ) ;
} catch ( Un de cl ar ed Th ro wa bl eE xc ep ti on e ) {
throw e . g e t U n d e c l a r e d T h r o w a b l e ( ) ;
}
}
2
@Test ( e x p e c t e d=U nd ec la re dT hr owa bl eE xc ep ti on . c l a s s )
public void
testAbsoluteWithBug ( ) { vc . absoluteWithBug ( 0 ) ; }
@Test
public void
t e s t I n v e r s e O K ( ) { vc . i n v e r s e ( 1 0 ) ; }
@Test ( e x p e c t e d=P a r a m e t e r V a l i d a t i o n E x c e p t i o n . c l a s s )
public void
t e s t I n v e r s e F a i l W i t h Z e r o ( ) throws Throwable {
try { vc . i n v e r s e ( 0 ) ;
} catch ( Un de cl ar ed Th ro wa bl eE xc ep ti on e ) {
throw e . g e t U n d e c l a r e d T h r o w a b l e ( ) ;
}
}
@Test ( e x p e c t e d=P a r a m e t e r V a l i d a t i o n E x c e p t i o n . c l a s s )
public void
t e s t I n v e r s e F a i l W i t h N e g ( ) throws Throwable {
try { vc . i n v e r s e ( −10) ;
} catch ( Un de cl ar ed Th ro wa bl eE xc ep ti on e ) {
throw e . g e t U n d e c l a r e d T h r o w a b l e ( ) ;
}
}
@Test
public void
t e s t P r i n t I t O K ( ) { vc . p r i n t I t (new I n t e g e r ( 5 ) ) ; }
@Test ( e x p e c t e d=P a r a m e t e r V a l i d a t i o n E x c e p t i o n . c l a s s )
public void
t e s t P r i n t I t F a i l W i h N u l l ( ) throws Throwable {
try { vc . p r i n t I t ( null ) ;
} catch ( Un de cl ar ed Th ro wa bl eE xc ep ti on e ) {
throw e . g e t U n d e c l a r e d T h r o w a b l e ( ) ;
}
}
}
Lorsqu’une valeur de paramètre ou de résultat n’est pas valide, l’exception ParamlterValidationException est lancée (le fait qu’elle se retrouve emballée dans une exception UndeclaredThrowableException est dû au traitement automatique de Java).
Nous vous demandons de donner la définition de l’annotation Validation puis d’implanter la
classe ValidatorFactory et sa méthode create utilisée dans les tests pour créer un proxy qui
va tester les conditions à chaque appel des méthodes définies sur l’interface. La méthode create
prend deux paramètres : l’objet dont on veut créer un proxy et l’instance de Class<?> représentant
l’interface servant à la création du proxy.
Nota : pour implanter les tests sur les nombres de manière générique, vous pouvez utiliser le
fait que toutes les classes Integer, Long, Float et Double implantent l’interface Comparable<T>
avec la méthode compareTo qui, appelée sur un nombre a avec en paramètre un nombre b, retourne
un entier négatif si a est inférieur à b, 0 si a est égal à b, et un entier positif si a est supérieur à b.
Vous pouvez alors aussi utiliser le fait qu’elles implantent toutes aussi la méthode statique valueOf
prenant un nombre sous la forme d’une chaîne de caractères et retournant sa valeur dans le type
numérique correspondant.
Question 2.
La programmation contractuelle en Java.
Bertrand Meyer a toujours été un grand défenseur de la programmation contractuelle dans son
langage Eiffel. Cette forme de programmation consiste à concevoir ses classes autour d’un invariant
de représentation et les méthodes autour de pré-conditions sur les paramètres et de post-conditions
sur le résultat. Par rapport à ce que vous avez faits dans la question 1, les conditions sont ici des
expressions booléennes générales, utilisant tout le langage Java, et ces expressions peuvent lier
les valeurs de plusieurs paramètres les uns avec les autres (par exemple, imposer que le premier
paramètre d’une méthode soit plus petit que le second, etc.).
3
(10 points)
Par exemple, la classe suivante implante un réservoir en programmation contractuelle :
package f r . upmc . ala dyn . c o n t r a c t s . t e s t s ;
import f r . upmc . ala dyn . c o n t r a c t s . a n n o t a t i o n s . I n v a r i a n t ;
import f r . upmc . ala dyn . c o n t r a c t s . a n n o t a t i o n s . Post ;
import f r . upmc . ala dyn . c o n t r a c t s . a n n o t a t i o n s . Pre ;
@ I n v a r i a n t ( bexp=" c u r r e n t C o n t e n t >= 0 . 0 && c u r r e n t C o n t e n t <= c a p a c i t y " )
public c l a s s
Tank {
protected double
currentContent ;
protected double
capacity ;
public
Tank ( double c u r r e n t C o n t e n t , double c a p a c i t y ) {
this . currentContent = currentContent ;
this . capacity = capacity ;
}
public boolean
f u l l () {
return t h i s . c a p a c i t y == t h i s . c u r r e n t C o n t e n t ;
}
public boolean
empty ( ) {
return t h i s . c u r r e n t C o n t e n t == 0 . 0 ;
}
@Post ( bexp=" f u l l ( ) " )
public void
f i l l () {
this . currentContent = this . capacity ;
}
@Pre ( bexp=" $1 <= ( c a p a c i t y − c u r r e n t C o n t e n t ) " )
public void
add ( double v ) {
this . currentContent = this . currentContent + v ;
}
@Pre ( bexp=" $1 <= c u r r e n t C o n t e n t " )
public void
consume ( double v ) {
this . currentContent = this . currentContent − v ;
}
@Post ( bexp="empty ( ) " )
public void
drain () {
this . currentContent = 0.0 ;
}
}
L’interprétation des conditions est la suivante :
– à l’entrée d’une méthode, l’invariant de la classe et les pré-conditions doivent être vérifiées,
– à la sortie d’une méthode, l’invariant et les post-conditions doivent être également vérifiées.
Lorsque l’une ou l’autre de ces conditions ne sont pas vérifiées, l’exception correspondante est
lancée : PreconditionViolationException, PostconditionViolationException ou InvariantViolationException.
Bien sûr, il y a plus que ces tests relaivement simples dans la programmation contractuelle, mais
nous vous demandons ici de n’implanter que ces deux règles. Vous devez définir les annotations
Pre, Post et Invariant, puis implanter leur sémantique par transformation des classes concernées
au chargement avec Javassist.
Fin du contrôle terminal.
4