Un ejemplo completo y funcional, con el código fuente tanto del servidor como del cliente, de una mini tienda online. La aplicación, desarrollada en Java, usando RMI, inicia un servicio RMI en el servidor, los clientes se conectarán a este servidor para iniciar sesión (si no existe el usuario la aplicación lo creará) y poder gestionar usuarios y productos de forma remota (listar, crear, eliminar, etc.). Se utilizará la serialización de Java y se almacenarán los objetos Java en persistencia con JSON.

Ejemplo Java para realizar una tienda online con productos y usuarios en cliente/servidor

Se pide crear un servidor Java que permita ejecutar los siguientes métodos remotos:

  • inicioSesionUsuario: se le pasará como parámetro el nombre de usuario y la contraseña y devolverá un boolean indicando si el usuario y contraseña es correcta.
  • altaUsuario: insertará un nuevo usuario en el sistema. Se le pasará un objeto Usuario como parámetro.
  • listarUsuarios: devolverá una lista de objetos Usuario con todos los usuarios del sistema.
  • altaProducto: insertará un nuevo producto en el sistema. Se le pasará un objeto Producto como parámetro.
  • listarProductos: devolverá una lista de objetos Producto con todos los productos del sistema.
  • eliminarProducto: elimina un producto del sistema. Se le pasará el código del producto a eliminar como parámetro. Devolverá un boolean, indicando se se ha eliminado el producto
  • nombreServidor: método remoto que únicamente devuelve un string desde el servidor. Se utilizará para hacer un test de la conexión RMI.

Se pretende simular una «mini» tienda online, en la que podremos iniciar sesión con un usuario y contraseña y listar los productos de la tienda, añadir nuevos productos y eliminar productos. También podremos listar los usuarios de acceso a la aplicación y añadir nuevos usuarios.

La persistencia se realizará en JSON. Se almacenarán los productos en el fichero productos.json y los usuarios en el fichero usuarios.json.

Para almacenar la contraseña del usuario se usará el hash MD5, en lugar de almacenar la contraseña en texto plano. De esta forma, aunque se tenga acceso al fichero usuarios.json, las contraseñas están codificadas y no pueden ser leídas.

Para realizar la tienda virtual se usará programación distribuida. En el caso de Java, usaremos RMI, teniendo un equipo que hará de servidor y el resto de clientes que se conectarán a este servidor. Por lo tanto desarrollaremos dos aplicaciones, una Servidor y otra Cliente, separadas. En la aplicación servidor definiremos los métodos y clases para iniciar el servidor RMI y los métodos para la persistencia y manipulación de datos. Dichos métodos serán remotos, de forma que podrán ejecutarse desde los clientes RMI. Por parte de la aplicación Cliente, se implementarán los métodos de conexión con el servidor RMI y de ejecución de los métodos remotos del servidor para manipulación de datos y gestión de la tienda virtual.

Requisitos para desarrollar la aplicación Java RMI Tienda Online con persistencia en JSON

En el siguiente artículo explicamos lo que es RMI de Java y un ejemplo sencillo de su implementación:

Los únicos requisitos para desarrollar en Java son:

  • Disponer de un editor de textos plano, o bien de un IDE de desarrollo avanzado. En nuestro caso usaremos IntelliJ IDEA.
  • Para la compilación de la aplicación necesitaremos disponer de Java instalado, bien sea un equipo con Windows, Linux o macOS.

Todo ello, incluso la apertura de puertos y el mapeo si queremos acceder desde fuera de nuestra organización, desde Internet, por RMI, al servidor, está explicado en el artículo anterior.

Crear clases necesarias para la aplicación Servidor en Java RMI

Para la conexión cliente/servidor usaremos RMI, como hemos comentado. Definiremos una aplicación servidor que será la encargada de iniciar el registro RMI en el puerto indicado. En la aplicación servidor tendremos una interfaz remota (Remote), llamada TiendaOnlineRMI, para definir los métodos remotos (los que podrá invocar el cliente), con los métodos indicados anteriormente: inicioSesionUsuario, altaUsuario, listarUsuarios, altaProducto, listarProductos, eliminarProducto y nombreServidor.
La implementación de esta interfaz remota la realizaremos en la clase TiendaOnlineRMIImpl, que contendrá los métodos remotos y su código asociado. Será la que acceda al repositorio JSON para invocar a los métodos de acceso a datos (alta de usuarios, listado de usuarios, alta de productos, listado de productos y baja de productos).

