|
|
UNIVERSITAD
DE LAS AMERICAS Cholula,
Puebla MEXICO |
SISTEMAS DISTRIBUIDOS - IS 417
PRÁCTICA
III
(RMI)
INTRODUCCIÓN
En esta práctica revisaremos el procedimiento y las consideraciones a realizarse
durante la construcción de una aplicación distribuida con Java RMI. Como caso
de estudio tomaremos el desarrollo una aplicación simplificada de soporte a un
sitio para la venta de artículos basado en un modelo de subastas (como lo hace
eBay). El escenario típico de utilización de una aplicación de tal índole es el
siguiente:
1
Un usuario (jugando el rol de vendedor)
se conecta y ofrece un producto, estableciendo un precio inicial y una fecha y
hora límite de subasta.
2
Los compradores potenciales, se
conectan como usuarios corrientes, y tienen la posibilidad de visualizar el
catálogo de productos disponibles a la compra. El comprador puede entonces
seleccionar un producto y realizar una oferta. Cada comprador puede conectarse
y ofertar sobre un producto varias veces, mientras el periodo de subasta es aún
válido. Notemos que sólo se podrán hacer ofertas que sobrepasen el monto actual
(el famoso juego del ¿Quién da más?).
3
Al finalizar el periodo de la subasta,
el producto es asignado al mejor postor. El vendedor puede entonces verificar
los datos del comprador correspondiente, con los que se procederá a contactarlo
y así concluir la venta.
Para su operación, tal aplicación debe considerar la administración
centralizada de la siguiente información:
Notemos que analizar tal aplicación sería complejo, por lo que la versión
propuesta es considerablemente mas sencilla.
IMPLEMENTACIÓN
Dividimos el codigo entre 10 archivos :
Quatro archivos siguiendo el modelo MVC por la aplicación centralizada (que
va executarse en cada machinas clientes) :
Por la aplicacion distribuida, solo hemos cambiado
el archivo SubastaModelo.
SubastaModelo.java
SubastaVista.java
SubastaControlador.java
Principal.java
Dos clases serializable por manejar los informaciones :
Por la aplicacion distribuida, hemos cambiado
estas clases en “serializable”
InformacionOferta.java
InformacionProducto.java
El codigo de communicacion del lado cliente y su interfaz por RMI:
Estos archivos permiten de construyir un objecto “cliente” que puede estar
usado por el servidor por actualizar las vistas, po ejemplo.
SubastaClient.java
ClientInterfaz.java
El codigo del servidor y su interfaz por RMI :
Servicios del servidor, en
la aplicacion centralizada estaba el codigo del modelo.
SubastaInterfaz.java
SubastaServeur.java
SubastaVista.java
Implementacion de la interfaz de usuario (usando SWING).
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import
javax.swing.event.ListSelectionListener;
public class SubastaVista {
JFrame principal;
JTextField usuario;
JTextField producto;
JTextField precioInicial;
JTextField monto;
DefaultComboBoxModel productos;
JLabel
precioActual;
JList lista;
JButton conectar;
JButton salir;
JButton ponerALaVenta;
JButton
obtenerLista;
JButton ofrecer;
public SubastaVista() {
JFrame.setDefaultLookAndFeelDecorated(true);
Container
panel;
principal
= new JFrame("Cliente Subasta");
panel
= principal.getContentPane();
panel.setLayout(new
GridLayout(0, 2));
usuario
= new JTextField();
panel.add(new
JLabel("Nombre del usuario"));panel.add(usuario);
conectar
= new JButton("Conectar");
salir
= new JButton("Salir");
panel.add(conectar);
panel.add(salir);
producto
= new JTextField();
precioInicial
= new JTextField();
panel.add(new
JLabel("Producto a ofrecer")); panel.add(producto);
panel.add(new
JLabel("Precio inicial")); panel.add(precioInicial);
ponerALaVenta
= new JButton("Poner a la venta");
panel.add(ponerALaVenta); panel.add(new JLabel());
productos
= new DefaultComboBoxModel();
lista
= new JList(productos); //data has type Object[]
lista.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
lista.setLayoutOrientation(JList.VERTICAL);
JScrollPane
listaScroller = new JScrollPane(lista);
listaScroller.setPreferredSize(new
Dimension(250, 80));
obtenerLista
= new JButton("Obtener lista");
panel.add(obtenerLista);
panel.add(listaScroller);
precioActual
= new JLabel();
panel.add(new
JLabel("Precio actual")); panel.add(precioActual);
monto
= new JTextField();
ofrecer
= new JButton("Ofrecer");
panel.add(ofrecer);
panel.add(monto);
principal.setSize(400,
400);
principal.setVisible(true);
}
public void asignarActionListener(ActionListener
controlador) {
conectar.addActionListener(controlador);
salir.addActionListener(controlador);
ponerALaVenta.addActionListener(controlador);
obtenerLista.addActionListener(controlador);
ofrecer.addActionListener(controlador);
}
public void asignarListSelectionListener(ListSelectionListener
controlador) {
lista.addListSelectionListener(controlador);
}
public String getUsuario() {
return usuario.getText();
}
public String getProducto() {
return producto.getText();
}
public float getPrecioInicial() {
float resultado = 0.0f;
try {
resultado =
Float.parseFloat(precioInicial.getText());
} catch (Exception e) {
System.out.println("Oops
... problemas con el precio inicial");
}
return
resultado;
}
public void
reinicializaListaProductos() {
productos.removeAllElements();
}
public void agregaProducto(String prod) {
productos.addElement(prod);
}
public void desplegarPrecio(String precio) {
precioActual.setText(precio);
}
public float getMontoOfrecido() {
float resultado = 0.0f;
try {
resultado =
Float.parseFloat(monto.getText());
} catch (Exception e) {
System.out.println("Oops
... problemas con el monto ofrecido");
}
return resultado;
}
public String getProductoSeleccionado() {
return
(String)lista.getSelectedValue();
}
}
SubastaControlador.java
Implementacion del controlador de la interfaz.
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import javax.swing.JList;
import
javax.swing.event.ListSelectionListener;
import
javax.swing.event.ListSelectionEvent;
public class SubastaControlador implements ActionListener,
ListSelectionListener {
SubastaVista
vista;
SubastaModelo
modelo;
Hashtable
listaConPrecios;
public
SubastaControlador(SubastaVista v, SubastaModelo m) {
vista = v;
modelo = m;
}
public void actionPerformed(ActionEvent evento) {
String usuario;
String producto;
float monto;
System.out.println("<<"+evento.getActionCommand()+">>");
if
(evento.getActionCommand().equals("Salir")) {
System.exit(1);
}
else if (evento.getActionCommand().equals("Conectar")) {
usuario =
vista.getUsuario();
System.out.println("Registrarse como usuario: " + usuario);
modelo.registraUsuario(usuario);
} else if
(evento.getActionCommand().equals("Poner a la venta")) {
usuario =
vista.getUsuario();
producto =
vista.getProducto();
monto =
vista.getPrecioInicial();
System.out.println("Haciendo oferta del producto: " +
producto);
modelo.agregaProductoALaVenta(usuario, producto, monto);
} else
if (evento.getActionCommand().equals("Obtener lista")) {
Vector lista
= modelo.obtieneCatalogo();
Enumeration it;
InformacionProducto info;
listaConPrecios = new Hashtable();
vista.reinicializaListaProductos();
it =
lista.elements();
while
(it.hasMoreElements()) {
info =
(InformacionProducto) it.nextElement();
listaConPrecios.put(info.producto,
String.valueOf(info.precioActual));
vista.agregaProducto(info.producto);
}
} else if
(evento.getActionCommand().equals("Ofrecer")) {
producto =
vista.getProductoSeleccionado();
monto = vista.getMontoOfrecido();
usuario =
vista.getUsuario();
modelo.agregaOferta(usuario, producto, monto);
}
}
public void valueChanged(ListSelectionEvent e) {
if
(e.getValueIsAdjusting() == false) {
JList lista = (JList)e.getSource();
String item =
(String)lista.getSelectedValue();
if (item != null) {
System.out.println(item);
String precio =
(String)listaConPrecios.get(item);
vista.desplegarPrecio(
precio );
}
}
}
}
SubastaModelo.java
Ahora el modelo de la aplicacion esta solo una interfaz con
la clase Cliente
import java.util.Vector;
public class SubastaModelo
{
SubastaClient
client;
public SubastaModelo() {
try {
client = new
SubastaClient();
}
catch(java.rmi.RemoteException e) {
System.err.println(" l'objet distant a déclenché l'erreur "+e);
}
}
public boolean registraUsuario(String nombre) {
return
client.registraUsuario(nombre);
}
public boolean agregaProductoALaVenta(String vendedor, String producto,
float
precioInicial) {
return
client.agregaProductoALaVenta(vendedor,producto,precioInicial);
}
public boolean agregaOferta(String comprador, String producto,
float
monto) {
return
client.agregaOferta(comprador,producto,monto);
}
public Vector
obtieneCatalogo() {
return client.obtieneCatalogo();
}
}
InformacionOferta.java
Clase serializable para administrar las ofertas.
public class InformacionOferta implements
java.io.Serializable{
String comprador;
String producto;
float monto;
public InformacionOferta(String c, String p, float m) {
comprador =
c;
producto = p;
monto = m;
}
}
InformacionProducto.java
Clase serializable para administrar los productos.
public class InformacionProducto implements java.io.Serializable{
String vendedor;
String producto;
float precioInicial;
float precioActual;
public InformacionProducto(String v, String p, float pi) {
vendedor = v;
producto = p;
precioInicial
= pi;
precioActual = pi;
}
public boolean actualizaPrecio(float monto) {
if (monto > precioActual) {
precioActual = monto;
return true;
} else
return false;
}
public String getNombreProducto() {
return producto;
}
public float getPrecioActual() {
return precioActual;
}
}
Principal.java
Programa principal
public class Principal {
public static void main(String args[]) {
SubastaVista
vista;
SubastaControlador
controlador;
SubastaModelo
modelo;
vista = new
SubastaVista();
modelo = new
SubastaModelo();
controlador =
new SubastaControlador(vista, modelo);
vista.asignarActionListener(controlador);
vista.asignarListSelectionListener(controlador);
}
}
ClientInterfaz.java
Interfaz que permite a el servidor de actualizar los
clientes
import java.rmi.*;
public interface
ClientInterfaz extends Remote{
public void refreshClient() throws RemoteException;
}
SubastaClient.java
Implementacion del cliente, comunicacion con el servidor.
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import java.util.Vector;
public class SubastaClient extends
UnicastRemoteObject implements
ClientInterfaz, java.io.Serializable
{
private SubastaInterfaz obj;
public SubastaClient() throws RemoteException{
try {
System.setSecurityManager(new
RMISecurityManager());
obj = (SubastaInterfaz) Naming.lookup("IS417/SubastaServeur");
}
catch(Exception e) {
System.err.println(" l'objet distant a déclenché l'erreur "+e);
}
}
public boolean registraUsuario(String nombre)
{
boolean result=false;
try {
result=obj.registraUsuario(nombre,this);
}
catch(RemoteException e) {
System.err.println(" l'objet distant a déclenché l'erreur "+e);
}
return result;
}
public boolean agregaProductoALaVenta(String vendedor, String producto,
float
precioInicial) {
boolean result=false;
try {
result=obj.agregaProductoALaVenta(vendedor,producto,precioInicial);
}
catch(RemoteException e) {
System.err.println(" l'objet distant a déclenché l'erreur "+e);
}
return result;
}
public boolean agregaOferta(String comprador, String producto,
float monto) {
boolean result=false;
try {
return
obj.agregaOferta(comprador,producto,monto);
}
catch(RemoteException
e) {
System.err.println(" l'objet distant a déclenché l'erreur "+e);
}
return result;
}
public Vector obtieneCatalogo() {
Vector result=null;
try {
result=obj.obtieneCatalogo();
}
catch(RemoteException
e) {
System.err.println(" l'objet
distant a déclenché l'erreur "+e);
}
return result;
}
//Remote procedures
public void refreshClient() throws
RemoteException{
System.out.println("REFRESH");
}
}
SubastaInterfaz.java
Interfaz de los servicios del servidor
import java.rmi.*;
import java.util.Vector;
public
interface SubastaInterfaz extends Remote {
public boolean registraUsuario(String
nombre, ClientInterfaz client) throws RemoteException;
public boolean agregaProductoALaVenta(String
vendedor, String producto,
float
precioInicial) throws RemoteException;
public boolean agregaOferta(String
comprador, String producto, float monto)
throws RemoteException;
public Vector obtieneCatalogo() throws RemoteException;
}
SubastaServeur.java
Implementacion de los servicios del servidor
import java.rmi.*;
import java.net.*;
import java.rmi.server.UnicastRemoteObject;
import java.util.Hashtable;
import java.util.Vector;
public class SubastaServeur extends
UnicastRemoteObject implements
SubastaInterfaz, java.io.Serializable
{
Hashtable usuarios;
Hashtable productos;
Hashtable ofertas;
Vector clientes;
private void
refreshAllClients() {
for
(int i=0; i<clientes.size();i++)
{
ClientInterfaz
client=(ClientInterfaz)(clientes.elementAt(i));
try{
client.refreshClient();
}catch(Exception
ex){
System.out.println("Exception
declenchee par RefreshALlClients");
ex.printStackTrace();
}//fin catch
}//fin for
}
public SubastaServeur() throws
RemoteException {
// permet la communication par protocole TCP
usuarios
= new Hashtable();
clientes = new
Vector();
productos
= new Hashtable();
ofertas
= new Hashtable();
}
public boolean
registraUsuario(String nombre,ClientInterfaz client) throws RemoteException {
if
(!usuarios.containsKey(nombre)) {
System.out.println("Agregando un nuevo usuario: " + nombre);
usuarios.put(nombre, nombre);
clientes.addElement(client);
return true;
} else
return false;
}
public boolean agregaProductoALaVenta(String vendedor, String producto,
float precioInicial) throws RemoteException {
if(!usuarios.containsKey(vendedor)) {
System.out.println("Received
request from unknown user "+vendedor+", ignoring it !");
return false;
}
else {
if (!productos.containsKey(producto)) {
System.out.println("User
"+ vendedor +" agregando un nuevo producto: " + producto);
productos.put(producto,
new InformacionProducto(vendedor, producto, precioInicial));
refreshAllClients();
return true;
} else
return false;
}
}
public boolean agregaOferta(String comprador, String producto,
float
monto) throws RemoteException {
if (productos.containsKey(producto)) {
InformacionProducto
infoProd;
infoProd =
(InformacionProducto) productos.get(producto);
if
(infoProd.actualizaPrecio(monto)) {
ofertas.put(producto + comprador,
new InformacionOferta(comprador, producto, monto));
refreshAllClients();
return true;
} else
return false;
} else
return false;
}
public Vector obtieneCatalogo()
throws RemoteException {
Vector
resultado;
resultado
= new Vector(productos.values());
return resultado;
}
public static void main(String[] args) {
try {
SubastaInterfaz obj= new
SubastaServeur();
System.setSecurityManager(new
RMISecurityManager());
Naming.rebind("IS417/SubastaServeur",obj);
System.out.println("le serveur est pret.");
}
// on récupère
les RemoteException
catch(RemoteException re){
System.out.println("exception dans SubastaServeur.main:"+
re);
}
// on récupère
les MalformedURLException
catch(MalformedURLException e ){
System.out.println("MalformedURLException:"+
e);
}
}
}
BUILD.BAT
Compilacion y generacion de los talones
del *.class
javac *.java
rmic SubastaServeur
rmic SubastaClient
echo [DONE]
Despues la
complacion tenemos dos archivos mas : SubastaServeur_Stub.class y SubastaClient_Stub.class
que son los interfazes bajo-nivel de rmi.
Ahora podemos
executar la aplicacion (en tres ventanas diferentes) :
Ø rmiregistry
Ø java
SubastaServeur
Ø java
Principal
El
programa y las fuentes estan disponibles aqui
SECURITY
We
had added a SecurityManager before the bind and the lookup, so RMI can manage
securized operations.
System.setSecurityManager(new RMISecurityManager());
For
more informations, see : http://www.javaworld.com/javaworld/jw-11-1997/jw-11-hood.html
Pierre Derrier #203293