Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Práctica
2
El
servicio
“echo”
en
Java-‐RMI
Diseño
y
Aplicaciones
de
Sistemas
Distribuidos
(SDI)
José
Simó
Ten
Miguel
Mateo
Pla
Rev:
Septiembre
2015.
ETSINF-‐UPV
SDI
Contenido
Práctica
2.
El
servicio
de
"echo"
en
Java-‐RMI
..........................................................
3
2.1
Estructura
de
la
aplicación
.....................................................................................................................
3
2.1.1
El
proyecto
prj-‐rmi_Interfaces
(interfaces)
.................................................................................
4
2.1.2
El
proyecto
prj-‐rmi_Server
(servidores)
........................................................................................
4
2.1.3
El
proyecto
prj-‐rmi_Clients
(clientes)
.............................................................................................
5
2.2
Realización
del
servicio
de
echo
elemental
en
RMI
.....................................................................
6
2.2.1
Creación
del
proyecto
............................................................................................................................
6
2.2.2
Generación
de
la
interfaz
RMI
...........................................................................................................
6
2.2.3
Generación
del
servidor
RMI
..............................................................................................................
7
2.2.4
Generación
de
stubs
................................................................................................................................
7
2.2.5
Generación
del
cliente
RMI
..................................................................................................................
7
2.2.6
Compilación
y
ejecución
de
aplicaciones
RMI
en
Eclipse
.......................................................
8
2.3
Realización
de
la
aplicación
"echo"
utilizando
movilidad
de
código
..................................
10
2.4
Ficheros
de
apoyo.
Proyecto
prj-‐rmi_Interfaces.
........................................................................
13
2.4.1
Fichero
interfaces/echo/EchoInt.java
........................................................................................
13
2.4.2
Fichero
interfaces/compute/TaskInt.java
................................................................................
13
2.4.3
Fichero
interfaces/compute/ComputeServerInt.java
..........................................................
13
2.5
Ficheros
de
apoyo.
Proyecto
prj-‐rmi_Servers.
.............................................................................
14
2.5.1
Fichero
server/EchoObject.java
.....................................................................................................
14
2.5.1
Fichero
server/EchoObjectRMI.java
............................................................................................
14
2.5.2
Fichero
server/ServCompute.java
.................................................................................................
15
2.6
Ficheros
de
apoyo.
Proyecto
prj-‐rmi_Clients.
...............................................................................
17
2.6.1
Fichero
client/EchoRMI.java
...........................................................................................................
17
2.6.2
Fichero
client/ComputeTestClient.java
......................................................................................
17
2.6.3
Fichero
client/TaskEcho.java
.........................................................................................................
18
Pág:
2
ETSINF-‐UPV
SDI
Figura
3.1.
Organización
de
los
proyectos
de
la
práctica.
A
modo
de
ejemplo,
considerando
la
primera
parte
de
la
práctica
en
la
que
se
pretende
crear
un
servidor
“echo”
implementado
como
un
objeto
RMI,
la
utilización
del
servicio
por
parte
del
cliente
es
tan
simple
como
invocar
un
método
de
un
objeto
utilizando
la
infraestructura
RMI.
Ver
Figura
3.2.
Pág:
3
ETSINF-‐UPV
SDI
Figura
3.2.
Interacción
RMI.
2.1.1 El
proyecto
prj-‐rmi_Interfaces
(interfaces)
Consta
dos
paquetes,
uno
para
cada
servicio
a
realizar,
es
decir,
uno
para
cada
parte
de
la
práctica.
• interfaces.echo.EchoInt.java:
describe
el
servicio
"echo".
Este
fichero
debe
ser
prácticamente
igual
a
la
interfaz
especificada
en
la
práctica
anterior,
excepto
que
el
interfaz
en
esta
práctica
es
subclase
de
java.rmi.Remote.
public interface EchoIntRMI extends java.rmi.Remote
Pág:
4
ETSINF-‐UPV
SDI
servicio
de
"echo"
en
remoto
(para
clientes
remotos).
Su
implementación
se
basa
en
crear
una
instancia
del
objeto
EchoObject
y
delegar
en
el
la
implementación
del
método
echo.
La
funcionalidad
adicional
que
aporta
esta
clase
es
la
de
registrar
el
servicio
en
el
servidor
de
nombres
y
proporcionarle
la
capacidad
de
ser
invocado
remotamente
mediante
el
código
genérico
que
aporta
la
clase
UnicastRemoteObject.
El
objeto
servidor
lo
componen
el
código
objeto
correspondiente
a
estos
ficheros
junto
a
los
skeletons
generados
automáticamente
por
RMI.
Para
la
realización
de
esta
práctica
hay
que
usar
una
versión
del
JDK
posterior
a
1.4.
• server.ServCompute.java:
es
un
objeto
análogo
al
“EchoObjectRMI”
pero
que
implementa
el
interfaz
“ComputeServerInt”
en
la
segunda
parte
de
la
práctica.
Pág:
5
ETSINF-‐UPV
SDI
1. Descargue
los
ficheros
de
ayuda
al
directorio
de
descargas
($WS/descargas)
si
los
hubiera.
2. Cree
los
proyectos
prj-rmi_Interfaces, prj-rmi_Clients y
prj-rmi_Servers
en
el
workspace
según
se
indica
en
la
Práctica
1
cree
también
los
paquetes
en
los
que
se
estructura
cada
proyecto:
interfaces.echo,
interfaces.compute,
clients
y
servers
respectivamente.
3. Copie
los
ficheros
de
código
fuente
descargados
en
su
lugar
correspondiente
en
cada
uno
de
los
directorios
de
los
paquetes
creados.
4. Refresque
cada
uno
de
los
proyectos:
Select
Project
-‐>
Refresh
(F5)
5. Asegúrese
de
establecer
la
dependencia
del
proyecto
“inerfaces”
en
los
proyectos
“clients”
y
“servers”:
Project
Properties
-‐>
Java
Build
Path
-‐>
Projects
-‐>
Add…
2.2.2 Generación
de
la
interfaz
RMI
6. El
desarrollo
de
la
interfaz
RMI,
contenida
en
el
paquete
rmi,
consta
de
los
siguientes
pasos:
Realizar
este
paso
sólo
si
no
ha
descargado
ya
el
fichero
“EchoInt.java”
y
lo
ha
incorporado
al
proyecto
o Cree
una
interfaz
EchoInt.
La
opción
más
sencilla
es
utilizar
File-‐>New-‐
>Other-‐>Java-‐>RMI
-‐>
Remote
Interface.
También
se
puede
hacer
con
File-‐>New-‐>New
Interface
especificando:
§ Name:
EchoInt
Pág:
6
ETSINF-‐UPV
SDI
7. Cree
una
clase
EchoObjectRMI
con
File-‐>New
Class
especificando:
o Name:
EchoObjectRMI
o Superclass:
java.rmi.server.UnicastRemoteObject
o Extended
interfaces:
interfaces.echo.EchoInt
o public
static
void
main
o Constructors
from
superclass
8. Si
no
lo
ha
hecho
ya,
copie
el
fichero
EchoObject.java
del
directorio
de
descargas
al
directorio
server
del
proyecto
en
el
workspace
y
actualice
el
Package
explorer
para
visualizarlo.
9. Complete
la
implementación
del
servidor
con
la
implementación
de
los
métodos
echo
y
main.
o El
método
echo
delega
en
el
correspondiente
método
de
la
clase
EchoObject.
o El
método
main
básicamente
debe
realizar
una
instancia
del
EchoObjectRMI,
obtener
el
stub
y
exportar
el
objeto
mediante
UnicastRemoteObject.exportObject,
e
inscribirlo
en
el
Servicio
de
Nombres
de
RMI
2.2.4 Generación
de
stubs
la
generación
de
stubs
y
skeletons
puede
realizarse
desde
una
consola
MS-‐DOS
estableciendo
la
variable
de
entorno
CLASSPATH
y
ejecutando
el
compilador
de
RMI,
especificando
como
parámetros
el
servidor
de
echo:
> rmic server/EchoObjectRMI.java
Si
utilizamos
una
versión
posterior
a
1.4
de
JDK,
este
paso
no
es
necesario
ya
que
los
stubs
y
skeletons
se
generan
automáticamente
en
tiempo
de
ejecución.
2.2.5 Generación
del
cliente
RMI
El
desarrollo
del
cliente
RMI,
contenido
en
el
paquete
client,
consta
de
los
siguientes
pasos:
Pág:
7
ETSINF-‐UPV
SDI
10. Si
no
lo
ha
hecho
ya,
copie
el
fichero
EchoRMI.java
del
directorio
de
descargas
al
directorio
client
del
proyecto
en
el
workspace
y
actualice
el
Package
explorer
para
visualizarlo.
11. Realice
los
ejercicios
propuestos.
o Sólo
tiene
que
realizar
la
invocación
al
servidor
de
echo.
o Observe
la
necesidad
de
un
gestor
de
seguridad
en
el
cliente.
12. Arranque
el
servicio
de
nombres
RMI
rmiregistry.
Este
servicio
puede
arrancarse
desde
una
consola
MS-‐DOS
estableciendo
la
variable
de
entorno
CLASSPATH
y
ejecutando:
> start rmiregistry
En
Unix:
> rmiregistry&
Por
ejemplo:
> start rmiregistry -J-Djava.rmi.server.codebase=
file:///c:/dya/ws/
00_basico/prj-rmi_Interfaces/bin/
Pág:
8
ETSINF-‐UPV
SDI
O
bien:
> start rmiregistry -J-Djava.rmi.server.useCodebaseOnly=false
Esta
última
opción,
la
de
habilitar
la
descarga
desde
cualquier
codebase,
no
es
recomendable.
Se
ha
observado
que
en
versiones
de
JDK1.8
no
funciona.
13. Ejecute
el
servidor
EchoObjectRMI
creando
un
perfil
de
ejecución
con
el
menú
Run
as
-‐>
Java
Application
y
fijando
las
siguientes
propiedades
de
la
máquina
virtual
(menú
(x)
Arguments,
cuadro
de
texto
VM
Arguments):
o La
ruta
del
classpath
(no
es
necesario
en
el
perfil
de
ejecución
de
eclipse):
-classpath c:\sdi\ws\00_basico\prj-rmi_Server\bin;
c:\sdi\ws\00_basico\prj-rmi_Interfaces\bin
Si,
como
es
el
caso,
el
proyecto
“servidor”
ya
tiene
acceso
a
las
clases
de
“interfaz”,
no
es
necesario
especificar
esta
propiedad.
Es
muy
importante
no
olvidar
la
barra
final
en
la
especificación
del
codebase.
o La
ejecución
de
aplicaciones
desde
consola
debe
especificar
correctamente
las
propiedades
de
la
máquina
virtual:
> java server/EchoObjectRMI -Djava.rmi.server.codebase= ...
-Djava.security.policy=... –classpath ...
14. Ejecute
el
cliente
EchoRMI
creando
un
perfil
de
ejecución
con
el
menú
Run
as
-‐>
Java
Application
y
especificando:
o Argumentos
de
ejecución
(menú
(x)
Arguments):
host_del_servidor.
o Propiedades
de
la
máquina
virtual
igual
que
en
la
ejecución
del
servidor.
o Si
utiliza
un
gestor
de
seguridad
es
necesario
especificar
una
política:
-‐ java.security.policy:
permite
especificar
el
URL
para
un
fichero
(java.policy)
con
la
política
de
seguridad
necesaria
para
ejecutar
aplicaciones
RMI.
Pág:
9
ETSINF-‐UPV
SDI
Para
usar
una
política
de
seguridad,
debe
crear
un
fichero
(llamado
java.policy)
con
el
siguiente
contenido:
grant {
permission java.net.SocketPermission "*:1024-65535", "connect,accept,resolve";
};
Considere
también
la
siguiente
política
de
seguridad
en
la
que
se
especifica
el
codebase
junto
a
un
acceso
muy
permisivo.
grant codeBase "file:///C:/sdi/-{
permission java.security.AllPermission "", "";
};
15. Realice
también
pruebas
de
invocación
de
clientes
a
servidores
remotos
utilizando
el
servidor
de
echo
de
otros
compañeros
de
prácticas.
2.3 Realización
de
la
aplicación
"echo"
utilizando
movilidad
de
código
Visite
el
Tutorial
de
Java
(https://docs.oracle.com/javase/tutorial/)
y
seleccione
el
capítulo
de
RMI.
En
este
capítulo
se
desarrolla
una
aplicación
donde
existe
un
servidor
de
computo
genérico
ComputeEngine,
que
ejecuta
un
código
(subclase
de
Task)
que
el
cliente
le
puede
especificar
como
parámetro
por
valor
en
una
invocación
RMI
(movilidad
de
código).
La
Task
que
se
desarrolla
en
el
tutorial
es
las
Task
Pi
que
contiene
un
algoritmo
para
calcular
el
número
pi.
Ver
figura
3.3.
Figura
3.3.
La
aplicación
ComputeEngine
Esta
segunda
parte
de
la
práctica
consiste
en
compilar
y
ejecutar
el
ejemplo
del
tutorial
y,
posteriormente,
realizar
un
servidor
de
cómputo,
con
funcionalidad
extendida,
en
el
proyecto
prj-rmi_Servers.
Este
servidor
de
cómputo
a
realizar
tiene
que
cumplir
el
interfaz
definido
en
“
interfaces.compute.ComputeServerInt
.java”.
De
esta
manera
el
servidor
ComputeEngine
ejecutará
una
Task
con
el
servicio
de
"echo".
El
interfaz
del
servidor
de
cómputo
que
se
pretende
construir
es:
package
interfaces.compute;
import
java.rmi.Remote;
Pág:
10
ETSINF-‐UPV
SDI
import
java.rmi.RemoteException;
public
interface
ComputeServerInt
extends
Remote
{
//loadTask:
Cargar
una
nueva
task
en
el
ComputeEngine.
//No
la
ejecuta.
//retorna
el
identificador
de
la
tarea.
int
loadTask(TaskInt
a)
throws
RemoteException;
//removeTask:
elimina
una
tarea
del
servidor.
int
removeTask(int
idx)
throws
RemoteException;
//Carga
y
ejecuta
una
tarea
sin
parámetros.
//Retorna
un
"Object"
Object
executeTask(TaskInt
a)
throws
RemoteException;
//Carga
y
ejecuta
una
tarea
con
parámetros.
//Recibe
un
"Object
y
retorna
un
"Object"
Object
executeTask(TaskInt
a,
Object
params)
throws
RemoteExceptio
n;
//Ejecuta
una
tarea
previamente
cargada
con
parámetros.
//Recibe
un
"Object
y
retorna
un
"Object"
Object
executeTask(int
idx,
Object
params)
throws
RemoteException;
}
Esta
segunda
parte
consta
de
los
siguientes
pasos:
16. Descargue
el
tutorial
de
ejemplo
ComputePi.
17. Ejecute
la
aplicación
ejemplo
ComputePi.
Siga
los
pasos
de
la
sección
2.2.6.
18. Modifique
los
ficheros
ServCompute.java,
ComputeTestClient.java
y
TaskEcho.java
de
los
proyectos
prj-‐rmi_Servers,
prj-‐rmi_Clients
respectivamente.
La
nueva
aplicación
deberá
ajustarse
a
la
nueva
especificación
de
la
interfaz
ComputeServerInt.
19. Ejecute
la
nueva
versión
del
servidor
de
cómputo
genérico.
En
RMI,
todas
las
clases
involucradas
en
la
interacción
entre
los
objetos
distribuidos
deben
estar
en
alguna
de
las
rutas
especificadas
mediante
la
propiedad
“java.rmi.server.codebase”.
Cuando
se
especifica
en
una
JVM
la
propiedad
“java.rmi.server.codebase”,
lo
que
se
está
declarando
es
la
localización
de
confianza
para
descargar
clases
de
forma
dinámica.
Pág:
11
ETSINF-‐UPV
SDI
Pág:
12
ETSINF-‐UPV
SDI
Pág:
13
ETSINF-‐UPV
SDI
Pág:
14
ETSINF-‐UPV
SDI
/**
*
*/
public
EchoObjectRMI()
{
//throws
RemoteException
super();
}
private
static
EchoObject
eo
=
new
EchoObject();
@Override
public
String
echo(String
input)
{
return
eo.echo(input);
}
/**
*
@param
args
*/
public
static
void
main(String[]
args)
{
if
(System.getSecurityManager()
==
null)
{
System.setSecurityManager(new
SecurityManager());
}
try
{
//EJERCICIO:
get
the
local
registry
//EJERCICIO:
build
the
EchoObjectRMI
stub
//EJERCICIO:
bind
(or
rebind)
the
stub
into
the
local
registry
}
catch
(RemoteException
e)
{
System.err.println("Something
wrong
happended
on
the
remote
end");
e.printStackTrace();
System.exit(-‐1);
//
can't
just
return,
rmi
threads
may
not
exit
}
System.out.println("The
echo
server
is
ready");
}
}
Pág:
15
ETSINF-‐UPV
SDI
Pág:
16
ETSINF-‐UPV
SDI
2.6.1 Fichero
client/EchoRMI.java
package
client;
import
java.io.*;
import
java.rmi.registry.LocateRegistry;
import
java.rmi.registry.Registry;
import
interfaces.echo.EchoInt;
public
class
EchoRMI
{
/**
*
@param
args
*/
public
static
void
main(String[]
args)
{
if
(args.length<1){
System.out.println("Uso
echo
<host>");System.exit(1);
}
if(System.getSecurityManager()==
null)
{
System.setSecurityManager(new
SecurityManager());
}
BufferedReader
stdIn
=
new
BufferedReader(new
InputStreamReader(System.in));
PrintWriter
stdOut
=
new
PrintWriter(System.out);
String
input,output;
try{
//EJERCICIO:
"lookup"
the
Echo
RMI
object
stdOut.print(">
");
stdOut.flush();
while
(
(input
=
stdIn.readLine())!=null){
//EJERCICIO:
call
echo
RMI
object
stdOut.println(output);
stdOut.print(">
");
stdOut.flush();
}
}catch(Exception
e){
System.out.println("RMI
Echo
Client
error:
"
+
e.getMessage());
}
}
}
Pág:
17
ETSINF-‐UPV
SDI
import
java.rmi.Naming;
import
java.rmi.RemoteException;
import
interfaces.compute.ComputeServerInt;
import
interfaces.compute.TaskInt;
public
class
ComputeTestClient
implements
Serializable
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L;
public
TaskInt
echoTask
=
new
TaskEcho();
public
static
void
main(String[]
args)
{
String
server_name
=
new
String();
if
(args.length==1){
server_name="//"+args[0]+"/Compute";
}else{
server_name="//localhost/Compute";
}
ComputeTestClient
computeClient
=
new
ComputeTestClient();
if(System.getSecurityManager()==
null)
{
System.setSecurityManager(new
SecurityManager());
}
BufferedReader
stdIn
=
new
BufferedReader(new
InputStreamReader(System.in));
PrintWriter
stdOut
=
new
PrintWriter(System.out);
String
input,output;
try{
//EJERCICIO:
"lookup"
the
Compute
server
RMI
object
//EJERCICIO:
load
the
task
(computeClient.echoTask)
to
the
computeServer
stdOut.print(">
");
stdOut.flush();
while
(
(input
=
stdIn.readLine())!=null){
//EJERCICIO:
execute
the
loaded
task.
Get
the
response
in
"output"
stdOut.println(output);
stdOut.print(">
");
stdOut.flush();
}
}catch(Exception
e){
System.out.println("Error
en
el
cliente
de
echo
RMI
:
"
+
e.getMessage());
e.printStackTrace();
}
}
}
import java.io.Serializable;
import java.rmi.RemoteException;
import interfaces.compute.TaskInt;
Pág:
18
ETSINF-‐UPV
SDI
*
*/
private static final long serialVersionUID = 1L;
@Override
public Object execute() throws RemoteException {
//EJERCICIO
}
@Override
public Object execute(Object params) throws RemoteException {
//EJERCICIO
}
}
Pág: 19