En la interfaz RepositorioBD del servidor se definirán todos los métodos de acceso a datos, al repositorio:

  • altaUsuario: añadir un nuevo usuario.
  • obtenerUsuario: obtener un usuario por su nombre de usuario.
  • obtenerUsuarios: obtener todos los usuarios de la tienda.
  • altaProducto: añadir un nuevo producto a la tienda.
  • obtenerProductos: obtener todos los productos de la tienda.
  • eliminarProducto: eliminar un producto de la lista por su código.

El código fuente de los métodos anteriores se definirá en la clase JsonBDRepositorio que será una implementación de la interfaz RepositorioBD. Para la persistencia de los datos en JSON usaremos el paquete Java de Goolge Gson, que permite serializar y deserializar objetos Java en JSON. Por lo tanto necesitaremos añadir el import correspondiente en la clase JsonBDRepositorio:

import com.google.gson.Gson;

Y disponer de dicha biblioteca Gson, que incluimos en la descarga del ejemplo completo de este artículo.

El servidor contará con una clase principal Main, que será la encargada de solicitar la IP o nombre DNS y el puerto donde se ejecutará y registrará el objeto Registry RMI, para permanecer a la escucha de la conexión de los clientes.

Por último, para el servidor, contendrá dos clases con los atributos, setters y getters de los objetos Usuario (Usuario.java) y Producto (Producto.java), que serán los que serialicemos y almacenemos en JSON. Para ello se creará la clase Producto (serializable), con los atributos:

  • codigo: identificador del producto.
  • nombre: nombre del producto.
  • precio: precio del producto.

Se establecerán los setters y getters correspondientes para los atributos anteriores.

Para la clase Usuario, tendremos los atributos:

  • nombe: nombre del usuario con el que se iniciará sesión (nick).
  • contraseña: contraseña del usuario, se almacenará el hash MD5.
  • email: cuenta de correo electrónico del usuario.
  • teléfono: teléfono del usuario.

A continuación mostramos el código fuente completo de cada clase descrita anteriormente para la parte del Servidor RMI. Las partes importantes del código van comentadas con su utilidad y explicación.

  • Producto.java:
  • Usuario.java:
  • RepositorioBD.java:
  • JsonBDRepositorio.java:
  • TiendaOnlineRMI.java:
  • TiendaOnlineRMIImpl.java:
  • Main.java:

Crear clases necesarias para la aplicación Cliente en Java RMI

Por otra parte, crearemos un nuevo proyecto que será el cliente, el que conectará mediante RMI al servidor para usar sus métodos remotos. Contendrá una copia del modelo de datos serializado del servidor (Producto y Usuario), para el envío de datos.

El cliente tendrá una implementación de la interfaz remota TiendaOnlineRMI, con los métodos del servidor: inicioSesionUsuario, altaUsuario, listarUsuarios, altaProducto, listarProductos, eliminarProducto y nombreServidor, que será idéntica a la definida en el servodor. En esta implementación NO se desarrollarán los métodos, no tendrán código, únicamente su definición, dado que el código se establece en la parte del servidor.

En la clase principal Cliente, se agregarán todos los métodos para conectar con el servidor (solicitando al usuario la IP y el puerto), se usarán los métodos remotos para el inicio de sesión del usuario en la tienda online y se mostrarán los diferentes menús al usuario: menú principal, gestión de productos y gestión de usuarios.

El cliente tendrá una copia de las clases serializables Producto.java y Usuario.java del servidor, que serán idénticas a las del servidor.

