Examen de Java Correction FIUPSO 3/DESS – Année 2001/2002 Hugues M OUNIER 1 février 2002 Modalités – Durée : 4 heures – Tous les documents sont autorisés. – Les sources java répondant à chaque exercice, et seulement les sources seront obligatoirement copiés dans le répertoire examenjava (tout en minuscules) crée à la racine de votre compte. Solution de l’ex. 1 Calculatrice Voici une solution possible : // Gestion simpliste de banque utilisant des sockets import java.util.*; import java.io.*; public class Calculette { static double resultat = 0; /* methodes */ static int somme(int a, int b) { return(a+b); } static int soustraction(int a, int b) { return(a-b); } static int multiplication(int a, int b) { return(a*b); } static double division(int a, int b) { return(a/b); } public static void main(String args[]) { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); // Boucle infinie for(;;) { try { // Afficher une invite a l’utilisateur System.out.println("1 Somme de deux entiers\n" + "2 Soustraction de deux entiers\n" + "3 Multiplication de deux entiers\n" + "4 Division de deux entiers\n" + "5 Sortie du programme\n\n" + "Veuillez entrer votre choix :\n"); // Lire une ligne entree par l’utilisateur String line = in.readLine(); 1 Examen Java FIUPSO 3/DESS 2001/2002 // Si l’on rencontre une fin de fichier, ou si l’utilisateur // entre "quit", on sort if ((line == null) || line.equals("5")) break; // On essaie de formatter l’entree de l’utilisateur, int choix = Integer.parseInt(line); System.out.print("Veuillez entrer le 1er entier : "); line = in.readLine(); int x = Integer.parseInt(line); System.out.print("Veuillez entrer le 2eme entier : "); line = in.readLine(); int y = Integer.parseInt(line); switch(choix) { case 1 : resultat = somme(x, y); // Somme break; case 2 : resultat = soustraction(x, y); // Soustraction break; case 3 : resultat = multiplication(x, y); // Multiplication break; case 4 : resultat = division(x, y); // Division break; } System.out.println("Resultat : " + resultat); } // Affichage d’un message d’erreur en cas de probleme catch(Exception e) { System.out.println("Entree Invalide"); } } } } Gestion de banque en client/serveur par sockets L’objet des exercices qui suivent est de développer une application de gestion bancaire simple en réseau, utilisant les sockets. Ce dernier mécanisme n’offrant que du transfert de flux, donc d’assez bas niveau, il est nécessaire de concevoir un protocole permettant de différentier les différentes opérations bancaires tout en acheminant les données pertinentes. Ce type de conception ne serait pas nécessaire si l’on prenait des invocations de méthodes distantes (RMI), bien que l’on perde évidemment en souplesse de conception. Pour réaliser l’objectif visé, on se fondera sur l’exercice de gestion bancaire en local. On représentera un paquet par une classe décrite ci-dessous. Solution de l’ex. 2 Paquets à échanger Une solution possible est la suivante : // Gestion simpliste de banque utilisant des sockets import java.util.*; import java.io.*; /** * Classe pour stocker les donnees d’un compte **/ class Account { String password; // int balance; // Vector transactions = new Vector(); // Hugues M OUNIER bancaire mot de passe solde du compte historique des transactions 2 Examen Java FIUPSO 3/DESS 2001/2002 Account(String password) { this.password = password; this.balance = 0; transactions.addElement("Compte ouvert le " + new Date()); } } /** * Classe representant un paquet du protocoloe bancaire **/ public class BankSocketPacket { String name; // nom du compte int operation; // operation bancaire String password; // mot de passe int amount; // solde /* Operations public static public static public static public static public static public static /* Erreurs */ public static public static public static public static public static */ final final final final final final int int int int int int OPEN CLOSE DEPOSIT WITHDRAW BALANCE QUIT = = = = = = 1; 2; 3; 4; 5; 6; final final final final final int int int int int TRANSOK EXISTANT NONEXISTANT INVPASSWORD NEGBALANCE = = = = = 0; -1; -2; -3; -4; // // // // // // // // // // // Ouverture de compte Fermeture de compte Depot sur un compte Retrait sur un compte Solde d’un compte Sortie du programme Transaction bien passee Overture de compte deja existant Operation sur compte inexistant Mot de passe invalide Solde insuffisant pour retrait BankSocketPacket(String theName, int theOperation, String thePassword, int theAmount) { name = theName; operation = theOperation; password = thePassword; amount = theAmount; } public String foldPacket() { String foldedPacket; StringBuffer tobeFolded = new StringBuffer(); tobeFolded.append(this.name).append(":"); Integer opCode = new Integer(this.operation); tobeFolded.append(opCode.toString()); if (password != null) { tobeFolded.append(":").append(this.password); } tobeFolded.append(":").append(this.amount); tobeFolded.append("#"); foldedPacket = new String(tobeFolded); return(foldedPacket); } public void unfoldPacket(String foldedPacket) { Hugues M OUNIER 3 Examen Java FIUPSO 3/DESS String 2001/2002 unfoldedTab[] = new String[4]; /* Le deballage est d’abord effectue dans un tableau de String */ int current = foldedPacket.indexOf(’:’, 0); int next = foldedPacket.indexOf(’:’, current+1); int last = current; int i = 1; unfoldedTab[0] = foldedPacket.substring(0, current); while (next != -1) { unfoldedTab[i++] = foldedPacket.substring(current+1, next); last = current; current = foldedPacket.indexOf(’:’, last+1); next = foldedPacket.indexOf(’:’, current+1); } unfoldedTab[i] = foldedPacket.substring(current+1, foldedPacket.length()-1); /* On remplit les champs du paquet deballe */ try { name = new String(unfoldedTab[0]); operation = Integer.parseInt(unfoldedTab[1]); password = new String(unfoldedTab[2]); amount = Integer.parseInt(unfoldedTab[3]); } catch (NumberFormatException e) { System.err.println("Erreur en unfoldPacket()--parseInt() : "); System.err.println(e); System.exit(1); } } /* Test */ /* public static void main(String[] args) { Account cpte = new Account("issos"); BankSocketPacket packet = new BankSocketPacket("Alexandre", cpte, OPEN); String aAfficher = packet.foldPacket(); System.out.println(aAfficher); String[] deballe = BankSocketPacket.unfoldPacket(aAfficher); for(int i = 0; i < deballe.length; i++) System.out.println(deballe[i]); } */ } Solution de l’ex. 3 Élaboration du serveur Une solution possible est la suivante : // Serveur Bancaire simpliste en sockets import java.util.*; import java.io.*; import java.net.*; /** * Cette classe implante les methodes distantes definies par l’interface * RemoteBank. Son plus gros defaut est que toutes les informations sur les Hugues M OUNIER 4 Examen Java FIUPSO 3/DESS 2001/2002 * comptes sont perdues lorsque le serveur s’arrete. **/ public class BankServerSocket { public final static int DEFAULT_PORT = 6789; Hashtable accounts = new Hashtable(); static int transCode; /** * Ouvre un compte avec le nom et le mot de passe * Cette methode est synchronisee de facon qu’une * ne modifie a la fois la table des comptes. **/ public synchronized void openAccount(String name, // Verifier s’il exsite deja un compte ayant ce if (accounts.get(name) != null) { transCode = BankSocketPacket.EXISTANT; return; } // S’il n’existe pas, le creer Account acct = new Account(password); // Et l’enregsitrer accounts.put(name, acct); transCode = BankSocketPacket.TRANSOK; } specifie seule thread String password) { nom /** * Cette methode utilitaire n’est pas une methode accessible de maniere * distante. Etant donnes un nom et un mot de passe, verifie s’il existe * un compte correspondant. Si oui, renvoie l’objet Account. Sinon, * leve une exception. **/ public Account verify(String name, String password) { synchronized(accounts) { Account acct = (Account)accounts.get(name); if (acct == null) { transCode = BankSocketPacket.NONEXISTANT; return null; } if (!password.equals(acct.password)) { transCode = BankSocketPacket.INVPASSWORD; return null; } transCode = BankSocketPacket.TRANSOK; return acct; } } /** * Ferme le compte dont on donne le nom. Methode synchronisee **/ public synchronized FunnyMoney closeAccount(String name, String password) { Account acct; acct = verify(name, password); if (acct != null) { accounts.remove(name); synchronized (acct) { int balance = acct.balance; Hugues M OUNIER 5 Examen Java FIUPSO 3/DESS 2001/2002 acct.balance = 0; return new FunnyMoney(balance); } } else return new FunnyMoney(-1); } /** Deposer le montant specifie su le compte dont on donne le nom */ public void deposit(String name, String password, FunnyMoney money) { Account acct = verify(name, password); if (acct != null) synchronized(acct) { acct.balance += money.amount; acct.transactions.addElement(money.amount + " pieces deposees le " + new Date()); } } /** Effectue un retarit d’un montant specifie */ public FunnyMoney withdraw(String name, String password, int amount) { Account acct = verify(name, password); if (acct != null) synchronized(acct) { if (acct.balance < amount) { transCode = BankSocketPacket.NEGBALANCE; return new FunnyMoney(0); } acct.balance -= amount; acct.transactions.addElement("Retrait de " + amount + " le "+new Date()); return new FunnyMoney(amount); } else return new FunnyMoney(-1); } /** Renvoie le solde du compte dont on donne le nom */ public int getBalance(String name, String password) { Account acct = verify(name, password); if (acct != null) synchronized(acct) { return acct.balance; } else return -1; } /** * Renvoie un vecteur de String contenant l’historique pour * le compte dont on donne le nom **/ public Vector getTransactionHistory(String name, String password) { Account acct = verify(name, password); synchronized(acct) { return acct.transactions; } } void sendPacket(String name, int operation, String password, int amount, BankSocketPacket toSend = new BankSocketPacket(name, operation, password, amount); String lineTosend = toSend.foldPacket(); Hugues M OUNIER 6 Examen Java FIUPSO 3/DESS 2001/2002 out.println(lineTosend); } /** * Serveur bancaire par sockets **/ public static void main (String[] args) throws IOException { int port; // no de port du service String lineRecieved = null; ServerSocket server; Socket client; DataInputStream in; PrintStream out; String name; int operation; String password; int amount; FunnyMoney money; // Tests d’arguments switch(args.length) { case 0 : port = DEFAULT_PORT; break; case 1 : try { port = Integer.parseInt (args[0]); } catch (NumberFormatException e) { port = DEFAULT_PORT; } break; default : throw new IllegalArgumentException ("Syntaxe : java BankServerSocket [<port>]"); } // Creation de socket serveur System.out.println ("Demarrage sur le port " + port); server = new ServerSocket (port); System.out.println ("En ecoute ..."); BankServerSocket bank = new BankServerSocket(); // Boucle generale et service while (true) { // Acceptation de connexion client = server.accept(); System.out.println ("Connexion a " + client.getInetAddress() + " acceptee"); System.out.flush(); // Capture des flux de reception et d’envoi in = new DataInputStream(client.getInputStream()); out = new PrintStream(client.getOutputStream()); //----------------------// TRAITEMENT DU SERVICE //----------------------while (true) { // lire une ligne lineRecieved = in.readLine(); if ((lineRecieved == null)) break; Hugues M OUNIER 7 Examen Java FIUPSO 3/DESS 2001/2002 BankSocketPacket toUnfold = new BankSocketPacket(null, 0, null, 0); toUnfold.unfoldPacket(lineRecieved); name = toUnfold.name; operation = toUnfold.operation; password = toUnfold.password; amount = toUnfold.amount; switch(operation) { case BankSocketPacket.OPEN : bank.openAccount(name, password); bank.sendPacket("noname", transCode, "nopass", 0, out); break; case BankSocketPacket.CLOSE : money = bank.closeAccount(name, password); bank.sendPacket(name, transCode, password, money.amount break; case BankSocketPacket.DEPOSIT : money = new FunnyMoney(amount); bank.deposit(name, password, money); bank.sendPacket(name, transCode, password, money.amount break; case BankSocketPacket.WITHDRAW : money = bank.withdraw(name, password, amount); bank.sendPacket(name, transCode, password, money.amount break; case BankSocketPacket.BALANCE : int amt = bank.getBalance(name, password); bank.sendPacket(name, transCode, password, amt, out); break; case BankSocketPacket.QUIT : System.out.println ("Fermeture des socket client & serveur"); client.close (); server.close (); System.exit(0); } } if (lineRecieved == null) { // Sortie du service : le client a termine System.out.println ("Fermeture de socket client"); client.close (); } }// while(true) }/* main() */ }// BankServerSocket /* try { } catch (Exception e) { System.err.println(e); System.err.println("Utilisation : java BankServerSocket"); System.exit(1); // On sort } Hugues M OUNIER 8 Examen Java FIUPSO 3/DESS 2001/2002 */ Solution de l’ex. 4 Élaboration du client Une solution possible est la suivante : // Gestion simpliste de banque en sockets : client // Historique a terminer ?? import java.util.*; import java.io.*; import java.net.*; /** * Classes conteneur : * - FunnyMoney la monnaie utilisee **/ /** * Cette classe simple represente un montant monetaire. N’est qu’un emballage * d’un entier. **/ class FunnyMoney { public int amount; public FunnyMoney(int amount) { this.amount = amount; } } /** * Client simple interagissant avec un serveur **/ public class BankSocket { // Variables de classe constantes : port et machine par defaut public static final int DEFAULT_PORT = 6789; public static final String DEFAULT_HOST = "localhost"; // Methode utilitaire de saisie public static String getName(BufferedReader in) { String name = null; try { System.out.println("Veuillez entrer le nom du compte : "); name = new String(in.readLine().toLowerCase()); } catch (IOException e) { System.err.println("Erreur en readLine()"); } return name; } // Methode utilitaire de saisie public static String getPassword(BufferedReader in) { String password = null; try { System.out.println("Veuillez entrer le mot de passe : "); Hugues M OUNIER 9 Examen Java FIUPSO 3/DESS 2001/2002 password = new String(in.readLine().toLowerCase()); } catch (IOException e) { System.err.println("Erreur en readLine()"); } return password; } // Methode utilitaire de gestion d’erreur static void treatError(int errorNb) { switch(errorNb) { case BankSocketPacket.EXISTANT : System.out.println("Le compte existe deja"); break; case BankSocketPacket.NONEXISTANT : System.out.println("Compte inexistant"); break; case BankSocketPacket.INVPASSWORD : System.out.println("Mot de passe invalide"); break; case BankSocketPacket.NEGBALANCE : System.out.println("Solde insuffisant"); break; } } // Methode utilitaire d’envoi/reception de paquet static BankSocketPacket sendReceive(BankSocketPacket toSend, DataInputStream sin, PrintStream sout, Stri BankSocketPacket unfolded = new BankSocketPacket(null, 0, null, 0); String lineTosend = toSend.foldPacket(); sout.println(lineTosend); try { String lineRecieved = sin.readLine(); if (lineRecieved != null) { unfolded.unfoldPacket(lineRecieved); if (unfolded.operation == BankSocketPacket.TRANSOK) System.out.println(message); else treatError(unfolded.operation); } else { System.out.println("Serveur arrete ; sortie"); System.exit(1); } } catch (IOException e) { System.out.println("Erreur en reception de paquet : " + e.toString()); System.exit(1); } return(unfolded); } /** * Programme client **/ public static void main(String[] args) { Hugues M OUNIER 10 Examen Java // FIUPSO 3/DESS 2001/2002 try { String host = null; // Machine du serveur int port; // No de port du serveur (no de service) Socket s = null; // Reference a la socket client DataInputStream sin = null; // Flux en provenance du serveur PrintStream sout = null; // Flux a destination du serveur BufferedReader stdin = null;// Flux associe au clavier BankSocket b = new BankSocket(); // Tests d’arguments switch(args.length) { case 0 : host = new String(DEFAULT_HOST); port = DEFAULT_PORT; break; case 1 : host = new String(args[0]); port = DEFAULT_PORT; break; case 2 : host = new String(args[0]); try { port = Integer.parseInt (args[1]); } catch (NumberFormatException e) { System.err.println("Numero de port invalide. " + DEFAULT_PORT + " Utilise."); port = DEFAULT_PORT; } break; default : System.out.println("Nombre d’arguments illegal"); throw new IllegalArgumentException(); }// switch() s = new Socket(host, port); // Creer les flux pour lire et ecrire des lignes de texte // de et vers cette socket. sin = new DataInputStream(s.getInputStream()); sout = new PrintStream(s.getOutputStream()); System.out.println("Connecte a " + s.getInetAddress() + ":"+ s.getPort()); // Creer un flux pour lire des lignes de l’entree standard // (par defaut le clavier) stdin = new BufferedReader(new InputStreamReader(System.in)); String cmd = "nothing"; while(true) { System.out.println("\nVeuillez entrer une commande : \n" + "open (ouverture de compte)\n" + "close (cloture de compte)\n" + "deposit (depot sur le compte)\n" + "withdraw (retrait sur le compte)\n" + "balance (solde du compte)\n" + "quit (sortie du programme)\n"); // Convertir la commande utilisateur en minuscules cmd = stdin.readLine().toLowerCase(); // Differentes actions possibles if (cmd.equals("open")) { // ouverture de compte String name = getName(stdin); String password = getPassword(stdin); BankSocketPacket packet = Hugues M OUNIER 11 Examen Java FIUPSO 3/DESS 2001/2002 new BankSocketPacket(name, BankSocketPacket.OPEN, password, 0); BankSocketPacket unfolded = sendReceive(packet, sin, sout, "Compte ouve // // // // } else if (cmd.equals("close")) { // fermeture de compte String name = getName(stdin); String password = getPassword(stdin); BankSocketPacket packet = new BankSocketPacket(name, BankSocketPacket.CLOSE, password, -1); BankSocketPacket unfolded = sendReceive(packet, sin, sout, "Solde : "); System.out.println(unfolded.amount + " pieces rendues."); System.out.println("Au revoir."); } else if (cmd.equals("deposit")) { // depot d’argent String name = getName(stdin); String password = getPassword(stdin); System.out.println("Veuillez entrer le montant : "); int amount = Integer.parseInt(stdin.readLine().toLowerCase()); BankSocketPacket packet = new BankSocketPacket(name, BankSocketPacket.DEPOSIT, password, amount); BankSocketPacket unfolded = sendReceive(packet, sin, sout, "Depot reali } else if (cmd.equals("withdraw")) { // retrait d’argent String name = getName(stdin); String password = getPassword(stdin); System.out.println("Veuillez entrer le montant : "); int amount = Integer.parseInt(stdin.readLine().toLowerCase()); BankSocketPacket packet = new BankSocketPacket(name, BankSocketPacket.WITHDRAW, password, amount); BankSocketPacket unfolded = sendReceive(packet, sin, sout, "Retrait rea } else if (cmd.equals("balance")) { // solde du compte String name = getName(stdin); String password = getPassword(stdin); BankSocketPacket packet = new BankSocketPacket(name, BankSocketPacket.BALANCE, password, -1); BankSocketPacket unfolded = sendReceive(packet, sin, sout, "Solde : "); System.out.println(unfolded.amount + " pieces sur le compte."); } else if (cmd.equals("history")) { // historique des transactions String name = getName(stdin); String password = getPassword(stdin); Vector transactions = bank.getTransactionHistory(name, password); for(int i = 0; i < transactions.size(); i++) System.out.println(transactions.elementAt(i)); } else if (cmd.equals("quit")) { // break BankSocketPacket packet = new BankSocketPacket("noname", BankSocketPacket.QUIT, "nopass", -1); BankSocketPacket unfolded = sendReceive(packet, sin, sout, "Solde : "); System.out.println("Au revoir\n"); break; } else System.out.println("Action inconnue"); } }// while not quitted // Exceptions, erreurs de syntaxe, affichage d’utilisation catch (Exception e) { System.err.println(e); System.err.println("Utilisation : java BankLocal"); } } }// class Client Hugues M OUNIER 12 Examen Java Hugues M OUNIER FIUPSO 3/DESS 2001/2002 13