Logo Studenta

Java-RMI

¡Este material tiene más páginas!

Vista previa del material en texto

Java RMI (Remote Method Invocation) la tecnología hace que sea muy fácil crear applets basados ​​en la web que realizan poderosos del lado del servidor las operaciones, como acceder a una base de datos o la comunicación con aplicaciones de servidores remotos. Sin embargo, una serie de limitaciones RMI hacen que su uso en Internet basados ​​en applets poco prácticas. En este artículo se describe una forma de evitar estas limitaciones mediante el uso de servlets de Java y la serialización de objetos para la comunicación applet / servidor, y demuestra varias maneras de utilizar esta técnica. El objetivo de este artículo es sobre la conversión de una aplicación existente, pero el mismo diseño se pueden utilizar al crear nuevas aplicaciones.
Message Passing y llamada a procedimiento remoto
Fundamentos de paso de mensajes
En general hay dos tipos de primatives: enviar (mensaje, a) y recibir (mensaje, desde).
Recibe puede bloquear, no bloqueo, ya menudo se obtiene tanto de opciones. Enviar puede estar bloqueando, tamponada, sin bloqueo, o cualquier combinación.
Los mensajes pueden ser entendidas como que se envían a través de canales (o direcciones). Algunos diseños permiten múltiples recibe. Si es así, debemos hacer la pregunta aunque sólo sea un receptor recibe un mensaje (en el mejor de quetries simples, como HTTP) o todo recibe un mensaje (en el mejor de los mensajes de apagado, etc.)
Por último, algunos sistemas de empaquetar todo para que pareciera una RPC. Esto se realiza a través de bloqueo envía / recibe, y algunos de maniobra. Generalmente hablando RPC es menos flexible que el paso de mensajes, pero más fácil para el programador. Y normalmente ahorro de tiempo del programador es más importante de ahorro de tiempo de ejecución (normalmente).
marshalling
Marshalling es el acto de agrupar todos los parámetros para una llamada de procedimiento en un mensaje, y luego la separación de ellos a su regreso. En términos generales esto es difícil pero factible. Si un procedimiento quiere enviar 3 ints, puede enviar los 12 bytes. Si otro procedimiento quiere enviar 3 flotadores, también puede enviar 12 bytes. El receptor necesita saber cómo descodificar estos 12 bytes. Normalmente esto se logra haciendo que cada función (nombre,
firma) se asignan a un entero, y anteponiendo esta int en el mensaje. Pero ¿cómo se puede manejar los parámetros por defecto? ¿Y qué acerca de los punteros?
La respuesta Java es usar la inteface Serializable.
The Java RMI Basics
En términos generales, se trata de cómo funciona todo.
• El servidor crea algunos objetos, y le dice al registro de ellos.
• El servidor crea skels para esos objetos y esos skels comenzar lisstening de solicitudes.
• El cliente solicita el registro de dónde se encuentran los objetos y se le dice la dirección del servidor
• El cliente crea talones para cada objeto remoto encontrado, y estos
pedir a los talones de skels para pedir al servidor para preformas algunas operaciones en la cuenta de clientes.
• Los objetos pueden vivir más allá de su uso por cualquier cliente.
• El servidor puede aceptar bytecode que manipula objetos para el cliente. Eso es una ventaja única Java (tm).
Envío de ByteCode
Un método puede recibir como argumento un tipo de objeto que nunca ha oído hablar antes. Cómo: sucesiones.
Sin embargo, el receptor tendrá que utilizar los métodos asociados con ese objeto, por lo que se necesita el código que implementa estos métodos.
Desde bytecode Java es portátil, esto se puede hacer. Pero pensar en los dolores de cabeza de clasificación. Y pensar en los riesgos para la seguridad!
Objetos remotos y locales
Un objeto es un objeto local, al igual que tuvimos en CS120. Cuando un objeto local se envía a un receptor JVM, la JVM recibe una copia de todo el objeto y cualquier código de bytes necesarios como se describe anteriormente. Cuando un objeto remoto capaz es enviado a un receptor JVM, la JVM sólo se pone un trozo, y todas las operaciones sobre el objeto remoto se envían a través del talón a la ubicación de los objetos originales.
Un objeto se convierte remoto capaces de implementar interfaces remotas. Sólo los métodos definidos en las interfaces remotas pueden ser llamados remotamente. Interfaces remotas son creados por
 