A continuación mostramos el código fuente completo de cada clase descrita anteriormente para la parte del Cliente RMI. Las partes importantes del código van comentadas con su utilidad y explicación.

  • Producto.java: idéntico al del servidor.
  • Usuario.java: idéntico al del servidor.
  • TiendaOnlineRMI.java: idéntico al del servidor.
  • Cliente.java:

Compilar el cliente y el servidor desde IntelliJ IDEA para generar los .class y ejecutar en sistema operativo

Compilar aplicaciones servidor y cliente para generar los .class

Para probar las aplicaciones servidor y cliente las compilaremos desde IntelliJ IDEA (o cualquier otro compilador Java) y generaremos los .class. Para ello, abriremos el proyecto Servidor RMI, editaremos la configuración de la aplicación:

Compilar aplicaciones servidor y cliente para generar los .class

Revisaremos los datos de configuración y compilación de la aplicación. Anotaremos la versión de java que usaremos para la compilación, dado que en los equipos donde ejecutemos la aplicación deberemos tener una versión igual o superior:

Compilar aplicaciones servidor y cliente para generar los .class

Una vez revisado todo y establecida la clase principal Main, compilaremos el proyecto, desde el menú «Build» – «Build Project»:

Compilar aplicaciones servidor y cliente para generar los .class

Nos habrá generado los .class correspondientes en la carpeta …out\production…:

Compilar aplicaciones servidor y cliente para generar los .class

Realizaremos el mismo proceso para la aplicación cliente, que nos generará sus correspondientes .class:

Compilar aplicaciones servidor y cliente para generar los .class

Ejecutar aplicación Servidor RMI

Para la ejecución en otro equipo, copiaremos cada carpeta TiendaOnlineClientePruebas y TiendaOnlineServer los equipos en los que queramos ejecutar el servidor y el cliente, pueden estar en el mismo equipo o en diferentes equipos. Lo habitual en una arquitectura cliente/servidor, en una aplicación de producción, es que el programa que será el servidor RMI se encuentre en un equipo y el resto de equipos ejecutarán el programa cliente para conectarse al servidor. Será en el servidor donde se almacenen los datos (persistencia), en nuestro caso los fichreos usuarios.json y productos.json:

Ejecutar aplicación Servidor RMI

El equipo servidor, si optamos por un equipo con sistema operativo Windows, tendremos que abrir el puerto que usemos para RMI en el cortafuegos de Windows (o el cortafuegos del sistema antivirus que usemos):

Ejecutar aplicación Servidor RMI

Si ejecutamos el servidor en un equipo Linux, también tendremos que abrir el puerto RMI que usemos en el cortafuegos. Por ejemplo usando el comando:

firewall-cmd –zone=public –add-port=1099/tcp –permanent

(variará en función de la distribución de Linux y del sistema de cortafuegos que usemos)

Para ejecutar el servidor desde la línea de comandos (fuera del IDE IntellyJ IDEA) seguiremos los siguientes pasos. En primer lugar nos aseguraremos de disponer de la carpeta:

Servidor\out\production\TiendaOnlineServer\com\google\gson

Dicha carpeta contendrá los ficheros tras descomprimir el JAR de Gson gson-2.2.2.jar. Todos los ficheros se incluyen en la descarga del ejemplo de este artículo.

Para ejecutar el servidor, en un equipo Windows, desde una ventana de consola MS-DOS, accederemos a la carpeta donde tengamos la versión de Java que queramos usar para ejecutar la aplicación, en nuestro caso en la carpeta:

cd C:\Users\alonso\.jdks\openjdk-15.0.1-1\bin

La versión de Java debe ser igual o superior a la que se usó para generar los .class. Ejecutaremos el siguiente comando para abrir la aplicación servidor:

java.exe -Djava.security.manager -Djava.security.policy=D:\ProyectoA_Java\ProyectoA_Tienda_Online_Java_RMI\Servidor\java.policy -classpath D:\ProyectoA_Java\ProyectoA_Tienda_Online_Java_RMI\Servidor\out\production\TiendaOnlineServer com.main.Main

Teniendo en cuenta que en nuestro caso la aplicación servidor se encuentra en la carpeta:

D:\ProyectoA_Java\ProyectoA_Tienda_Online_Java_RMI\Servidor

