Introducción A La Programación en Java RMI Mediante Ejemplos
Introducción A La Programación en Java RMI Mediante Ejemplos
Introducción
El objetivo de esta guía es presentar varios ejemplos muy sencillos
que permitan familiarizarse con los aspectos básicos del desarrollo
de programas que
usan Java RMI.
Esta guía está basada en ejemplos que intentan recoger algunos de los
usos más típicos de Java RMI.
import java.rmi.*;
Recapitulando, desarrollaremos una clase derivada de UnicastRemoteObject y que implemente la interfaz remota ServicioEco
(fichero
ServicioEcoImpl.java):
import java.rmi.*;
import java.rmi.server.*;
return s.toUpperCase();
Es importante entender que todos los objetos especificados como parámetros de un método remoto, así como el retornado por el mismo,
se pasan por
valor, y no por referencia como ocurre cuando se realiza
una invocación a un método local. Esta característica tiene como consecuencia
que cualquier
cambio que se haga en el servidor sobre un objeto recibido
como parámetro no afecta al objeto original en el cliente.
Por ejemplo, este método remoto no
llevará a cabo la labor que se le supone,
aunque sí lo haría en caso de haber usado ese mismo código (sin la
excepción RemoteException, evidentemente)
para definir un
método local.
s.reverse();
import java.rmi.*;
import java.rmi.server.*;
class ServidorEco {
if (args.length!=1) {
return;
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
try {
catch (RemoteException e) {
System.exit(1);
catch (Exception e) {
System.err.println("Excepcion en ServidorEco:");
e.printStackTrace();
System.exit(1);
registry.rebind(name, srv);
Téngase en cuenta que el primer método permite usar una URL para manejar de forma integrada la información de localización, mientras que el
segundo utiliza directamente una cadena de caracteres para identificar el servicio.
Un aspecto clave en Java RMI es la seguridad. En el código
del servidor se puede apreciar cómo éste instancia un gestor de
seguridad (más sobre el
tema en la sección dedicada a la ejecución
del programa). Para ejemplos sencillos, podría eliminarse esta
parte del código del servidor (y del cliente)
pero es conveniente
su uso para controlar mejor la seguridad y es un requisito en caso
de que la aplicación requiera carga dinámica de clases.
La parte principal de este programa está incluida en la sentencia
try y consiste en crear un objeto de la clase que implementa
el servicio remoto y
darle de alta en el rmiregistry
usando el método estático rebind que permite especificar la
operación usando un formato de tipo URL. Nótese que el
rmiregistry
sólo permite que se den de alta servicios que ejecutan en su misma
máquina.
El cliente debe obtener una referencia remota (es decir, una referencia
que corresponda a un objeto remoto) asociada al servicio para luego
simplemente
invocar de forma convencional sus métodos, aunque
teniendo en cuenta que pueden generar la excepción RemoteException.
En este
ejemplo, la referencia
la obtiene a través del rmiregistry.
import java.rmi.*;
import java.rmi.server.*;
class ClienteEco {
if (args.length<2) {
return;
if (System.getSecurityManager() == null)
System.setSecurityManager(new SecurityManager());
try {
System.out.println(srv.eco(args[i]));
catch (RemoteException e) {
catch (Exception e) {
System.err.println("Excepcion en ClienteEco:");
e.printStackTrace();
Compilación
El proceso de compilación tanto del cliente como del servidor es el
habitual en Java. El único punto que conviene resaltar es que para
generar el programa
cliente, además de la(s) clase(s) requerida(s)
por la funcionalidad del mismo, se debe disponer del fichero class
que define la interfaz (en este caso,
ServicioEco.class),
tanto para la compilación como para la ejecución del cliente.
Esto se ha resuelto en este ejemplo creando un enlace simbólico.
Si
quiere probar el ejemplo usando dos máquinas, lo que recomendamos,
deberá copiar el fichero class a la máquina donde se ejecutará
el cliente.
Obsérvese
que no es necesario, ni incluso conveniente, disponer en el
cliente de las clases que implementan el servicio.
En el ejemplo que nos ocupa, dado que, por simplicidad, no se han definido
paquetes ni se usan ficheros JAR, para generar el programa cliente
y el
servidor, basta con entrar en los directorios respectivos y ejecutar directamente:
javac *.java
Ejecución
cd servidor
grant {
permission java.security.AllPermission;
};
Y la del cliente:
cd cliente
HOLA
ADIOS
Esta concurrencia automática hace que, como puede observarse en el ejemplo previo,
el programa servidor no termine cuando
completa su código, sino
que se quede esperando indefinidamente
la llegada de peticiones. Nótese cómo en el tratamiento de las excepciones
se usa una llamada a System.exit para
completar explícitamente
su ejecución.
import java.rmi.*;
import java.io.*;
import java.rmi.*;
import java.rmi.server.*;
PrintWriter fd;
try {
fd = new PrintWriter(f);
catch (FileNotFoundException e) {
System.err.println(e);
System.exit(1);
System.out.println(m);
fd.println(m);
cd servidor
cd ../cliente
2544d2543
< tu 714
2545a2545
> tu 714
9985d9984
< yo 5997
9986a9986
> yo 5997
15325a15326
> yo 8469
15444d15444
< yo 8469
17708a17709
> tu 8229
17985d17985
< tu 8229
De manera similar a los ejemplos previos, el servidor ofrecerá un servicio remoto para darse de alta y de baja, así como
para enviarle la información que
escribe cada usuario.
Sin embargo, en este caso, se requiere, además, que los clientes ofrezcan
una interfaz remota para ser notificados de lo que escriben los otros clientes.
import java.rmi.*;
import java.rmi.*;
Pasamos a la implementación del servicio de chat (fichero ServicioChatImpl.java) que usa un contenedor de tipo lista para guardar los clientes
conectados:
import java.util.*;
import java.io.*;
import java.rmi.*;
import java.rmi.server.*;
List<Cliente> l;
l = new LinkedList<Cliente>();
l.add(c);
l.remove(l.indexOf(c));
throws RemoteException {
for (Cliente c: l)
if (!c.equals(esc))
c.notificacion(apodo, m);
import java.util.*;
import java.rmi.*;
import java.rmi.server.*;
class ClienteChat {
if (args.length!=3) {
return;
if (System.getSecurityManager() == null)
System.setSecurityManager(new SecurityManager());
try {
srv.alta(c);
while (ent.hasNextLine()) {
srv.baja(c);
System.exit(0);
catch (RemoteException e) {
catch (Exception e) {
System.err.println("Excepcion en ClienteChat:");
e.printStackTrace();
import java.rmi.*;
import java.rmi.server.*;
Referencias remotas como valor retornado (fábricas de referencias remotas): servicio simple de
banco
Además de poder ser recibidas como parámetros de un método, puede obtenerse
una referencia remota como el valor de retorno de un método (al fin y al
cabo, eso es lo que hace el método lookup del rmiregistry).
import java.rmi.*;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.*;
import java.rmi.*;
import java.rmi.server.*;
nombre = n;
return nombre;
return saldo;
saldo += valor;
return saldo;
Cuenta c = srv.crearCuenta(args[2]);
c.operacion(30);
import java.io.*;
Titular(String n, String i) {
nombre = n;
iD = i;
return nombre;
return iD;
Como se puede observar, se trata de una clase trivial pero que presenta
una característica importante: dado que se van a usar objetos
de esta clase como
parámetros y valores de retorno de métodos
RMI, es necesario especificar que esta clase implemente la interfaz
Serializable, declarando así el
programador que esa clase puede serializarse.
import java.rmi.*;
import java.rmi.*;
import java.rmi.server.*;
tit = t;
return tit;
return saldo;
saldo += valor;
return saldo;
import java.rmi.*;
import java.util.*;
import java.util.*;
import java.rmi.*;
import java.rmi.server.*;
List<Cuenta> l;
l = new LinkedList<Cuenta>();
l.add(c);
return c;
return l;
Cuenta c = srv.crearCuenta(tit);
c.operacion(30);
List <Cuenta> l;
l = srv.obtenerCuentas();
for (Cuenta i: l) {
Titular t = i.obtenerTitular();
import java.io.*;
super(n, i);
nombreTutor = t;
return nombreTutor;