Práctica 4: Java Remote Method Invocation (RMI) Aplicaciones Telemáticas II Introducción Hasta el momento hemos visto aplicaciones remotas donde un cliente utiliza un objeto remoto que ha sido publicado por otro programa al cual llamamos un servidor. En esta práctica veremos un caso distinto, donde tanto el cliente como el servidor deben usar objetos que no han sido implementados por ellos, es decir deben utilizar interfaces. Al igual que en las prácticas anteriores solo un objeto será publicado en el servidor RMI (rmiregistry). Este corresponde al servidor, el cual deberá estar esperando clientes con peticiones, pero la diferencia será que el servidor recibirá como parámetro un objeto proporcionado por el cliente. Ejercicio 1 En esta práctica desarrollaremos una aplicación Chat. Para esto se deben implementar las siguientes clases: ChatServidorInterface.java: declaración de los métodos disponibles por el servidor. ChatServidor.java: corresponde al servidor del chat. Esta clase recibe los mensajes desde cada cliente y los distribuye al resto de los usuarios del chat. ChatClienteInterface.java: definición de los métodos disponibles del cliente. ChatCliente.java: implementa una interfaz gráfica, los usuarios ingresan el texto y este lo envia al servidor chat. La figura 1 muestra el esquema del cliente y servidor. La idea es que muchos clientes se conecten a un servidor de chat, este deberá mantener una lista con los usuarios conectados. Cada cliente puede enviar un mensaje a todos los usuarios del chat o a un usuario en particular. Si el cliente desea enviar un mensaje a todos solo debe escribir el mensaje, si desea enviar un mensaje a un cliente en particular debe escribir 1
Figure 1: Esquema cliente - servidor nombre cliente- mensaje. Cada cliente debe tener un nombre como variable de instancia, este nombre puede ser recibido por línea de comando al ejecutar el cliente de la siguiente manera: [prompt] java ClienteChat Maria Lo que creará un cliente cuyo nombre en el chat será Maria, de esta forma si otro usuario desea enviar un mensaje solo debe escribir: Maria- mensaje para Maria El servidor será el encargado de revisar si el mensaje es para todos los usuarios para uno en particular. Para simplificar el problema suponga que los mensajes no contienen el caracter - y que no existe espacio entre el nombre del usuario y el caracter -. Se encuentra publicado en el Aula Global un cliente que implementa una interfaz gráfica, debe bajar este fichero y completarlo. Descripción de las clases a implementar La clase ChatServidor.java debe implementar los métodos definidos en Chat- ServidorInterface.java, los métodos son los siguientes: void agregarcliente(chatclienteinterface c) 2
Este método recibe una instancia ChatClienteInterface y debe agregar al cliente a la lista de clientes conectados al servidor. Para esto, el servidor debe tener una variable de instancia donde vaya guardando los clientes que se van agregando. Esta variable puede ser del tipo ArrayList o Vector. Además, el método se debe encargar de enviar la información actualizada al resto de los clientes. void borrarcliente(string s) Elimina al cliente que tenga el nombre igual al String s de la lista de usuarios conectados y envia la información actualizada de los clientes conectados a todos los clientes. public void enviarmensaje(string s, ChatClienteInterface c) El servidor recibe el mensaje s desde el cliente c, debe revisar si el mensaje corresponde a un mensaje para todos los usuarios del chat o un usuario en particular y debe enviar este mensaje a quien corresponda. Recuerde que primero debe escribir la interfaz ServidorChatInterface.java y definir los métodos, para luego escribir la clase ChatServidor.java que debe extender de UnicastRemoteObject e implementar ServidorChatInterface. La figura 2 muestra la interfaz del usuario del chat, en la parte derecha se encuentra la lista de usuarios conectados al servidor. La clase ChatCliente.java debe implementar los siguientes métodos: public String getnombre() Retorna el nombre del usuario conetado. public void nuevomensaje(string s) Recibe un nuevo mensaje desde el servidor y debe mostrarlo en el chat. public void refrescarlista(string[] usuarios) El servidor del chat llamará a este método cada vez que se agregue o elimine un usuario al chat. La idea es actualizar la lista de usuarios conectados que se muestra en la interfaz gráfica. Para esto debe mantener un variable java.awt.list que es la que se encarga de dibujar la información en la interfaz gráfica. Este método debe borrar el contenido de la lista y agregar el nuevo contenido. Recibe como parámetro un arreglo de String donde vienen los nombres de los usuarios conectados. Entrega Debe entregar todos los ficheros generados, a través de la sección de prácticas del Aula Global en un fichero comprimido, con el nombre NIA P4.zip, antes de las 24:00 del miércoles 14 de febrero. 3
Figure 2: Interfaz usuario chat 4
Nota Se entrega la siguiente clase como esqueleto donde debe completar los métodos que faltan. import java.rmi.*; import java.rmi.server.unicastremoteobject; import java.net.*; import java.io.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class ChatCliente extends UnicastRemoteObject implements ChatClienteInterface, ActionListener { Frame f; //direccion del servidor static String host = "localhost"; // TextField con la entrada del usuario. TextField in = new TextField(50); // corresponde al TextArea donde se mostraran los mensajes recibidos // desde el servidor // DEBE ACTUALIZAR ESTA VARIABLE CUANDO LLEGA UN MENSAJE TextArea out = new TextArea(23, 15); // boton para enviar mensaje Button send = new Button("Enviar"); // boton para cerrar el chat (el usuario sale del chat) Button end = new Button("Cerrar"); // lista con los clientes conectados, corresponde a una lista de String java.awt.list connected = new java.awt.list(); // nombre del usuario String minombre; Panel p = new Panel(); ChatServidorInterface o; // constructor de la clase ChatCliente(String nombre) throws Exception { // titulo del frame AGREGAR NOMBRE DEL USUARIO f = new Frame("Chat "); this.minombre = nombre; 5
Remote ro = Naming.lookup("rmi://localhost/chat"); System.out.println("Servidor encontrado"); o = (ChatServidorInterface) ro; // se va creando el frame o.agregarcliente(this); out.seteditable(false); f.setbackground(new Color(100, 100, 100)); FlowLayout fl = new FlowLayout(); fl.setvgap(20); p.setlayout(fl); p.add(in); p.add(send); p.add(end); BorderLayout gl = new BorderLayout(); f.setlayout(gl); f.add(out, "Center"); f.add(p, "South"); f.add(connected, "East"); send.addactionlistener(this); end.addactionlistener(this); in.addactionlistener(this); f.setvisible(true); f.pack(); public void refrescarlista(string[] members) { // COMPLETAR ESTE METODO // maneja los botones de la interfaz public void actionperformed(actionevent e) { try { if (e.getsource() == end) { // se selecciono el boton end // DEBE COMPLETAR else { // caso en que envia un mensaje // DEBE COMPLETAR catch (Exception ex) { ex.printstacktrace(); // main llama al constructor con variable de linea de comando static public void main(string args[]) throws Exception { new ChatCliente(args[0]); 6
public String getnombre() throws RemoteException { // COMPLETAR METODO public void nuevomensaje(string s) throws RemoteException { // COMPLETAR METODO Las variables de instancia más importantes de la clase son: TextField in, textfield donde se guarda la información ingresada por el usuario en este caso el mensaje. Para obtener la información contenida en la variable usamos el método in.gettext() y para poner texto in.settext(string s). TextArea out, es el textarea donde se despliegan los mensajes enviados desde el servidor. Para agregar texto al TextArea utilizar out.append(string s). java.awt.list connected, corresponde a un objeto que contiene otros objetos (como un ArrayList) pero este objeto permite desplegarse en la interfaz gráfica. En nuestra implementación corresponde a la lista de usuarios conectados la cual debemos actualizar según lo indique el servidor. Para limpiar la lista utilizar el método removeall() y para agregar elementos add(object). minombre, corresponde al nombre del usuario. El método ActionPerformed se encarga de manejar la interfaz, cuando se presiona alguno de los 2 botones se invoca el método. En la implementación entregada hay un if para el caso en que el usuario seleccionar el botón Cerrar o Enviar. 7