Donde también se incluye el fichero java.policy con el siguiente contenido:

Si todo es correcto se iniciará la aplicación servidor, nos solicitará una IP o nombre DNS (normalmente la misma IP del equipo donde lo estamos ejecutando) y un puerto (el defecto para RMI es el 1099). Introduciremos estos datos y la aplicación iniciará el servicio RMI y quedará a la espera de conexión de clientes. NO recomendamos usar «localhost» para la IP, dado que los clientes necesitarán registrar la IP o nombre DNS del equipo servidor y si usamos «localhost» no lo encontrarán. Por ello, aunque el registro RMI se haga en el mismo equipo desde el que se ejecuta la aplicación servidor (que es lo normal), introduciremos su IP y NO «localhost», en nuestro caso 192.168.1.2:

Ejecutar aplicación Servidor RMI

Si el puerto establecido está libre y el servirio Registry RMI de Java se ha ejecutado correctamente, se iniciará la aplicación servidor y nos mostrará el log de su ejecución en la consola, como vemos en la imagen anterior:

Ejecutar aplicación Cliente RMI

Para el caso de la aplicación cliente, al igual que para el servidor, nos aseguraremos de disponer de la misma versión de Java o superior, y ejecutaremos el siguiente comando (desde una ventana de MS-DOS si estamos en un equipo Windows o desde la línea de comandos de una ventana de terminal si estamos en Linux o macOS). Para equipos Windows ejecutaríamos:

java.exe -Djava.security.manager -Djava.security.policy=D:\ProyectoA_Java\ProyectoA_Tienda_Online_Java_RMI\Cliente\java.policy -classpath D:\ProyectoA_Java\ProyectoA_Tienda_Online_Java_RMI\Cliente\out\production\TiendaOnlineClientePruebas com.main.Cliente

Teniendo en cuenta que en nuestro caso la aplicación cliente se encuentra en la carpeta:

D:\ProyectoA_Java\ProyectoA_Tienda_Online_Java_RMI\Cliente

El fichero java.policy también debe existir en la carpeta indicada y con el mismo contenido que el de la aplicación servidor.

Para equipos Linux o macOS ejecutaremos:

java -Djava.security.manager -Djava.security.policy=/tmp/TiendaOnlineClientePruebas/java.policy -classpath /tmp/TiendaOnlineClientePruebas com.main.Cliente

Teniendo en cuenta que la carpeta de la aplicación cliente se encuentra en /tmp/TiendaOnlineClientePruebas.

Si la aplicación cliente se ejecuta correctamente, nos solicitará la IP del servidor y su puerto de conexión. Introduciremos estos datos. Si conecta con el servidor nos preguntará si tenemos usuario registrado. Si respondemos «S» (sí) nos pedirá el usuario y la contraseña, si repondemos «N» (no) nos llevará al menú de alta de nuevo usuario. Más adelante mostramos el funcionamiento completo de la aplicación:

Ejecutar aplicación Cliente RMI

Vemos que gracias a la versatilidad de Java, la aplicación puede funcionar en casi cualquier sistema operativo. Es indiferente desde qué sistema operativo ejecutemos tanto el servidor como el cliente. En el equipo hemos ejecutado el servidor en Windows y un cliente en Linux, funcionando perfectamente.

Descarga del código fuente completo en Java con el IDE IntelliJ IDEA

A continuación dejamos enlace para la descarga completa tanto del Cliente como del Servidor (desarrolado en Java con el IDE IntelliJ IDEA):

La aplicación Tienda Online Java RMI en funcionamiento

Vamos a mostrar el funcionamiento completo de la aplicación cliente/servidor (programación distribuida) en Java RMI.

La parte del servidor es bastante sencilla, como hemos visto anteriormente, únicamente nos solicitará la IP y el puerto y quedará a la espera de conexiones de clientes. Además, desde la consola del servidor, se irán mostrando datos de acciones que los clientes van realizado (a modo de depuración):

La aplicación Tienda Online Java RMI en funcionamiento

