Université de Sherbrooke Département d informatique IFT287 : Exploitation de bases de données Examen final Professeur : Marc Frappier Mercredi 14 avril 2004, 9h00 à 12h00 Notes importantes : Documentation permise. La correction est, entre autres, basée sur le fait que chacune de vos réponses soit claire, précise, concise et complète. Pondération : Question Point Question Point 1 35 3 15 2 15 4 35 Total 100 1. (35 pt) On désire développer un petit système de gestion des examens finaux à l université en utilisant la base de donnés ObjectStore. Ce système doit permettre de traiter les informations suivantes. département : son numéro, son nom, le nom de son directeur et ses cours. cours : son titre, son sigle et ses examens. examen : le cours, la session (ex: H04 ), la date (YYYY-MM-DD), la période ( AM, PM ou soir ) et ses locaux (ex: D4-3021 ). On a les contraintes suivantes: un cours peut avoir plusieurs examens, mais il y a un seul examen final par session pour un cours. (a) (10 pt) Afin de stocker ces informations dans une BD ObjectStore, donnez la définition des classes suivantes : gestionnaires de collections; tuples (ie, classe représentant un objet contenu dans une collection). Pour chaque classe, donnez seulement les informations suivantes : nom de la classe; déclaration des attributs. Ne donnez pas les méthodes et les énoncés import. Par souci de simplicité, ne créez pas de gestionnaire de collection pour les locaux et les sessions. public class Departement private Connexion cx; private Map alldepartements; public class Examen 1
private Connexion cx; private Map allexamens; public class Cours private Connexion cx; private Map allcours; public TupleCours getcours(string sigle) return (TupleCours) allcours.get(new Integer(sigle)); public class TupleDepartement private int numero; private String nom; private String directeur; private Set cours; public class TupleCours private String sigle; private String titre; private Set examens; private TupleDepartement departement; public class TupleExamen private TupleCours cours; private String session; private String datedebut; private String periode; private Set locaux; (b) (25 pt) Donnezlecodedelaméthode ajouterexamen, qui implémente la transaction ajouter un examen. Voici la signature de cette méthode. public void ajouterexamen( String sigle, String session, String datedebut, String periode, String local) Cette méthode ne prend qu un seul local. Si un examen utilise plusieurs locaux, les autres locaux seront ajoutés séparément avec une autre transaction. Voici certaines contraintes qui doivent être respectées par la transaction : le sigle doit être un sigle valide; le local ne doit pas être occupé par un autre examen. On peut supposer que la méthode est appelée avec des données du format approprié. Donnez seulement 2
le nom de la classe où apparaît cette méthode; le code source de cette méthode; les attributs utilisés par cette méthode; le code source des méthodes appelées par cette méthode. public class GestionExamen private Cours cours; private Examen examen; public void ajouterexamen( String sigle, String session, String datedebut, String periode, String local) throws BiblioException, Exception Transaction tr = Transaction.begin(ObjectStore.UPDATE); try /* Verifie si l examen existe deja */ if (examen.existe(sigle, session)) throw new BiblioException("Examen existe deja"); TupleCours c = cours.getcours(sigle); if (c == null) throw new BiblioException("Cours inexistant"); if (!examen.libre(datedebut,periode,local)) throw new BiblioException("Local non-disponible."); /* Ajout de l examen */ TupleExamen e = new TupleExamen(c, session, datedebut, periode, local); examen.ajouter(e); c.ajouterexamen(e); tr.commit(objectstore.retain_hollow); catch (Exception e) tr.abort(objectstore.retain_hollow); throw e; // class GestionExamen public class Examen... public boolean existe(string sigle, String session) return allexamens.get(creercleexamen(sigle, session))!= null; public List creercleexamen(string sigle, String session) OSVectorList cle = new OSVectorList(); 3
cle.add(sigle); cle.add(session); return cle; public boolean libre(string datedebut, String periode, String local) Collection c = allexamens.values(); Iterator it = c.iterator(); boolean estlibre = true; while (it.hasnext() && estlibre) TupleExamen t = (TupleExamen) it.next(); if (t.getdatedebut().equals(datedebut) && t.getperiode().equals(periode) && t.getlocaux().contains(local)) estlibre = false; return estlibre; public void ajouter(tupleexamen e) allexamens.put(creercleexamen(e.getcours().getsigle(),e.getsession()),e); public class TupleCours... public String getsigle() return sigle; public void ajouterexamen(tupleexamen e) examens.add(e); public class TupleExamen... public TupleCours getcours() return cours; public String getsession() return session; public String getdatedebut() return datedebut; public String getperiode() return periode; public Set getlocaux() return locaux; public TupleExamen(TupleCours c, String session, String datedebut, String periode, String local) this.cours = c; this.session = session; this.datedebut = datedebut; this.periode = periode; this.locaux = new OSHashSet(); this.locaux.add(local); 2. (15 pt) Complétez le code de la méthode extraire qui retourne dans une liste toutes les adresses de courriel d une personne dans un bottin. Ce bottin est stocké dans un fichier XML dont la structure est définie par la DTD suivante. <?xml version= 1.0 encoding= ISO-8859-1? 4
<!-- bottin -- <!ELEMENT bottin (personne*) <!ELEMENT personne (courriel*, telephone*) <!ATTLIST personne nom CDATA #REQUIRED prenom CDATA #REQUIRED <!ELEMENT courriel EMPTY <!ATTLIST courriel description CDATA #REQUIRED adresse CDATA #REQUIRED <!ELEMENT telephone EMPTY <!ATTLIST telephone description CDATA #REQUIRED numero CDATA #REQUIRED Voici le code partiel de la méthode qui est àcompléter. public static List extraire( String nomfichierxml, String prenom, String nom) throws ParserConfigurationException, SAXException, IOException DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setvalidating(true); factory.setexpandentityreferences(true); factory. setignoringelementcontentwhitespace(true); factory.setignoringcomments(true); DocumentBuilder builder = factory.newdocumentbuilder(); Document document = builder.parse(new File(nomFichierXML));... // positionne sur bottin Node n = document.getfirstchild().getnextsibling(); // positionne sur premiere personne n = n.getfirstchild(); boolean trouve = false; List l = new LinkedList(); while (n!= null &&!trouve) if (n.getattributes().getnameditem("nom").getnodevalue().equals(nom) && n.getattributes().getnameditem("prenom").getnodevalue().equals(prenom)) trouve = true; // positionne sur premier courriel ou telephone 5
n = n.getfirstchild(); while (n!= null && n.getnodename().equals("courriel")) l.add(n.getattributes().getnameditem("adresse").getnodevalue()); n = n.getnextsibling(); else n = n.getnextsibling(); return l; 3. (15 pt) Donnez une DTD pour un fichier XML devant contenir un livre avec tout son texte. Un livre auntitre, unnuméro d édition (optionnel) et des auteurs (au moins un auteur). Un auteur est défini par un nom et un prénom. Un livre se décompose en une préface et une suite non-vide de chapitres. Une préface ne contient que du texte. Un chapitre a un titre et un numéro. Un chapitre se décompose en sections. Une section a un titre et un numéro. Elle se décompose aussi en sections, sans limite sur le niveau de décomposition. Par souci de simplicité, on modélise le contenu textuel d une section (ie, les paragraphes) comme une simple suite de caractères. <?xml version= 1.0 encoding= ISO-8859-1? <!-- livre -- <!ELEMENT livre (auteur+,preface,chapitre+) <!ATTLIST livre titre CDATA #REQUIRED edition CDATA #IMPLIED <!ELEMENT auteur EMPTY <!ATTLIST auteur nom CDATA #REQUIRED prenom CDATA #REQUIRED <!ELEMENT preface (#PCDATA) <!ELEMENT chapitre (#PCDATA section)* <!ATTLIST chapitre numero CDATA #REQUIRED titre CDATA #REQUIRED <!ELEMENT section (#PCDATA section)* <!ATTLIST section numero CDATA #REQUIRED titre CDATA #REQUIRED 4. (35 pt) On veut définir un babillard web avec servlet-jsp. Un babillard permet à une personne de laisser un message à une autre personne. L application comporte deux pages. La première, login.jsp, permet à une personne d entrer son nom pour s enregistrer. Le serveur ajoute cette personne à la liste des personnes enregistrées dans l application, si elle n y est pas déjà. Ensuite, le serveur affiche une nouvelle page, listenom.jsp, qui donne la liste des personnes enregistrées, avec le nombre total de messages reçus depuis le démarrage de l application, le dernier message reçu et la provenance. Lors de l arrêt du serveur, les données du babillard sont perdues. La figure 1 illustre la page login.jsp et la figure 2 illustre la page listenom.jsp. Voici le code source de la méthode doget du servlet qui est appelé par le formulaire de la page listenom.jsp. 6
ServletContext c = getservletcontext(); List listenoms = (List) c.getattribute("listenoms"); String nomselectionne = request.getparameter("nomselectionne"); if (nomselectionne!= null) synchronized(listenoms) int nbmessage = ((Integer) c.getattribute (nomselectionne+"nbmessage")).intvalue(); c.setattribute(nomselectionne+"nbmsg", new Integer(nbMessage+1)); c.setattribute(nomselectionne+"derniermsg", request.getparameter("message")); c.setattribute(nomselectionne+"source", request.getsession().getattribute("nom")); RequestDispatcher dispatcher = request.getrequestdispatcher("/listenom.jsp"); dispatcher.forward(request, response); En considérant ce code source, répondez aux questions suivantes. Figure 1: La page login.jsp Figure 2: La page listenom.jsp Indice : le servlet qui traite la requête provenant de la page login.jsp utilise le contexte de l application pour stocker les personnes enregistrées. On accède à un contexte avec la méthode getservletcontext 7
(dans un jsp aussi). Un contexte est partagé entre tous les utilisateurs du système; il a des attributs. L objet listenoms contient les personnes enregistrées. Les informations d une personne sont stockée dans un attribut "<nomxxxx" où XXX est une information comme le nb de message reçus, le dernier message reçu, etc. (a) (5 pt) Donnezlecodesourcedelaméthode contextinitialized d un listener de contexte pour cette application. public void contextinitialized(servletcontextevent sce) sce.getservletcontext().setattribute("listenoms", new LinkedList()); (b) (10 pt) Donnezlecodesourcedelaméthode doget du servlet appelé par la page login.jsp, en sachant que le formulaire HTML de cette page est codé comme suit: <FORM ACTION="Login" METHOD="GET" Nom : <INPUT TYPE="TEXT" NAME="nom""<BR<BR <INPUT TYPE="SUBMIT" VALUE="Soumettre" </FORM ServletContext c = getservletcontext(); List listenoms = (List) c.getattribute("listenoms"); String nom = request.getparameter("nom"); synchronized(listenoms) if (!listenoms.contains(nom)) listenoms.add(nom); c.setattribute(nom+"nbmessage", new Integer(0)); c.setattribute(nom+"derniermessage", ""); c.setattribute(nom+"source", ""); request.getsession().setattribute("nom",nom); RequestDispatcher dispatcher = request.getrequestdispatcher("/web-inf/listenom.jsp"); dispatcher.forward(request, response); (c) (20 pt) Donnez le code jsp qui permet d afficher les lignes du tableau de la page listenom.jsp. Ne donnez pas le reste de la page (entête du tableau, reste du formulaire, etc) <% ServletContext c = getservletcontext(); List listenoms = (List) c.getattribute("listenoms"); ListIterator it = listenoms.listiterator(); while (it.hasnext()) String nom = (String) it.next(); % <tr <td<input TYPE="RADIO" NAME="nomSelectionne" VALUE="<%= nom %"<br</td 8
<td<%= nom %<br</td <td<%= c.getattribute(nom+"nbmessage") %<br</td <td<%= c.getattribute(nom+"derniermessage") %<br</td <td<%= c.getattribute(nom+"source") %<br</td </tr <% % </tbody 9