• Una interfaz remota amplía el java.rmi.Remote interfaz.
• Cada método de la interfaz declara java.rmi.RemoteException en su cláusula throws, además de las excepciones específicas de la aplicación.
Los objetos remotos deben
• Declarar las interfaces remotas están aplicando
• Definir el constructor para el objeto remoto
• Proporcione una implementación para cada método remoto en las interfaces remotas
Pasar argumentos en RMI
Los argumentos de las o los valores devueltos por métodos remotos pueden ser de cualquier tipo, incluidos los objetos locales, objetos remotos, y los tipos primitivos. Más precisamente, cualquier entidad de cualquier tipo puede ser pasado a o por un método remoto siempre que la entidad es una instancia de un tipo que es un tipo de datos primitivo, un objeto remoto, o un objeto serializable, lo que significa que implementa la interfaz java.io.Serializable.
Unos pocos tipos de objetos no cumplen ninguno de estos criterios y por lo tanto no se puede pasar o regresar de un método remoto. La mayoría de estos objetos, tal como un descriptor de archivo, encapsular la información que tiene sentido sólo dentro de un único espacio de direcciones. Muchas de las clases fundamentales, incluidos los de la java.lang y java.util paquetes, implementar la interfaz Serializable.
Las normas que rigen cómo los argumentos y valores de retorno se transmiten son los siguientes.
• Los objetos remotos son esencialmente pasa por referencia. Una referencia a un objeto remoto es un esbozo, que es un proxy del lado del cliente que implementa el conjunto completo de interfaces remotas que el objeto remoto implementa.
• Los objetos locales son pasados ​​por copia, utilizando la serialización de objetos. Por defecto, todos los campos se copian, excepto los que están marcados estático o transitorio. El comportamiento por defecto de serialización se puede reemplazar en una base de clase por clase.
• Al pasar un objeto por referencia (como se hace con los objetos remotos)
significa que los cambios realizados en el estado del objeto de las llamadas a métodos remotos se reflejan en el objeto remoto original. Cuando se pasa un objeto remoto, sólo aquellas interfaces que son interfaces remotos están disponibles para el receptor; cualquier método
definido en la clase de implementación o definido en no remoto interfaces implementadas por la clase no están disponibles para ese receptor.
Haga una interfaz remota
Esta interfaz debe extenderse java.rmi.Remote. Cada función dentro de ella debe lanzar java.rmi.RemoteException. Las funciones juntas debe capturar el comportamiento del servicio que desea realizar.
public interface RemoteInterface extends java.rmi.Remote
{
public java.util.Date askTime() throws java.rmi.RemoteException;
public	void	tellTime(java.util.Date	d)	throws java.rmi.RemoteException;
}
Make a Remote Object
This object should extend java.rmi.server.UnicastRemoteObject and implement the interface you created above. Hopefully the functions you make here do the service you are trying to create.
public	class	RemoteObject	extends java.rmi.server.UnicastRemoteObject
implements RemoteInterface{
public RemoteObject() throws java.rmi.RemoteException{
// Empty constructor
}
public java.util.Date askTime() throws java.rmi.RemoteException{ System.out.println("RemoteObject.askTime	called"	+	new
java.util.Date() + "\n");
return new java.util.Date();
}
public	void	tellTime(java.util.Date	d)	throws java.rmi.RemoteException{
System.out.println("RemoteObject.tellTime called" + d + "\n");
}
}
Make a Server
The server should have a 'main' that ... Installs a new security manager
Possibly starts an rmiregistry (if it's not already running)
Creates an object of type equal to the interface of the remote objectyou just made.
Binds the object to a name on the registry public class Server{
public static void main(String[] args){
try{
System.setSecurityManager(new java.rmi.RMISecurityManager()); RemoteInterface v=new RemoteObject();
java.rmi.registry.LocateRegistry.createRegistry(5099);
java.rmi.Naming.rebind("//:5099/count", v);
}catch(java.rmi.UnknownHostException x){x.printStackTrace();}
catch(java.rmi.RemoteException x){x.printStackTrace();}
catch(java.net.MalformedURLException x){x.printStackTrace();}
catch(Exception x){x.printStackTrace();}
}
}
Make a Client
The client should
Call lookup to get an object of type remoteobject_stub.	Use that to access the remote object
public class Client{
public static void main(String[] args){ RemoteInterface s=null;
try{
s=(RemoteInterface)java.rmi.Naming.lookup("rmi://euclid.nmu.edu:509
9/count");
s.tellTime(new java.util.Date()); System.out.println(s.askTime());
}catch (Exception x){x.printStackTrace();}
}
}
Las ventajas de usar RMI en Applets
En teoría, la elección de RMI como medio de comunicación entre los applets de Java y aplicaciones de servidor en el Internet es una gran idea. Los beneficios del uso de RMI en dicha aplicación son convincentes, por las siguientes razones:
RMI es parte del núcleo de Java 1,1 especificación, así como Java 2 Standard Edition. Esto significa que el applet RMI será apoyado por todos los JDK 1.1 y el cliente 1.2-compatible, así que usted no tiene que preocuparse sobre cómo crear sus propias clases de comunicación y empaquetarlos con su applet. Incluido en el estándar Java 1,1 y 1,2 paquetes de tiempo de ejecución (JRE y JDK) es una versión ejecutable del registro RMI, el servidor responsable de proporcionar el acceso a objetos RMI. Esto le permite desplegar basado en RMI
aplicaciones de servidor sin necesidad de un servidor de aplicaciones caro o añadir otra en el lado del servidor.
Aunque RMI típicamente hace una conexión directa entre el applet y el servidor, sino que también su operación a través de un túnel HTTP si fuera necesario. Esto permite a los usuarios que se conectan a Internet a través de un proxy HTTP
servidor para utilizar applets RMI.
RMI permite que el código del applet para ejecutar las operaciones del lado del servidor con sintaxis simple llamada a la función, así que es increíblemente fácil de escribir el código para como una aplicación distribuida.
Las limitaciones de Applets RMI en Internet
Aunque el uso de RMI en Internet basados ​​en applets parece una buena idea, en la práctica, la inestabilidad de las conexiones a Internet y la falta de apoyo de cliente que sea poco práctico. Entre los obstáculos para el despliegue de applets RMI en Internet son los siguientes:
A pesar de apoyar la mayoría de la Java 1.1, los navegadores web más populares - Netscape Navigator y Microsoft Internet Explorer - no apoyan adecuadamente RMI. Navigator soporta RMI en la mayoría de plataformas, pero no admite HTTP tunneling, para que los usuarios de Netscape conectados a la web a través de un servidor proxy no puede utilizar un applet RMI. Mientras tanto, Netscape para Macintosh no es compatible con RMI en absoluto. Internet Explorer 4.0-5.0 también carece de soporte RMI, aunque se supone que es un parche disponible para añadir las clases que faltan (las clases RMI a través del sitio web de Microsoft, pero las instrucciones para su instalación no está incluida). Como resultado de ello, un gran porcentaje de los internautas no pueden utilizar applets RMI en su navegador actual.
 
El registro de RMI pueden experimentar problemas al manejar las conexiones a través de Internet. Un error que hace que el registro de dejar de aceptar nuevas conexiones se produce con demasiada frecuencia con los clientes web RMI, lo que requiere que el registro se apaga y se reinicia. Además, en entornos de ejecución Java JDK anteriores a 1.2.2, el servidor de sockets creados para manejar las conexiones de clientes basados ​​en Internet RMI pueden vivir indefinidamente (consumiendo recursos del servidor) incluso después de que los clientes se apagan.
El registro RMI no es tan flexible como la mayoría de aplicaciones de servidor, respondiendo sólo a las conexiones de clientes realizadas a un nombre de host específico. Esto significa que la aplicación de servidor RMI no puede admitir varios dominios en un único servidor web, lo que limita sus opciones de implementación. Muchas aplicaciones pueden requerir que los datos intercambiados entre el cliente y el servidor de ser encriptado, comprimido, o manipulados de alguna otra manera. Hacer esto con RMI es bastante simple, sólo requiere que implemente sus propias clases socket y una fábrica de socket RMI que crea sus tomas especiales. Sin embargo, haciendo caso omiso de la fábrica toma por defecto RMI desactiva la capacidad de hacer HTTP túnel de acceso, lo que impide a los usuarios del servidor proxy.
Implementar una aplicación de servidor RMI requiere una configuración adicional cortafuegos de red. De forma predeterminada, los objetos RMI están obligados a azar, "anónimos" números puerto del servidor, por lo que es imposible predecir los puertos que deben estar a disposición de los clientes de Internet. Funcionalidad RMI nuevo a Java 2 permite especificar un número de puerto cuando se crea un objeto remoto, pero la implementación todavía requiere que los cortafuegos permiten el acceso a los puertos no utilizados normalmente por aplicaciones basadas en web.
Ejemplo de aplicación
En este artículo usted estará trabajando con una aplicación de ejemplo: un simple basada en web de cliente de base de datos. La aplicación consta de un applet de Java que muestra los valores de base de datos y un objeto del lado del servidor Java que realiza tareas de base de datos (añadir, quitar, modificar y recuperar datos) en nombre del applet. La comunicación entre el applet y el objeto de servidor es a través de RMI.
El gráfico siguiente muestra la arquitectura original de la solicitud:
El lado del servidor de esta ilustración es una aplicación Java que hace tres cosas: crea un registro RMI, crea una instancia de la base de datos de acceso a objetos (UserDatabaseImpl), y enlaza el objeto en el registro para que esté disponible para los clientes remotos. Estas son las líneas de código de la aplicación de servidor que realizan estas tareas:
// Create the registry on port 1099
Registry reg =
java.rmi.registry.LocateRegistry.createRegistry(
1099);
// Create the database object
UserDatabaseImpl db = new UserDatabaseImpl();
// Bind the object to the registry as "DB1" java.rmi.Naming.rebind("DB1", db);
Una vez que el objeto de base de datos está obligado al registro, el applet cliente puede utilizar java.rmi.Naming.lookup () para obtener una referencia a la misma. Aquí está el código de cliente:
UserDatabase db; //The remote database object
...
String host = this.getCodeBase().getHost(); String name = "//" + host + "/DB1";
db = (UserDatabase)java.rmi.Naming.lookup(name);
Una vez que el applet tiene una referencia al objeto remoto, puede ejecutar operaciones de bases de datos mediante la invocación de métodos en este objeto. Al igual que con otros objetos RMI, el objeto de base de datos (UserDatabaseImpl) implementa una interfaz remota (UserDatabase) que se extiende java.rmi.Remote. Esta interface remoto define los métodos del objeto de base de datos que pueden ser ejecutados por el applet del cliente, y se parece a esto:
public interface UserDatabase
extends java.rmi.Remote
{
public String createUser(
UserData data) throws RemoteException;
public void editUser(
String userid, UserData data)
throws RemoteException;
public void deleteUser(
String userid) throws RemoteException;
public UserData getUser(
String userid) throws RemoteException;
public Hashtable listUsers(
) throws RemoteException;
}
Para ejecutar una operación de base de datos del applet cliente simplemente llama a uno de estos métodos en el objeto remoto. Por ejemplo, para obtener una lista de los usuarios actualmente en la base de datos,el applet cliente ejecuta:
Usuarios Hashtable = db.listUsers ();
Resumen de los cambios
Aunque los fragmentos de código anteriores demuestran la facilidad con que RMI permite crear applets que tienen acceso a su base de datos y realizar otras tareas en el servidor, usted todavía desea eliminarlo de la parte cliente de la aplicación debido a los problemas descritos anteriormente. Al hacer esto, usted tiene los siguientes objetivos:
Haga todas las transacciones basadas en objetos. En otras palabras, para cumplir una tarea que desea enviar objetos completos al servidor y recibir uno o más objetos a cambio. RMI permite hacer esto fácilmente a través de las llamadas a funciones, y en los cambios que desea mantener este intercambio orientado a objetos.
Usar HTTP para todas las comunicaciones del applet / servidor. Mediante el uso de sólo HTTP, puede hacer que el applet lo más compatible posible con los servidores proxy y firewalls de red.
Haga los cambios cero a la aplicación de servidor RMI. En lugar de volver a escribir el código de servidor sólo quiere agregar una capa de servlet para facilitar la comunicación applet, por lo que no debería tener que tocar los objetos de servidor que ya ha implementado.
Escriba un mínimo de código nuevo. Una vez más, no quiero hacer ninguna más trabajo de lo que sea necesario.
Para lograr estos objetivos va a añadir una serie de servlets de Java del lado del servidor que ejecutará las transacciones RMI en nombre del applet cliente. Para comunicarse con los servlets a través de HTTP, vamos a crear un objeto proxy en el cliente cuyos métodos serán similares a los métodos del objeto servidor RMI. Cuando el applet se ejecuta actualmente métodos directamente en el objeto RMI, en su lugar va a ejecutar métodos en el objeto proxy, preservando toda la sintaxis de la función en el código del applet.
Una vez realizados los cambios en la arquitectura de la aplicación se verá así:
Los Servlets
La capa de servlet de su nueva arquitectura contendrá una serie de servlets que cada uno ejecuta un método único en el objeto remoto. Aquí están los servlets se le hacen y la forma de UserDatabase están asociados con:
Debido a que los servlets se ejecutan cada una única llamada RMI, van a ser muy simple. Cada servlet llevará a cabo las mismas tareas básicas:
1.Prepárese una referencia al objeto de la base de datos del registro RMI.
2.Read en valores de los parámetros del cliente.
3.Execute el método apropiado en el objeto de base de datos.
4.Write el valor de retorno para el applet.
 
 
El primero de estos pasos-que consigue el objeto de base de datos desde el registro-
será común para todos los servlets, por lo que tendrá que definir de una vez en un servlet super-clase y que todos sus específicas método servlets extenderla. en
definición de este servlet padre desea proporcionar flexibilidad al hacer que el
ubicación del registro RMI configurable a través de un parámetro de inicialización. Esto le permite localizar el registro en un sistema distinto del web
servidor (posiblemente uno detrás de un firewall), que no se podía hacer cuando el
subprograma se requiere acceso al registro. Una vez que tenga el lugar del registro se puede obtener el objeto de base de datos a través de java.rmi.Naming.lookup () al igual que el código del applet original hace.
He aquí, pues es el código para DatabaseServlet, el servlet super-clase:
public class DatabaseServlet extends HttpServlet
{
String registry; // The path to the RMI
//registry
UserDatabase db; // The database access
//object
public void init( ServletConfig config)
throws ServletException
{
super.init(config);
// Get the location of
//the RMI registry.
registry = config.getInitParameter( "registry");
// If undefined, assume the local host
if (registry == null) registry = "//localhost";
try {
// Now get the database object
String name = registry + "/DB1";
db = (UserDatabase)Naming.lookup(
name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Ahora que tiene su padre servlet puede crear los servlets individuales que se extienden a éste. De este modo, podrás seguir la receta anterior: lectura de los valores de los parámetros, ejecutar el método de base de datos y devuelve el resultado. Todas estas operaciones se producirá en el método del servlet doPost (), que también incluirá el código necesario para abrir y cerrar los flujos de E / S en el applet.
Esta es su primera servlet, UserCreateServlet, que ejecuta el método createUser () base de datos en nombre del applet:
public class UserCreateServlet extends DatabaseServlet
{
public void doPost( HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
{
// Open the I/O streams ObjectInputStream in = new ObjectInputStream(
req.getInputStream() ); ObjectOutputStream out =
new ObjectOutputStream(
resp.getOutputStream() );
try {
// 1. Read in parameters from
//the applet
UserData data = ( UserData)in.readObject();
// 2. Execute the RMI call
String id = db.createUser(data);
// 3. Write the result out.writeObject(id);
} catch (
Exception e) { e.printStackTrace(
); }
// Close the I/O streams
in.close();
out.close();
}
}
Un par de notas sobre este código:
Usted está utilizando ObjectInputStream y ObjectOutputStream aquí porque quiere mantener a objetos basados ​​en las transacciones entre el applet y el servidor. En este servlet podría haber devuelto el valor de la cadena de resultado en lugar del objeto String entero, pero la pervivencia de un objeto / objetos en el planteamiento de hace su vida mucho más simple.
Aunque usted no declaró ni inicializar el objeto de base de datos (db) en el código para UserCreateServlet, no hace falta. Una vez más, este servlet
extiende DatabaseServlet, el servlet padre ha definido anteriormente, por lo que
obtiene una referencia al objeto base de datos del registro RMI cuando el servlet se inicializa.
En la definición de los servlets que usted puede simplemente copiar y pegar el contenido de UserCreateServlet. El único código que tiene que cambiar en cada servlet (además del nombre de la clase, por supuesto) son las tres líneas de código en el bloque try / catch-esas son las únicas líneas específicas de método. Así que para crear nuestro servlet UserGetServlet, que ejecuta el método de base de datos getUser (), sólo tiene que cambiar estas líneas a ser:
// 1. Read in parameters from
//the applet
String id = (String)in.readObject();
// 2. Execute the RMI call
UserData data = db.getUser(id);
// 3. Write the result out.writeObject(data);
En estos dos servlets que he leído en un objeto y un objeto escrito, pero se puede leer o escribir tantos objetos como quieras. En el caso del método de la base de datos editUser (), debe recuperar dos parámetros del applet cuando regresaba ninguno. En el applet que se ejecuta este método (UserEditServlet) que comience con la misma plantilla de tres líneas y sólo tiene que añadir un extra de readObject () para obtener la línea segundo parámetro, y retire la writeObject () la línea que escribe la Valor de retorno:
// 1. Read in parameters from
//the applet
String id = (String)in.readObject(); UserData data = ( UserData)in.readObject();
// 2. Execute the RMI call db.editUser(id, data);
// 3. Write the result
// [no return value]
Sus otros servlets todos siguen el mismo formato, por lo que no se imprimirá a todos aquí Descargar el código
Client objeto proxy
Ahora que tiene servlets que pueden ejecutar sus métodos RMI a través de HTTP, el applet cliente necesita saber cómo utilizar estos servlets. Aunque se puede reescribir cada llamada al método RMI en el applet para incluir las llamadas servlet correspondiente, que puede ser un montón de cambios en el código existente. Es por eso que vamos a usar un objeto proxy que sintaxis duplica el objeto de base de datos y hace todo de la comunicación servlet necesario, lo que le permite mantener casi la totalidad de su actual código del applet en el tacto.
En la creación de su objeto proxy, UserDatabaseProxy, se iniciacon la interfaz UserDatabase que define los métodos remotos disponibles. Los métodos de su objeto proxy debe ser idéntica a la interfaz, que define estos métodos:
String createUser(UserData data)
void editUser(
String userid, UserData data)
void deleteUser(String userid)
UserData getUser(String userid)
Hashtable listUsers()
Al igual que hizo al crear los servlets, podrás seguir una receta sencilla para definir cada uno de estos métodos en el objeto proxy. En cada uno de estos métodos que quiere lograr lo siguiente: Abra una conexión con el servlet de destino, escriba uno o más parámetros al servlet, y leer el resultado de la ejecución de servlets y devolverlo a la applet.
Para ayudar a lograr esto usted va a utilizar una clase auxiliar llamada ServletWriter. Esta clase gestiona los flujos de E / S entre el applet y servlet y ejecuta la actual transacción HTTP POST. Al igual que los servlets, ServletWriter utiliza ObjectInputStream y ObjectOutputStream pasar objetos enteros entre el cliente y el servidor. El método único definido por ServletWriter es:
PostObjects ObjectInputStream servlet (URL, Serializable objs []) donde servlet define la URL del servlet de destino y objs define una lista de objetos serializables que se escriben en el servlet. El método devuelve el ObjectInputStream de que sus métodos pueden recuperar los valores devueltos por el servlet. Ver código ServletWriter.java para más detalles. Con ServletWriter a su disposición, esto es lo que cada uno de sus métodos de objeto de proxy hará:
1.Cree una URL para el servlet de destino.
2.Crear una matriz de objetos serializable que contiene los parámetros del método.
3.Call ServletWriter.postObjects () para ejecutar la operación de servlet.
4.Read y devolver el valor del resultado.
Paso 1 aquí requiere que conozca la URL base para el servidor web servlet. Dado que este valor será el mismo para cada método, lo vas a definir de una vez por colocarlo en el constructor del objeto de proxy:
URL webBase;// The URL to the servlet web server public UserDatabaseProxy(URL web)
{
webBase = web;
}
Debido a que la dirección URL del servidor web servlet se inicializa durante la construcción, cada uno de sus métodos pueden crear la dirección URL de servlets sus correspondientes relativos a esta URL base.
Con eso fuera del camino, ahora se puede escribir cada uno de los métodos del objeto proxy. Vamos a empezar con createUser (), que los datos puestos a UserCreateServlet:
public String createUser( UserData data) throws Exception
{
// 1. Create a URL for the target
//servlet
URL servlet = new URL(
webBase, "servlet/UserCreateServlet");
// 2. Create an array of parameter
//objects
Serializable objs[] = { data };
// 3. Execute the servlet
//transaction
ObjectInputStream in = ServletWriter.postObjects( servlet, objs);
// 4. Read and return the result value String id = (String)in.readObject(); in.close();
return id;
}
Al igual que con los servlets, la creación de otros métodos en el objeto proxy requiere principalmente de copiar y pegar este primer método. Aquí está el código para el método getUser (), que difiere del método anterior sólo en el nombre del servlet objetivo (UserGetServlet) y el tipo del objeto de retorno:
public UserData getUser(
String userid) throws Exception
{
// 1. Create a URL for the target servlet
URL servlet = new URL(webBase, "servlet/UserGetServlet");
// 2. Create an array of parameter objects
Serializable objs[] = { userid };
// 3. Execute the servlet transaction
ObjectInputStream in = ServletWriter.postObjects(
servlet, objs);
// 4. Read and return the result value UserData data = (UserData)in.readObject(); in.close();
return data;
}
Los otros métodos de su objeto proxy está construido de manera similar, por lo que no mencionarlos a todos aquí. UserDatabaseProxy.java
Cambios Applet
Una vez que tenga su objeto proxy y servlets en su lugar, todo lo que necesita hacer es actualizar el applet con el uso del objeto de proxy en lugar del objeto original RMI. Aquí de nuevo es el código original para acceder a la base de datos:
UserDatabase db; //The remote database object
...
String host = this.getCodeBase().getHost(); String name = "//" + host + "/DB1";
db = (UserDatabase)Naming.lookup(name);
el objeto RMI, es necesario crear la dirección URL del servidor web servlet (requerido por el constructor de UserDatabaseProxy). Para ello se construye una URL desde el protocolo, el nombre de host y número de puerto de la base de código del applet:
UserDatabaseProxy db; // The proxy
//database object
URL codebase = this.getCodeBase();
// Get the host, protocol, and port
String host = codebase.getHost();
String protocol = codebase.getProtocol();
int port = codebase.getPort();
// Build the URL for the servlet
//web server
URL servletBase = new URL(
protocol + "://" + host + ":" + port);
// Create the proxy object
db = new UserDatabaseProxy(servletBase);
Con el código original de RMI ha sustituido con este código proxy nuevo objeto, que está básicamente hecho con el applet cambios-sin otras modificaciones son necesarias, ya que la sintaxis de llamada de función en el objeto proxy db es la misma que para el objeto db RMI . Hiciste trampa aquí un poco a quitar importancia a manejo de excepciones, ya que su método remoto llama tirar RemoteException mientras que sus métodos de objeto proxy puede lanzar una serie de excepciones (MalformedURLException, ClassCastException, etc), pero se entiende la idea.
Eso es todo-usted hace. El applet de cliente ahora puede utilizar HTTP en lugar de RMI para comunicarse con el objeto de acceso a base de datos en el lado del servidor, y usted ni siquiera tiene que cambiar una sola línea de código de servidor existente (y sólo una media docena de líneas en el applet) . Con apenas estos pocos cambios que ha hecho el applet más compatible, su servidor más flexible, y su comunicación mucho más confiable y amigable de Internet.
Más allá de lo básico
Al modificar la aplicación de ejemplo anterior se diseñó el servlets para imitar lo que su objeto no RMI, es decir, toman un cierto número de parámetros y devolver un solo objeto de un tipo específico (o ninguno). Pero a diferencia de las llamadas a métodos RMI, los servlets pueden ser muy flexibles en la manera en que manejan y devolver datos. Los siguientes ejemplos ilustran algunas de las opciones disponibles para las aplicaciones applet / servlet / RMI.
Recepción de varios objetos de una llamada
En los cambios anteriores que ha creado un servlet que, al igual que su objeto RMI, responde a una solicitud de devolución de un applet Hashtable que puede contener un gran número de objetos. Esto funciona, pero al igual que con el original RMI
 
llamada al método, el cliente se quedará esperando el Hashtable acaba de ser devuelto. Esto evita que el applet muestre una barra de progreso o parar la operación después de sólo un subconjunto de valores se han recuperado, características muy deseables si un gran número de entradas están involucrados. (Por supuesto, usted puede obtener una lista de los objetos de uno en uno-a-tiempo, haciendo un llamado servidor separado para cada uno, pero si lo hace en miles de objetos es poco práctico.)
En lugar de esperar a que nuestro servlet para devolver un Hashtable, puede enviar las claves y valores de Hashtable que como objetos individuales. Para ver cómo, aquí está la doPost () original método de UserListServlet, que recupera la lista de usuarios de la base de la aplicación de ejemplo:
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
ObjectInputStream in = new ObjectInputStream( req.getInputStream() ); ObjectOutputStream out = new ObjectOutputStream( resp.getOutputStream() ); try {
Hashtable database =
db.listUsers();
out.writeObject(database);
} catch (
Exception e) { e.printStackTrace(
); }
// Close the I/O streams in.close();
out.close();
}
Aquí hay otra versión del servlet anterior, pero éste informa al cliente el número de entradas que hayen el valor de retorno, y luego envía el Hashtable una clave y un valor a la vez:
public void doPost( HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
{
ObjectInputStream in =
new ObjectInputStream(
req.getInputStream() );
ObjectOutputStream out =
new ObjectOutputStream(
resp.getOutputStream(
) );
try {
Hashtable database =
db.listUsers();
// Tell the client how many
//entries are coming
int num = database.size(); Integer numInt = new Integer(
num);
out.writeObject(numInt);
// Write each key and
//value of
//the Hashtable
Enumeration keys =
database.keys(); while ( keys.hasMoreElements(
) )
{
String nextKey = keys.nextElement(); UserData nextData = ( UserData)database.get(
nextKey); out.writeObject(nextKey); out.writeObject(nextData);
}
} catch (
Exception e) { e.printStackTrace(
); }
// Close the I/O streams in.close();
out.close();
}
En esta nueva versión del servlet va a enviar los datos Hashtable exactamente igual que la versión anterior, pero ahora estás dejando que el cliente sepa qué cantidad de datos que viene antes de empezar a enviarlo. Esto le da al cliente mucha más flexibilidad en el tratamiento de estos datos.
Obviamente, si usted cambia de servlet como este tiene que cambiar su objeto proxy para hacer frente a estos valores de retorno. Aquí está el código original del objeto proxy de la aplicación de ejemplo:
public Hashtable listUsers(
) throws Exception
{
servlet = new URL(webBase, "servlet/UserListServlet"); Serializable objs[] = {};
in = ServletWriter.postObjects(
servlet, objs); Hashtable database = (
Hashtable)in.readObject();
in.close();
return database;
}
Aquí hay otra versión de la misma función que recupera datos desde el servlet nuevo, y también muestra sus avances en la interfaz de usuario del applet:
public Hashtable listUsers(
java.awt.Label label) throws Exception
{
servlet = new URL(
webBase, "servlet/UserListServlet"); Serializable objs[] = {}; ObjectInputStream in = ServletWriter.postObjects(
servlet, objs);
// Initialize the return value
Hashtable database = new Hashtable(
);
// Get the number of data
//values coming
Integer numInt = ( Integer)in.readObject();
int num = numInt.intValue();
// Read each key/value pair and
//add to the Hashtable
for (int x = 1; x <= num; x++)
{
label.setText(
"Getting data (
" + x + "/" + num + ")..."); String nextKey = (
String)in.readObject();
UserData nextData = ( UserData) in.readObject();
database.put(
nextKey, nextData);
}
in.close();
return database;
}
Cuando el applet se ejecuta esta versión de Listusers (), el nombre dado muestra el número total de entradas y el número actual se leerá (por ejemplo, "Obtención de datos (7/26) ...").
La combinación de Servlets
Otro de los cambios que puede realizar en la aplicación de ejemplo es el de consolidar todas sus servlets en un servlet individual, con todos los métodos RMI llama contenido en un solo doPost () método. Esto complica el servlet, ya que se necesita para cambiar entre varios bloques de código basado en la operación a realizar, sino que simplifica la implementación, eliminando la necesidad de múltiples servlets.
He aquí un servlet que implementa toda la base de datos del método que llama a sus cinco servlets, utilizando un código entero enviado por el applet para determinar la acción apropiada:
public class UniversalServlet extends DatabaseServlet
{
static final int LIST	= 0; static final int GET	 = 1; static final int CREATE = 2; static final int EDIT	 = 3; static final int DELETE = 4;
public void doPost( HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
{
// Open the I/O streams
ObjectInputStream in =
new ObjectInputStream(
req.getInputStream() ); ObjectOutputStream out = new ObjectOutputStream( resp.getOutputStream(
) );
try {
// Find out which operation to perform
Integer codeInt = ( Integer)in.readObject();
int code =
codeInt.intValue();
if (code == LIST) { Hashtable database = db.listUsers(); out.writeObject(database);
}
else if (code == GET) { String id = ( String)in.readObject(); UserData data = db.getUser(id); out.writeObject(data);
}
else if (
code == CREATE) { UserData data = (
UserData)in.readObject();
String id =
db.createUser( data); out.writeObject(id);
}
else if (code == EDIT) { String id = (
String)in.readObject();
UserData data = ( UserData)in.readObject(); db.editUser(id, data);
}
else if (code == DELETE) { String id = (
String)in.readObject();
db.deleteUser(id);
}
} catch (
Exception e) { e.printStackTrace(
); }
// Close the I/O streams in.close();
out.close();
}
}
Observe que en este nuevo código servlet que los parámetros para cada operación son de sólo lectura después de que el código se lee entero y determinar qué bloque de código a ejecutar. A diferencia de los servlets anteriores, no se puede leer todos los parámetros de la corriente de entrada al comienzo de este doPost (), ya que no se conoce el número y tipo de parámetros a leer hasta después de saber qué operación se debe ejecutar. Para utilizar este servlet es necesario cambiar todos los métodos de su objeto proxy en consecuencia, utilizando un código entero para especificar la operación a realizar. Por ejemplo, he aquí una versión de createUser el objeto de proxy () del método que utiliza el UniversalServlet arriba:
public String createUser( UserData data) throws Exception
{
URL servlet = new URL(
webBase, "servlet/UniversalServlet");
Integer code =
new Integer(2); //CREATE Serializable objs[] =
{ code, data };
ObjectInputStream in = ServletWriter.postObjects(
servlet, objs);
String id = ( String)in.readObject(); in.close();
return id;
}
La única modificación que hemos hecho aquí es crear un entero que corresponde a la operación realizada (en este caso, la creación de una entrada de base de datos) y añadir este entero a la matriz de parámetros enviados
al servlet.
Manejo de varios tipos de objetos
También se puede construir el applet para dar cabida a diferentes tipos de objetos en respuesta a la llamada de servlet mismo. Por ejemplo, si al recuperar información de su base de datos se encuentra con un servlet RemoteException, es posible que su applet para recibir la excepción. Para ello, puede escribir el objeto RemoteException a ObjectOutputStream del servlet en lugar del valor de retorno que estabas esperando. Aunque esto podría causar una ClassCastException que se produzca en el objeto proxy cuando intentó leer el valor de retorno, puede utilizar instanceof para determinar si la salida del servlet el valor de retorno o una excepción, como en este ejemplo:
public UserData getUser(
String userid) throws Exception
{
URL servlet = new URL(webBase, "servlet/UserGetServlet");
Serializable objs[] = { userid };
objectInputStream in = ServletWriter.postObjects( servlet, objs);
// Read the value as an Object
Object obj = in.readObject();
// Is it an Exception?
if (obj instanceof Exception) {
throw (Exception)obj;
}
else {
UserData data = ( UserData)obj; in.close();
return data;
}
}
Aquí usted está leyendo en el valor de retorno desde el servlet como tipo Object
y luego utilizar instanceof para determinar si se trata de una excepción. Si es así, lo lanzas como una excepción y echarlo a permitir que el acuerdo applet con él como
necesario, de lo contrario, se asume el valor devuelto es un objeto UserData,
proyectarlo como tal, y devolverlo a la applet.
conclusión
Como se puede ver, las opciones para trabajar con applet / servlet comunicación son mucho mayores que cuando se utiliza RMI en el applet, los ejemplos que se muestran aquí son sólo un comienzo. Al insertar otra capa en su solicitud puede tardar un poco más trabajo, la recompensa es un applet que es más compatible, comunicación cliente / servidor que es más flexible, y una aplicación de servidor RMI que es más fiable.

Otros materiales