Y en el servidor, en la carpeta …TiendaOnlineServer\com\main\bd, se almacenarán los ficheros de la persistencia de los obejetos serializables productos y usuarios:

La aplicación Tienda Online Java RMI en funcionamiento

La aplicación cliente, al ejecutarla, nos solicitará IP y puerto del servidor, como hemos indicado anteriormente. Una vez conectados, nos preguntará si tenemos ususuario registrado. Si lo tenemos, nos solicitará usuario y contraseña para iniciar sesión. Si las credenciales son correctas nos mostrará el menú principal de la aplicación:

La aplicación Tienda Online Java RMI en funcionamiento

Si no tenemos usuario registrado, nos mostrará el menú de alta de usuario. Desde este menú nos solicitará un nombre de usuario (comprobará que sea alfanumérico), una contraseña (comprobará que tenga una cierta fortaleza), una dirección de correo electrónico (comprobará que esté bien formada), la confirmación de la dirección de correo y un teléfono. Si el nombre de usuario no existen en la base de datos nos creará este nuevo usuario y nos llevará al menú de inicio de sesión:

La aplicación Tienda Online Java RMI en funcionamiento

Desde el menú principal, si pulsamos 1 e INTRO, nos llevará al menú de gestión de productos:

La aplicación Tienda Online Java RMI en funcionamiento

Desde aquí podremos mostrar todos los productos de la base de datos, pulsando «1»:

La aplicación Tienda Online Java RMI en funcionamiento

Eligiendo la opción «2» podremos añadir un nuevo producto a la tienda:

La aplicación Tienda Online Java RMI en funcionamiento

Y eligiendo la opción «3» podremos eliminar un producto existente (sabiendo su código):

La aplicación Tienda Online Java RMI en funcionamiento

Para los usuarios tendremos las mismas opciones que para los productos, salvo la de eliminar:

La aplicación Tienda Online Java RMI en funcionamiento

Con la opción «1» la aplicación nos permitirá dar de alta un nuevo usuario para inicio de sesión en el sistema. En caso de existir un usuario con el mismo nombre, la aplicación nos solicitará un nombre de usuario, hasta que esté «libre»:

La aplicación Tienda Online Java RMI en funcionamiento

Y también podremos consultar el listado de usuarios de la tienda:

La aplicación Tienda Online Java RMI en funcionamiento

Algunos errores y su posible solución

Si al intentar ejecutar la aplicación (sea la cliente o la servidor) aparece el siguiente error:

Error: Se ha producido un error de enlace al cargar la clase principal com.main.Cliente
java.lang.UnsupportedClassVersionError: com/main/Cliente has been compiled by a more recent version of the Java Runtime (class file version 59.0), this version of the Java Runtime only recognizes class file versions up to 55.0

Habrá que instalar una versión de Java superior a la que tenemos en el equipo. Por ejemplo, para un equipo con Linux CentOS 7 (válido para Red Hat o Fedora) seguiremos estos pasos para actualizar la versión de Java:

1º para comprobar la versión actual de Java podemos ejecutar el comando:

java –version

2º Para actualizar el sistema podemos ejecutar el comando

yum -y update

3º Para descargar la versión jdk 16.0.2 podemos ejecutar el comando:

wget –no-check-certificate -c –header «Cookie: oraclelicense=accept-securebackup-cookie» https://download.oracle.com/otn-pub/java/jdk/16.0.2+7/d4a915d82b4c4fbb9bde534da945d746/jdk-16.0.2_linux-x64_bin.rpm

4º Para instalar el rmp descargado podemos ejecutar el comando:

rpm -ivh jdk-16.0.2_linux-x64_bin.rpm

5º Y por último, para establecer la nueva versión de Java instalada como la versión principal a usar en el sistema, podemos ejecutar el comando:

alternatives –config java

Elegiremos la versión instalada en /usr/java/jdk-16.0.2/bin/java como la versión principal. Pero antes de elegir esta versión, conviene que nos aseguremos de que ninguna otra aplicación Java de nuestro equipo requiera la versión anterior para su funcionamiento.

Algunos errores y su posible solución