Tutorial: Création d’une application sans base de données avec le Visual Web Pack
Rendre notre classe Comparable
Maintenant, nous allons implémenter l’interface Comparable. De cette manière, il sera facilement possible d’affecter un ordre par défaut à nos données (l’ordre alphabétique sur les noms dans notre cas), au lieu de se contenter de l’ordre dans lequel les données sont présentes dans le fichier person.txt. Modifions la déclaration de votre classe en public class Person implements Comparable {
Netbeans va alors signaler en rouge que notre classe contient une erreur: en effet, nous indiquons que notre classe implémente Comparable, mais les méthodes nécessaires ne sont pas présentes. Cliquons sur l’ampoule que Netbeans affiche, et sélectionnons la correction proposée: Implements all abstract methods. Netbeans rajoute alors la méthode compareTo()… mais encore une fois soulignée de rouge: en effet la méthode est censée retourner un int, mais ici elle ne retourne rien.
Nous pouvons terminer d’implémenter Comparable comme suit: c’est la valeur de l’attribut name de Person qui va permettre de les classer. Utilisons également la méthode toLowerCase() pour éviter les erreurs de tri si des personnes ne sont pas toutes saisies de la même manière.
Déjardin (Creabeans)
public int compareTo(Object o) {
return compareTo((Person) o);
}
public int compareTo(Person person) {
return (this.name.toLowerCase()).compareTo(person.getName().toLowerCase());
}
Rendre notre classe Serializable
Lorsque Tomcat s’arrête, il essaie de conserver les informations relatives aux sessions des utilisateurs, afin de pouvoir redémarrer plus tard dans les mêmes conditions que précédemment. Pour ce faire, Tomcat va tenter de sérialiser tous les objets en mémoire. Afin d’éviter de remplir nos fichiers de logs avec de disgracieuses java.io.NotSerializableException, nous allons déclarer que notre classe implémente l’interfaceSerializable.
Complétons la classe Person en rajoutant Serializable dans la liste des interfaces:
public class Person implements Comparable, Serializable {
L’ampoule et le surlignage rouge signalant une erreur apparaissent. Cliquons sur l’ampoule, une suggestion apparaît signalant de déclarer la classe java.io.Serializable. Pas de problème, on applique cette suggestion. Puis, rien plus d’erreur. A la différence de l’interface Comparable, Serializable ne nécessite pas d’implémenter de classe abstraites, l’interface sert uniquement de marqueur pour signaler que la classe est bien sérialisable (ce qui est le cas car tous les champs sont eux-mêmes sérialisables). Java s’occupe de tout.
Toutefois, une petite modification du code est nécessaire: en effet, une classe sérialisable doit posséder un constructeur vide, ce qui n’est pas le cas de la notre. De plus, il est fortement recommandé de rajouter un champ private static final long serialVersionUID dans notre classe pour lui attribuer un numéro de version unique. Complétons donc notre méthode avec le code suivant :
private static final long serialVersionUID = 1L;
/** Creates a new instance of Person */
public Person() {
}
Plus d’information sur la sérialisation:
• La FAQ de développez.com
• Article La sérialisation binaire en Java par Yann D’ISANTO sur developpez.com
• La javadoc
Les classes terminées
Notre fichier Person.java est désormais terminé. Nous pouvons le retrouver ici. De la même manière, il faut
également créer le fichier Trip.java, que nous trouverons ici. Voici ci dessous le descripteur de la classe ainsi que ses attributs.
Déjardin (Creabeans)
// skipping package and import…
public class Trip implements Comparable, Serializable { private int tripId;
private int personId;
private Date departureDate;
private String fromCity;
private String toCity;
private String tripType;
private String tripDesc;
// Skipping contructors, getters, setters, compareTo()…
Lire et gérer les données en session
Il est maintenant nécessaire de s’occuper de la gestion des fichiers par l’application. Un peu à la manière des datasources « databases » de Creator, nous allons travailler depuis le bean SessionBean1.java créé avec le projet. Il faut d’abord indiquer à l’application où se trouvent les fichiers de données. Pour cela nous allons utiliser les paramètres de contexte de l’application web. En effet, une application JSF reste avant tout une application JSP/Servlet, elle dispose d’un fichier web.xml qu’il est possible d’exploiter.
Dans le projet, cliquer sur « Configurations files » et ouvrir le web.xml. Dans Général | context parameters, ajoutons des paramètres indiquants que les fichiers sont dans le répertoire racine de l’application (une fois déployée, ce qui correspond au répertoire web du projet).
Indiquons à notre application où trouver ses données
Il serait inutile de relire et reparser les fichiers à chaque requête de l’utilisateur. Nous allons donc créer deux méthodes, une pour chaque fichier, pour trouver dans le contexte de l’application le chemin des fichiers, lire leur date de dernière modification, la comparer avec une valeur stockée en session, et le cas échéant ordonner une mise à jour.
/**
* last time the person file was modified
*/
private Date personDate = new Date(0);
/**
* last time the trip file was modified
*/
private Date tripDate = new Date(0);
public void checkPerson() {
// Get the parameter person set in web.xml
// This parameter value is relative to the root of the application String personPath = getExternalContext().getInitParameter(« person »);
// compare last modified time of the file to the internal last value if (personPath != null) {
// getting servlet context
ServletContext context = (ServletContext) getExternalContext().getContext();
// getting the full path of the file, depending of the place from
// where you run this example
personPath = context.getRealPath(personPath);
File personFile = new File(personPath);
if ((personFile.exists()) && (personFile.lastModified() > personDate.getTime())) { managePerson(personFile);
}
}
}
// idem for checkTrip()
Toujours dans Session1.java, nous allons rajouter la logique pour parser et gérer les fichiers. Chaque ligne des fichiers person.txt et trip.txt sera bien sûr mémorisée respectivement dans un objet Person et Trip, mais comment organiser ces données? Il nous faut une collection.
Souvenons-nous de Person.java. Nous avions pris soin d’implémenter l’interface Comparable dans cette classe, c’est le moment de l’utiliser. Nous choisissons la collection TreeSet. L’avantage de cette collection est que chaque nouvel élément qui y est rajouté est trié en respectant l’ordre naturel des éléments, c’est à dire l’ordre obtenu par le biais de l’interface Comparable. De cette manière, même si les personnes ne sont pas rangées dans l’ordre dans notre fichier person.txt, elles seront dans l’ordre alphabétique en mémoire et dans toutes les fonctions qui y feront appel.
C’est décidé, les personnes seront stockées dans un TreeSet. Mais en ce qui concerne les Trips? Réfléchissons à l’usage que nous souhaitons en faire… Nous voulons afficher les voyages réalisés par une personne. Il serait donc intéressant de pouvoir dans un premier temps regrouper tous les voyages réalisés par une personne. Comme pour Person.java, Trip.java implémente Comparable, utilisons à nouveau un TreeSet pour stocker les voyages (triés par date de départ cette fois). Et pour regrouper tous ces TreeSet, une simple Hashtable, avec l’id des personnes comme clef, fera l’affaire.
Dans cet exemple, la gestion des exceptions est rudimentaire: toute ligne de données qui est mal formée sera simplement ignorée.
/**
* handle the contens of the person file
*/
private TreeSet personTreeSet = new TreeSet();
/**
* handle the content of the trip file, keyed by personId
*/
private Hashtable tripHashtable = new Hashtable();
/**
* parses the person file and store results in personTreeSet
* @param personFile the File where to find data
*/
private void managePerson(File personFile) {
String strLine = null;
try {
// getting file to work with
FileInputStream in = new FileInputStream(personFile);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// for each line of the file
while ((strLine = reader.readLine()) != null) {
int personId = 0;
String name = null;
String function = null;
boolean frequentFlier = false;
// parsing each line — take care, bad exceptions handling String[] elements = strLine.split(« \\t »);
try {
personId = Integer.parseInt(elements[0]);
name = elements[1];
function = elements[2];
frequentFlier = (« 1 ».equals(elements[3])) ? true : false;
} catch (NumberFormatException nfe) {
// Exception handling here continue;
}
// creating new Person object and adding it to the treeset
// I used treeset and the interface comparable in the Person
// class to have a sorted collection
Person person = new Person(personId, name, function, frequentFlier); personTreeSet.add(person);
} // end while
this.personDate = new Date(personFile.lastModified());
} catch (FileNotFoundException ex) {
log(« Error finding person.txt file: » + ex.toString()); error(« Error finding person.txt file: » + ex.toString()); ex.printStackTrace();
} catch (IOException ex) {
log(« Error parsing person.txt file: » + ex.toString()); error(« Error parsing person.txt file: » + ex.toString()); ex.printStackTrace();
}
}
/**
* parses the trip file and store results in tripHashtable
* @param tripFile the File where to find data
*/
private void manageTrip(File tripFile) {
String strLine = null;
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, Locale.FRANCE);
try {
// getting file to work with
FileInputStream in = new FileInputStream(tripFile);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// for each line of the file
while ((strLine = reader.readLine()) != null) {
int tripId = 0;
int personId = 0;
Date departureDate = null;
String fromCity = null;
String toCity = null;
String tripType = null;
String tripDesc = null;
// parsing each line — take care, bad exceptions handling String[] elements = strLine.split(« \\t »);
try {
tripId = Integer.parseInt(elements[0]);
personId = Integer.parseInt(elements[1]);
departureDate = df.parse(elements[2]);
fromCity = elements[3];
toCity = elements[4];
tripType = elements[5];
tripDesc = elements[6];
} catch (NumberFormatException nfe) {
// Exception handling here continue;
} catch (ParseException ex) {
// Exception handling here continue;
}
// creating new Trip object
Trip trip = new Trip(tripId, personId, departureDate, fromCity, toCity, tripType,
tripDesc);
// storing Trip object into a hashtable of treeset
TreeSet trips4user = (TreeSet) tripHashtable.get(new Integer(personId));
if (trips4user == null) {
trips4user = new TreeSet();
}
trips4user.add(trip);
tripHashtable.put(new Integer(personId), trips4user);
} // end while
this.tripDate = new Date(tripFile.lastModified());
} catch (FileNotFoundException ex) {
log(« Error finding trip.txt file: » + ex.toString()); error(« Error finding trip.txt file: » + ex.toString()); ex.printStackTrace();
} catch (IOException ex) {
log(« Error parsing trip.txt file: » + ex.toString()); error(« Error parsing trip.txt file: » + ex.toString()); ex.printStackTrace();
}
}
Maintenant, il suffit de rajouter l’appel à ces fonctions lors de l’initialisation du bean de session, dans Init():
// Perform application initialization that must complete
// *after* managed components are initialized
// TODO – add your own initialization code here checkPerson();
checkTrip();
Enfin, comme ces données devront être accessibles depuis la Page1.java, nous rajoutons un getter sur personTreeSet et TripHashtable.
public Hashtable getTripHashtable() {
return tripHashtable;
}
public TreeSet getPersonTreeSet() {
return personTreeSet;
}
Construire un dropdown
Concentrons nous désormais sur quelque chose qui concerne davantage le VWP: nous devons rajouter un dropdown sur notre page d’accueil. Avec une base de données, pas de problème: il suffit de glisser-déposer une table sur un dropdown en mode design. Ici, c’est un peu plus compliqué, mais ce n’est pas non plus irréalisable!
Plaçons d’abord un dropdown sur notre page, appelons-le dropdownPerson, ajoutons lui un label « Choisissez un nom ».
Création et initialisation de notre dropdown.
Passons en mode java et rajoutons une méthode getPerson4Dropdown():
/**
* Return a list of options describing a dropdown elements. The value of the option is an Integer, while the text
* displayed shall be the name of the person.
*/
public List getPerson4Dropdown() {
List returnValue = new ArrayList();
for (Iterator it = this.getSessionBean1().getPersonTreeSet().iterator(); it.hasNext();) { Person elem = (Person) it.next();
Integer id = new Integer(elem.getId());
String name = elem.getName();
// creating option: value = id, label = name Option option = new Option(id, name);
// adding the new option to the list returnValue.add(option);
}
return returnValue;
}
Désormais, il reste à lier cette méthode au dropdown. En mode design, sélectionnons le dropdown, puis cliquons sur les … de l’attribut items. Nous pouvons désormais lier (en anglais « to bind ») les éléments de notre dropdown
à la propriété person4Dropdown de la classe Page1. Notez que le VWP avait pris le soin de créer une propriété dropDownPersonDefaultOptions. Dès que nous affecterons le dropdown à notre propre méthode, cette propriété « par défaut » sera supprimée automatiquement.
I – Introduction
II – Installation
III – Gérer les fichiers
III.A – Attributs, getters et setters
III.B – Rendre notre classe Comparable
III.C – Rendre notre classe Serializable
III.D – Les classes terminées
III.E – Lire et gérer les données en session
IV – Construire un dropdown
V – Remplir une table
V.A – Créer un dataProvider pour nos données
V.B – Récupérer les données de voyages relatives à une personne.
VI – Rajouter une seconde page et de la navigation
VII – Conclusion
VIII – Sources et Crédits