:: Enseignements :: ESIPE :: E4INFO :: 2025-2026 :: Java Inside ::
![[LOGO]](http://monge.univ-eiffel.fr/ens/resources/mlv.png) |
Examen de Java Inside - 2025 - session 1
|
Exercice 1 - Property Mapper
Le but de ce TP est d'implanter une classe PropertyMapper capable
de voir des propriétés de configuration (la classe java.util.Properties)
de façon structurée et typée.
En Java, il existe une classe
java.util.Properties qui permet de lire et écrire
des fichiers de configuration ".properties".
Voici un exemple de fichier de configuration :
server.port=8080
server.host=localhost
La classe
java.util.Properties est une table de hachage qui associe à une clé (une chaine de caractères)
une valeur (aussi une chaine de caractère).
Pour lire ou écrire les couples clé/valeur, on utilise les méthodes
- getProperty(key) pour lire une valeur d'une clé (ou renvoie null)
- setProperty(key, value) pour écrire la valeur d'une clé
Cette API considère que les valeurs sont des chaines de caractères donc pour obtenir la valeur
server.port comme un entier, le développeur qui doit
appeler la méthode
Integer.parseInt à la main.
On se propose de fournir une API
PropertyMapper qui ajoute une couche de
typage qui va grouper les propriétés en fonction d'un préfixe (par exemple,
"server")
et effectuer les conversions de type primitifs vers les chaines de caractères (et inversement).
Voici quelques exemples d'utilisation:
-
Lire des propriétés de même préfixe dans un objet Java Bean.
La méthode PropertyMapper.create() créé une instance de PropertyMapper
à partir d'un texte d'un fichier de configuration.
La méthode extract(prefix, beanType) remplie un objet Java bean avec les propriétés du fichier
de configuration commençant par un même prefixe.
public class AppBean {
private String name;
private String version;
public AppBean() {
}
public void setName(String name) {
this.name = name;
}
public void setVersion(String version) {
this.version = version;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
}
...
var propertyText = """
app.name=TestApp
app.version=1.0
""";
var mapper = PropertyMapper.create(propertyText);
var bean = mapper.extract("app", AppBean.class);
IO.println(bean.getName()); // TestApp
IO.println(bean.getVersion()); // 1.0
-
Créer un proxy sur des propriétés de configuration ayant un même préfixe
Contrairement à un objet Java bean où lorsque l'on appelle un setter,
seuls les champs de l'objet sont modifié, avec un proxy, appeler un setter
change la valeur de l'objet java.util.Properties
car le proxy agit comme une vue.
public interface App {
String getName();
String getVersion();
void setVersion(String version);
}
...
var propertyText = """
app.name=TestApp
app.version=1.0
""";
var mapper = PropertyMapper.create(propertyText);
App proxy = mapper.proxy("app", App.class);
proxy.setVersion("1.1");
IO.println(proxy.getName()); // TestApp
IO.println(proxy.getVersion()); // 1.1
Dans la suite du TP, nous allons implanter la classe
PropertyMapper au fur et à mesure,
voici une ébauche
import java.util.Properties;
import java.io.IOError;
import java.io.IOException;
import java.io.StringReader;
public final class PropertyMapper {
private final Properties properties;
private PropertyMapper(Properties properties) {
this.properties = properties;
super();
}
public static PropertyMapper create(Properties properties) {
Objects.requireNonNull(properties);
return new PropertyMapper(properties); // no defensive copy
}
public static PropertyMapper create(String propertyText) {
Objects.requireNonNull(propertyText);
var properties = new Properties();
try(var reader = new StringReader(propertyText)) {
properties.load(reader);
} catch (IOException e) {
throw new IOError(e);
}
return create(properties);
}
// TODO
La javadoc 25 est
https://igm.univ-mlv.fr/~juge/javadoc-25/.
Les trucs et astuces utilisés pour l'implantation
COMPANION.pdf
La classe
Utils qui gère les exceptions correctement :
Utils.java
Les tests unitaires correspondant à l'examen se trouvent dans la classe
PropertyMapperTest.java
Note : comme on utilise les tests unitaires JUnit sans Maven, dans la configuration de votre projet, il faut ajouter
la librairie JUnit 5, soit à partir du fichier
PropertyMapperTest.java, en cliquant sur l'annotation
@Test et en sélectionnant le quickfix "Fixup project ...", soit en sélectionnant les "Properties" du projet
(avec le bouton droit de la souris sur le projet) puis en ajoutant la librairie JUnit 5 (jupiter) au ClassPath.
-
On cherche dans un premier temps à implanter la méthode extract(prefix, beanType).
qui renvoie un objet Java Bean de la classe prise en paramètre.
L'algorithme est le suivant, le constructeur sans paramètre du beanType est appelé
puis pour chaque property descriptor, la propriété correspondante
est cherchée dans l'objet java.util.Properties (avec le préfixe),
si la propriété possède une valeur associé alors le setter est appelé
avec la valeur de la propriété.
Pour l'instant, on ne va implanter le support que des propriétés dont les valeurs
sont des chaines de caractères.
Écrire la méthode extract(prefix, beanType).
Vérifier que les tests unitaires marqués "Q1" passent.
-
On souhaite que extract fonctionne aussi si le préfixe est "".
Modifier si nécessaire le code de la méthode extract.
Vérifier que les tests unitaires marqués "Q2" passent.
-
On souhaite maintenant écrire la méthode proxy(prefix, beanType).
L'algorithme est un peu différent. Lors de l'appel à la méthode proxy,
le proxy est créé indépendamment du fait que les propriétés sous-jacentes existe ou pas.
En effet, on peut créer les propriétés en appelant les setter de l'objet.
Lorsqu'un getter est appelé, la propriété correspondante (en prenant en compte le préfixe)
est recherchée et sa valeur est retournée (null peut être renvoyé si la propriété n'existe pas).
Lorsqu'un setter est appelé, la propriété correspondante est écrit dans l'objet
java.util.Properties.
Ecrire le code de la méthode proxy(prefix, beanType) sachant que, pour l'instant,
on veut uniquement que les getter marche.
Vérifier que les tests unitaires marqués "Q3" passent.
-
Modifier votre code pour que les setter du proxy fonctionnent aussi.
Vérifier que les tests unitaires marqués "Q4" passent.
-
En fait, on veut maintenant une implantation efficace des getter et
setter. Pour cela, on va utiliser un dictionnaire qui associe
à l'objet java.lang.reflect.Method, une fonction qui execute le code
du getter ou du setter.
Le code de l'InvocationHandler devrait donc être
var op = propertyMap.get(method);
if (op != null) {
return op.apply(args == null ? null : args[0]);
}
throw new UnsupportedOperationException("Method not supported: " + method);
Faites les changements qui s'imposent dans votre code.
Les tests unitaires devraient continuer de passer.
Note: si vous n'y arrivez pas, vous pouvez passer à la question suivante.
-
On souhaite que si getter renvoie une interface, alors
le getter renvoie un nouvel objet proxy qui permet de voir les propriétés
qui commence par le même sous préfixe.
Par exemple, on souhaite que le code suivant fonctionne
public interface Address {
String getStreet();
}
public interface Person {
String getName();
Address getAddress(); // returns an interface
}
...
var propertyText = """
user.name=Alice
user.address.street=32 canyon road
""";
var mapper = PropertyMapper.create(propertyText);
var person = mapper.proxy("user", Person.class);
var address = person.getAddress(); // returns a new proxy
IO.println(person.getName()); // Alice
IO.println(address.getStreet()); // 32 canyon road
Modifier votre code pour que cela fonctionne.
Vérifier que les tests unitaires marqués "Q6" passent.
Note: en termes de performance, vous ne devriez créer l'objet proxy correspondant
à une adresse que si nécessaire.
-
On souhaite ajouter une méthode freeze() qui interdit toutes modifications
ultérieures des propriétés par les setter des proxies (même si ceux-ci ont été créés avant).
Écrire la méthode freeze.
Vérifier que les tests unitaires marqués "Q7" passent.
-
Enfin, on veut que les Java Bean et les proxies fonctionnent avec des types primitifs.
Modifier votre implantation en conséquence.
Vérifier que les tests unitaires marqués "Q8" passent.
Note: pour le type char, la chaine de caractères correspondante devra
avoir un seul caractère !
© Université de Marne-la-Vallée