packageud3.examples.cinema.models;importjava.io.Serializable;/** * Classe que representa una pel·licula. * <p> * Aquesta classe implementa Serialitzable per poder ser convertida a * bytes i poder ser enviada mitjançant sockets. */publicclassFilmimplementsSerializable{/** * Nom de la pel·licula */privateStringname;/** * Any de publiacació */privateintreleaseYear;/** * Duració de la pel·lícula en minuts */privateintduration;/** * Constructor * @param name Nom de la pel·lícula * @param releaseYear Any de publicació * @param duration Duració de la pel·lícula */publicFilm(Stringname,intreleaseYear,intduration){this.name=name;this.releaseYear=releaseYear;this.duration=duration;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetReleaseYear(){returnreleaseYear;}publicvoidsetReleaseYear(intreleaseYear){this.releaseYear=releaseYear;}publicintgetDuration(){returnduration;}publicvoidsetDuration(intduration){this.duration=duration;}/** * Obté la representació de la pel·lícula en String * @return Representació de la pel·lícula */@OverridepublicStringtoString(){return"Film{"+"name='"+name+'\''+", releaseYear="+releaseYear+", duration="+duration+'}';}}
Request.java
packageud3.examples.cinema.models;importjava.io.Serializable;/** * Classe que representa una petició o resposta entre el servidor i el client. * <p> * Aquesta classe implementa Serialitzable per poder ser convertida a * bytes i poder ser enviada mitjançant sockets. */publicclassRequestimplementsSerializable{/** * Tipus de petició (GET/POST/SUCCESS/ERROR) * @see RequestType */privateRequestTypetype;/** * Objecte que es pot adjuntar a la comunicació */privateObjectobject;/** * Missatge opcional que es pot adjuntar a la comunicació */privateStringmessage;/** * Constructor de la petició * @param type Tipus de la petició * @param object Objecte adjuntat */publicRequest(RequestTypetype,Objectobject){this.type=type;this.object=object;}/** * Constructor de la petició * @param type Tipus de la petició * @param object Objecte adjuntat * @param message Missatge adjuntat */publicRequest(RequestTypetype,Objectobject,Stringmessage){this.type=type;this.object=object;this.message=message;}publicRequestTypegetType(){returntype;}publicvoidsetType(RequestTypetype){this.type=type;}publicObjectgetObject(){returnobject;}publicvoidsetObject(Objectobject){this.object=object;}publicStringgetMessage(){returnmessage;}publicvoidsetMessage(Stringmessage){this.message=message;}}
RequestType.java
packageud3.examples.cinema.models;importjava.io.Serializable;/** * Enumeració amb els diferents tipus de peticions que podem trobar * <p> * Aquesta classe implementa Serialitzable per poder ser convertida a * bytes i poder ser enviada mitjançant sockets. */publicenumRequestTypeimplementsSerializable{/** * El tipus GET s'utilitza per sol·licitar algun element del servidor */GET,/** * El tipus POST s'utilitza per enviar algun element al servidor */POST,/** * El tipus SUCCESS s'utilitza per indicar que l'acció s'ha dut a terme correctament */SUCCESS,/** * El tipus SUCCESS s'utilitza per indicar que l'acció no s'ha dut a terme correctament */ERROR}
Servidor:
CinemaServer.java
packageud3.examples.cinema.server;importud3.examples.cinema.models.Film;importjava.io.IOException;importjava.net.ServerSocket;importjava.net.Socket;importjava.util.ArrayList;importjava.util.List;/** * CinemaServer és un servidor TCP/IP que gestiona pel·lícules. * <p> * Actualment perme't obtindre pel·licules del servidor mitbançant la petició "GET" * i afegir pel·licules mitjançant la petició "POST" * * @author Joan Puigcerver */publicclassCinemaServer{privatefinalServerSocketserver;privatefinalList<CinemaServerHandler>clients;privatefinalList<Film>films;privatebooleanrunning;/** * Crea un servidor CinemaServer en el port port especificat. * * @param port Port on escoltarà el servidor * @throws IOException Excepcions del constructor ServerSocket */publicCinemaServer(intport)throwsIOException{server=newServerSocket(port);clients=newArrayList<>();films=newArrayList<>();running=true;}/** * Afig una pel·licula al servidor. * @param film Pel·licula que s'afegirà */publicvoidaddFilms(Filmfilm){films.add(film);}/** * Obté les pel·licules disponibles en el servidor * @return Llista de pel·licules en el servidor */publicList<Film>getFilms(){returnfilms;}/** * Obté la pel·lícula identificada per un id. Actualment l'id és * la posició en la llista. * @param id id de la pel·lícula * @return Pel·lícula amb l'id especificada. */publicFilmgetFilm(intid){returnfilms.get(id);}/** * Esborra un client de la llista de clients * @param client Client que es vol esborrar */publicvoidremoveClient(CinemaServerHandlerclient){clients.remove(client);}/** * Fil d'execució del servidor. * <p> * El servidor escolta el port i espera noves connexions. * Quan una nou client es connecta, es crea un objecte CinemaServerHandler, * que gestionarà la comunicació amb aquest client en un fil distint. * <p> * D'aquesta manera, el servidor pot continuar escoltant i esperant * noves connexions mentres cada fil gestiona la comunicació * amb cada client. */publicvoidrun(){while(running){try{// Escolta i esperà una nova connexió.Socketclient=server.accept();// Quan un client es connecta, es crea un objecte CinemaServerHandler que// gestionarà la comunicació amb el client connectat.System.out.println("Nou client acceptat.");CinemaServerHandlerhandler=newCinemaServerHandler(client,this);clients.add(handler);// S'inicia CinemaServerHandler en un fil independenthandler.start();}catch(IOExceptione){System.err.println("Error while accepting new connection");System.err.println(e.getMessage());}}}/** * Inicia un servidor CinemaServer en el port 1234 * @param args Arguments del programa. No se'n utilitza cap. */publicstaticvoidmain(String[]args){try{CinemaServerserver=newCinemaServer(1234);server.run();}catch(IOExceptione){thrownewRuntimeException(e);}}}
CinemaServerHandler.java
packageud3.examples.cinema.server;importud3.examples.cinema.models.Film;importud3.examples.cinema.models.Request;importud3.examples.cinema.models.RequestType;importjava.io.*;importjava.net.Socket;/** * Classe que gestiona la comunicació del servidor * amb un únic client en un fil d'execució independent. */publicclassCinemaServerHandlerextendsThread{/** * Socket que permet comunicar-se amb el client. */privatefinalSocketclient;/** * Servidor CinemaServer */privatefinalCinemaServerserver;/** * Objecte ObjectInputStream que permet rebre objectes pel Socket. */privatefinalObjectInputStreamobjIn;/** * Objecte ObjectOutputStream que permet enviar objectes pel Socket. */privatefinalObjectOutputStreamobjOut;/** * Constructor que inicialitza els canals de comunicació a partir de l'objecte Socket. * @param client Socket per comunicar-se amb el client. * @param server Servidor * @throws IOException Llançada si hi ha algun error creant els canals de comunicació */publicCinemaServerHandler(Socketclient,CinemaServerserver)throwsIOException{this.client=client;this.server=server;objIn=newObjectInputStream(client.getInputStream());objOut=newObjectOutputStream(client.getOutputStream());}/** * Fil d'execució independent per cada client. * <p> * El servidor espera peticions (Request) i contesta a elles. * <p> * Si es del tipus POST, afegirà la pel·licula rebuda al servidor. * Si es del tipus GET, enviarà la pel·licula sol·licitada al client. */@Overridepublicvoidrun(){try{// Obtenim objectes del tipus Request del client fins que aquest es desconnecte.Requestreq;while((req=(Request)objIn.readObject())!=null){if(req.getType()==RequestType.POST){// Si la petició és del tipus POST// Recuperem la pel·licula de la peticióFilmfilm=(Film)req.getObject();// Afegim la pel·lícula al servidorserver.addFilms(film);// Enviem una resposta del tipus SUCCESS al client// indicant que la pel·licula s'ha afegit correctamentStringmessage=String.format("Film %s added.",film);Requestresponse=newRequest(RequestType.SUCCESS,null,message);System.out.println(message);objOut.writeObject(response);}elseif(req.getType()==RequestType.GET){// Si la petició és del tipus GET// Recuperem la ID de pel·licula de la peticióintid=(Integer)req.getObject();// Enviem una resposta al clientRequestresponse;if(id>=server.getFilms().size())// Si no existeix la pel·licula, enviem una resposta del tipus ERRORresponse=newRequest(RequestType.ERROR,null,String.format("No s'ha trobat cap pel·licula amb id %d",id));else// Si existeix la pel·licula, enviem una resposta del tipus SUCCESS amb la pel·licula solicitadaresponse=newRequest(RequestType.SUCCESS,server.getFilm(id));// Enviem la respostaobjOut.writeObject(response);}}client.close();}catch(IOException|ClassNotFoundExceptione){System.err.println("Error while handling client.");System.err.println(e.getMessage());}finally{this.server.removeClient(this);}}}
Client:
CinemaClient.java
packageud3.examples.cinema.client;importud3.examples.cinema.models.Film;importud3.examples.cinema.models.Request;importud3.examples.cinema.models.RequestType;importjava.io.IOException;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.net.Socket;importjava.util.Scanner;/** * Client que es connecta a un CinemaServer * i permet realitzar les accions de afegir o obtenir * pel·lícules del servidor */publicclassCinemaClient{/** * Socket que permet connectar-se amb el servidor */privatefinalSocketsocket;/** * Scanner per interactuar amb l'usuari */privatefinalScannerscanner;/** * Canal de comuncació per rebre objectes del servidor mitjançant el socket. */privatefinalObjectInputStreamobjIn;/** * Canal de comuncació per enviar objectes al servidor mitjançant el socket. */privatefinalObjectOutputStreamobjOut;/** * Constructor del client. * Es connecta al servidor indicat i inicialitza els cannals de comunicació i el Scanner. * @param host Direciió del servidor * @param port Port on escolta el servidor * @throws IOException Llançada si hi ha algun error connectant-se al servidor. */publicCinemaClient(Stringhost,intport)throwsIOException{this.scanner=newScanner(System.in);this.socket=newSocket(host,port);this.objOut=newObjectOutputStream(socket.getOutputStream());this.objIn=newObjectInputStream(socket.getInputStream());}/** * Envia una pel·licula al servidor * @param f Pel·lícula * @throws IOException Llançada si hi ha un error al enviar la pel·lícula o rebent una resposta * @throws ClassNotFoundException Llançada si l'objecte rebut pel servidor no és d'una classe coneguda. */privatevoidsendFilm(Filmf)throwsIOException,ClassNotFoundException{// Creem una petició POST amb la pel·lículaRequestreq=newRequest(RequestType.POST,f);// Enviem la peticióobjOut.writeObject(req);// Esperem una resposta del servidorRequestresponse=(Request)objIn.readObject();// Comprovem si hi ha hagut algun error i mostrem el missatge de repostaif(response.getType()==RequestType.ERROR)System.err.printf("ERROR: %s\n",response.getMessage());elseif(response.getType()==RequestType.SUCCESS)System.out.printf("%s\n",response.getMessage());}/** * Reb una pel·lícula del servidor a partir de la seua ID * @param id ID de la pel·lícula * @return Pel·licula rebuda pel servidor; null si hi hagut algun error. * @throws IOException Llançada si hi ha un error al rebre la pel·lícula * @throws ClassNotFoundException Llançada si l'objecte rebut pel servidor no és d'una classe coneguda. */privateFilmreceiveFilm(intid)throwsIOException,ClassNotFoundException{// Creem una petició del tipus GET amb la IDRequestreq=newRequest(RequestType.GET,id);// Enviem la peticióobjOut.writeObject(req);// Esperem una resposta del servidorRequestin=(Request)objIn.readObject();// Si hi ha hagut algun error, mostrem el missatgeif(in.getType()==RequestType.ERROR)System.err.printf("ERROR: %s\n",in.getMessage());// Retornem l'objecte rebutreturn(Film)in.getObject();}/** * Mostra les accions del menú */privatevoidprintMenuActions(){System.out.println("1. Afegir pel·lícula.");System.out.println("2. Obtenir pel·lícula.");System.out.println("0. Eixir.");}/** * Demana una elecció vàlida a l'usuari (0 fins max incluït) * Si l'usuari no indica una elecció vàlida, li tornarà a preguntar. * @param max Elecció màxima * @return Elecció de l'usuari */privateintaskUserAction(intmax){System.out.print("Introdueix la teua elecció: ");intaction=scanner.nextInt();scanner.nextLine();while(action<0||action>max){System.out.print("La elecció introduida no està entre els valors vàlids.");System.out.print("Introdueix la teua elecció: ");action=scanner.nextInt();scanner.nextLine();}returnaction;}/** * Mostra el menu principal de l'aplicació. */publicvoidmenu(){while(true){printMenuActions();intaction=askUserAction(2);switch(action){case0:return;case1:try{addFilm();}catch(IOException|ClassNotFoundExceptione){System.err.println("Error afegint una pel·lícula.");}break;case2:try{getFilm();}catch(IOException|ClassNotFoundExceptione){System.err.println("Error obtenint una pel·lícula.");}break;}}}/** * Interactua amb l'usuari per afegir una pel·lícula al servidor. * @throws IOException Llançada si hi ha un error al afegir la pel·lícula * @throws ClassNotFoundException Llançada si l'objecte rebut pel servidor no és d'una classe coneguda. */privatevoidaddFilm()throwsIOException,ClassNotFoundException{System.out.println("AFEGIR PEL·LÍCULA");System.out.print("Introdueix el nom de la pel·lícula: ");Stringnom=scanner.nextLine();System.out.print("Introdueix el any de publicació de la pel·lícula: ");intyear=scanner.nextInt();System.out.print("Introdueix la duració de la pel·lícula: ");intduration=scanner.nextInt();Filmfilm=newFilm(nom,year,duration);sendFilm(film);}/** * Interactua amb l'usuari per obtindre una pel·lícula al servidor. * @throws IOException Llançada si hi ha un error al obtindre la pel·lícula * @throws ClassNotFoundException Llançada si l'objecte rebut pel servidor no és d'una classe coneguda. */privatevoidgetFilm()throwsIOException,ClassNotFoundException{System.out.println("OBTENIR PEL·LÍCULA");System.out.print("Introdueix la id de la pel·lícula: ");intid=scanner.nextInt();Filmfilm=receiveFilm(id);if(film!=null)System.out.printf("S'ha obtingut la pel·lícula %s.\n",film);}/** * Inicia el client * @param args Arguments del programa. No s'utilitzen. */publicstaticvoidmain(String[]args){System.out.println("Connectant-se amb el servidor...");try{CinemaClientcinema=newCinemaClient("localhost",1234);cinema.menu();}catch(IOExceptione){System.err.println("Error connectant-se amb el servidor.");}}}
L'objectiu principal de l'aplicació es gestionar pel·lícules, per tant, hem creat la classe Film
que representa una pel·lícula.
L'aplicació ha de permetre realitzar l'enviament de pel·lícules en els dos sentits.
Per poder indicar-li al servidor quina acció es vol realitzar, hem creat la enumeració
RequestType.
Aquesta enumeració permet classificar cada petició en els següents tipus:
GET: El client enviarà una petició d'aquest tipus al servidor per sol·licitar una pel·lícula al servidor.
POST: El client enviarà una petició d'aquest tipus al servidor per enviar-li una pel·lícula.
SUCCESS: El servidor respondrà amb aquest tipus si la acció sol·licitada s'ha portat a terme correctament.
ERROR: El servidor respondrà amb aquest tipus si hi ha hagut algun error processant la acció sol·licitada.
Per últim, hem creat l'objecte Request, que representa una petició al servidor o una resposta al client.
Aquest objecte esta identificat per un tipus RequestType, conté un objecte Object que pot ser adjuntat
i un missatge String.
L'objectiu d'aquesta classe es ser enviada mitjançant un ObjectOutputStream i ser rebuda per un ObjectInputStream,
que poden ser creats amb el OutputStream i InputSteam de Socket respectivament.
Client:
ObjectOutputStreamobjOut=newObjectOutputStream(socket.getOutputSteam());Requestreq=newRequest(RequestType.POST,newFilm("La vida es bella",1997,116))objOut.writeObject(request);
Per poden enviar aquests objectes mitjançant sockets, s'han de poder convertir a un array de bytes.
Per indicar-ho, és necessari que les classes implementen la interfície Serializable.
La classe CinemaServer implementa un servidor que gestiona pel·lícules.
Aquest servidor escolta en el port especificat i espera a que els clients es connecten.
Cada vegada que un client es connecta, es crea un objecte CinemaServerHandler,
que s'executa en un fil independent i gestiona la comunicació amb el client.
La classe CinemaServerHandler s'encarrega de gestionar les peticions del client.
Si la petició és del tipus POST, rebrà una pel·lícula i l'afegeix al servidor.
Si la petició és del tipus GET, buscarà la pel·lícula sol·licitada i li la retornarà al client.