Implementacion de suma de cadenas parte II

Como mejora de la implementacion anterior y estudio de C#, sobretodo en las mejoras incoorporadas en la version 4, me decidi a implementar una nueva version de esta suma. Basicamente los cambios agregados fue la suma por bloques y la suma de ceros, que pienso yo que reduce la cantidad de operaciones, por lo menos unas 19 veces ya que es el bloque de numeros que toma.

Habria que hacer un profundo analisis en revisar la cantidad de codigo usado si este mejora el performance del algoritmo.

StringInteger.cs

...
        public static readonly int MAX_ULONG_LENGTH = ulong.MaxValue.ToString().Length;
...
        public static StringInteger operator +(StringInteger lhs, StringInteger rhs)
        {
            //Validaciones

            String tmpX = lhs.Container, tmpY = rhs.Container;
            ulong x, y, result = 0;
            int limit = lhs.Container.Length > rhs.Container.Length ? lhs.Container.Length : rhs.Container.Length;
            StringBuilder tmp = new StringBuilder(limit);
            do
            {
                if (IsTheOnlyOperand(ref tmpX, ref tmpY, (int)result))
                {
                    result = 0;
                    tmp.Insert(0, tmpX + tmpY);
                }
                else
                {
                    x = RetrieveNextNumber(ref tmpX);
                    y = RetrieveNextNumber(ref tmpY);

                    result += x + y;
                    if (result.ToString().Length == MAX_ULONG_LENGTH)
                    {
                        tmp.Insert(0, result.ToString().Substring(1));
                        result = 1;
                    }
                    else
                    {
                        tmp.Insert(0, result);
                        result = 0;
                    }
                }
            } while (!(string.IsNullOrEmpty(tmpX) && string.IsNullOrEmpty(tmpY)));
            tmp.Insert(0, result);

            return new StringInteger(tmp.ToString());
        }//operator+

        private static bool IsTheOnlyOperand(ref string firstOperand, ref string secondOperand, int carry)
        {
            bool response = false;
            firstOperand = SanitizeStringInput(firstOperand);
            secondOperand = SanitizeStringInput(secondOperand);

            if (string.IsNullOrEmpty(firstOperand) && !string.IsNullOrEmpty(secondOperand))
            {

                secondOperand = secondOperand.Substring(0, secondOperand.Length - 1) +
                    Convert.ToChar(Convert.ToInt32(secondOperand[secondOperand.Length - 1]) + carry);
                response = true;
            }
            else if (string.IsNullOrEmpty(secondOperand) && !string.IsNullOrEmpty(firstOperand))
            {
                firstOperand = firstOperand.Substring(0, firstOperand.Length - 1) +
                    Convert.ToChar(Convert.ToInt32(firstOperand[firstOperand.Length - 1]) + carry);
                response = true;
            }

            return response;
        }//IsTheOnlyOperand

        private static string SanitizeStringInput(string input)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }

            input = input.TrimStart('0').Trim();
            if ("".Equals(input))
            {
                input = "0";
            }

            Regex pattern = new Regex(@"^d+$");
            if (!pattern.IsMatch(input))
            {
                throw new ArgumentException("The string value " + input + " is not a integer value");
            }

            return input;
        }//SanitizeStringInput

        private static ulong RetrieveNextNumber(ref string stringNumber)
        {
            int length = stringNumber.Length > MAX_ULONG_LENGTH - 1 ? MAX_ULONG_LENGTH - 1 : stringNumber.Length;
            int index = stringNumber.Length - length;
            ulong number = length > 0 ? ulong.Parse(stringNumber.Substring(index, length)) : 0;
            stringNumber = stringNumber.Remove(index, length);

            return number;
        }//RetrieveNextNumber

Implementacion de suma de cadenas

Continuando con la serie de operaciones con numeros grandes, desarrollo la implementacion de una suma de valores contenidos en una cadena, la cual nos puede evitar el desboramiento de bufer. Realizando un ligero analisis encontramos que seguimos limitados por el manejo de indices de arreglos, ya que solo nos permite indexarlos con numeros enteros.

Suma.java

...
	private static String sumar(String a, String b) {
		//Validaciones
...
		int x, y, result = 0;
		int limite = a.length() > b.length() ? a.length() : b.length();
		char[] tmp = new char[limite + 1];
		for (int i = 1; i <= limite; i++){
			x = i <= a.length() ? a.charAt(a.length() - i) - '0' : 0;
			y = i <= b.length() ? b.charAt(b.length() - i) - '0' : 0;
			result += x + y;
			tmp[tmp.length - i] = (char) ((result % 10) + '0');
			result /= 10;
		}

		if ( result > 0){
			tmp[0] = (char) ((result % 10) + '0');
			return String.valueOf(tmp);
		}
		return String.valueOf(tmp, 1, tmp.length -1);
	}

Implementacion de multiplicacion ultizando arreglos

Esta implementacion surgio de la necesidad de implementar una solucion para la multiplicacion de numeros grandes, me refiero a numeros que su resultado causaran el desboramiento de bufer. Mi propuesta se baso en la utilizacion de cadenas para su representacion y manejo de datos, pienso que la solucion que encontre es relativamente buena y quizas tenga muchos puntos a mejorar, lo cual se aceptan sugerencias.

Quiza una de las mayores limitantes que tengo es que el tipo de valor para el indice de los arreglos solo puede ser entero, esto reduce la cantidad de digitos con los cuales puedo trabajar.

Multiplicacion.java

...
	private static String multiplicar(String a, String b) {
		//Validaciones
...		
		StringBuffer result = new StringBuffer();
		int x = a.length();
		int y = b.length();
		int i;
		int j;
		long[][] results = new long[x][y];
		List sum = new LinkedList();
		long tmp;

		for (i = 0; i < x; i++) {
			for (j = 0; j < y; j++) {
				results[i][j] = (a.charAt(i) - '0') * (b.charAt(j) - '0');
			}
		}

		x--;
		y--;
		tmp = 0;

		while (true) {
			j = y;
			i = x;
			while (j >= 0 && i < a.length()) {
				tmp += results[i][j];
				i++;
				j--;
			}
			sum.add(tmp % 10);
			tmp /= 10;

			if (x > 0) {
				x--;
			} else if (y > 0) {
				y--;
			} else {
				if(tmp > 0){
					sum.add(tmp);
				}
				break;
			}
		}
		Collections.reverse(sum);
		for (long l : sum) {
			result.append(l);
		}

		return result.toString();
	}

Memristor

Simbolo Electrico
Simbolo Electrico

Es un elemento de circuito pasivo. Ha sido descrito como el cuarto elemento de los circuitos pasivos, junto con el capacitor, la resistencia y el inductor; su nombre es una palabra compuesta de memory resistor (resistencia-memoria). Y se cree que puede llevar a la creación de circuitos analógicos que podrían pensar como el cerebro humano.

El memristor fue predicho y descrito en 1971 por Leon Chua, de la Universidad de California, Berkeley. Durante 37 años, fue un dispositivo hipotético, hasta que en abril de 2008, una implementación física fue divulgada por un equipo de investigadores de HP Labs.

El concepto general es una matriz de cables (conductores), entre cada par de conductores de toda la matriz, existen llaves (interruptores) que en primera instancia se encuentran abiertos. Si conectamos una fuente de alimentación entre un par de conductores, al principio esta disposición no almacena o posee información alguna, pero si empezamos a cerrar algunos de estos interruptores podemos programar la matriz para que contenga una cantidad impresionante de información. Se puede abrir o cerrar cualquier interruptor particular, si queremos manejar otro interruptor solo debemos cambiar la conexión de la fuente.

Teóricamente se puede reemplazar 10 transistores con tan solo 1 memristor, las implicancias de esto son abismales, en modo simplista podríamos decir que aumentaríamos en 10 veces la capacidad de todos los circuitos electrónicos, y con ello las PC, celulares, tablets, etc

Los memristors de estado sólido de Williams pueden ser combinados para formar transistores, aunque son mucho más pequeños. Pueden también ser formados como memoria de estado sólido no volátil, que permitiría una mayor densidad de datos que los discos duros con tiempos de acceso similares a la DRAM, sustituyendo ambos componentes. Además, al ser un dispositivo analógico, no solo podría almacenar bits («1″s y «0»s), sino bytes o cadenas de bytes en el mismo espacio, solamente mejorando el dispositivo de control del memristor. Esto ofrece un futuro muy prometedor a largo plazo.

Hewlett Packard presenta los chips del futuro

RESTful (REpresentational State Transfer)

La Transferencia de Estado Representacional o REST, termino publicado por de Roy Fielding (uno de los principales autores de la especificación del protocolo HTTP) en el año 2000, el termino por si solo no define una arquitectura, sino una serie de reglas o restricciones que, cuando son aplicadas al diseño de un sistema, este crea un estilo de arquitectura. Despues de evaluar todos los recursos en la red y tecnologias disponibles para crear sistemas distribuidos y viendo que sin alguna restriccion, era dificil el mantenimiento y desarrollo de aplicaciones; Fielding definio una serie de restricciones definiendo el sistema RESTful:

  • Debe ser un sistema cliente-servidor
  • Debe ser stateless, es decir, cada peticion debe ser independiente de las otras.
  • Tiene que soportar un sistema de caching.
  • Debe ser accesible uniformemente, cada recurso debe tener una unica direccion y un punto de acceso valido.
  • Debe estar diseñado en capas para soportar escabilidad.
  • Debe proveer codigo bajo demanda, esta es una restriccion opcional.

Estas restricciones no dictan que tipo de tecnologia usar, solo definen como los datos son transferidos entre componentes y sus beneficios. Actualmente contamos con un ejemplo claro de un sistema RESTful funcionando, la Web. Antes que nada debemos puntualizar que significa que la Web sea RESTful, mas particularmente la web estatica, dicha web se apega a las restricciones de RESTful, su infraestructura web actual ofrece sistemas de cacheo, conexion stateless, y unicos hyperlinks para obtener recursos, donde los recursos son todos los documentos disponibles en cada sitio, siendo sus representaciones legibles por el explorador; por lo tanto la web estatica esta construida sobre la arquitectura de REST.

Por otra parte las aplicaciones web dinamicas no siempre son RESTful, porque pueden no cumplir con alguna restriccion.

Para conocer mas acerca de la arquitectura de RESTful necesitamos comprender que son los recursos y sus representaciones, definiendo un recurso como cualquier cosa direccionable sobre la web, entendiendo por direccionable, como aquellos recursos que pueden ser accedidos y trasferidos entre cliente y servidor. Siendo el mapeo de un recurso unico, diferentes peticiones sobre el mismo pueden regresar la misma representacion binaria guardada en el servidor. Por otra parte, la representacion de un recurso, es el estado temporal de la informacion actual que es enviado de vuelta hacia los clientes, por lo tanto una representacion puede tomar varias formas, como una imagen, un archivo de texto, o un stream de XML o JSON, pero todos deben estar disponibles bajo la misma URI.

Deshabilitar warnings en C#

Durante la vida de cualquier proyecto, incluso en la vida diaria de cada programador, se debería tener como meta que el código creado no genere warnings, ya que dichos warnings fueron creados con el propósito de prevenir futuros errores o evitar confusiones en el código, siempre hay que pensar que el código que escribas hoy, podrá ser leído, modificado, corregido o aumentado el día de mañana por lo que se recomienda prestar atención a cada warning generado.

Por otra parte, y como toda regla, existen excepciones donde por una parte, no se recomienda modificar las reglas impuestas por el compilador ni tampoco el ignorar los warnings de manera tal que con el tiempo se acumulen. Para esos casos aislados, en los cuales necesitamos deshabilitar los warnings creados existe la instrucción #pragma warning que deshabilita la generación de warnings por parte del compilador; a continuación escribo un ejemplo de su uso.

ProgramExample.cs

namespace Electrocucaracha.Warnings
{
    class ProgramExample
    {
#pragma warning disable
        private int unusedDisable;

#pragma warning restore
        private int unusedEnable;

        static void Main(string[] args)
        {
        }
    }
}

Mostrar los valores de los parámetros de Hibernate

Una propiedad de Hibernate la cual es muy útil y que nos permite visualizar las consultas realizadas sobre la base de datos, es show_sql la cual al activarse nos muestra las consultas HQL realizadas sobre la base de datos.

hibernate.properties

hibernate.show_sql = true

Aunque es una propiedad muy util, muchas veces es necesario conocer los valores que son enviados o recibidos por la base de datos; en muchos de estos servidores es posible configurarlos para generar trazas de las consultas realizadas, el problema de hacerlo de esta forma, es que para una base de datos muy concurrida, aun siendo de desarrollo, resulta complicado encontrar la consulta realizada.

Para obtener los valores enviados podemos utilizar dos frameworks, Log4j y P6Spy.

Cuando utilizamos el framework de Log4j solo se requiere la modificación de su configuración añadiendo una propiedad.

log4j.properties

log4j.logger.org.hibernate.type=trace

Lo cual nos muestra, de forma separada, el tipo y valor de cada parámetro enviado en la consulta.

Output

13:20:43,281 TRACE StringType:153 - binding 'root' to parameter: 1
13:20:43,296 TRACE BooleanType:153 - binding 'true' to parameter: 2

Otra forma y quizás mas elegante de mostrar las consultas que se realizan es utilizando P6Spy, para lo cual es necesario configurarla, con los siguientes pasos:

  • Descargar la ultima version de p6spy-install.zip de su sitio oficial.
  • Descomprimir y copiar en el proyecto los archivos p6spy.jar y spy.properties.
  • Realizar las configuraciones necesarias para incluir los jars en el proyecto.
  • Modificar la configuración de la base de datos. Remplazando el driver utilizado por com.p6spy.engine.spy.P6SpyDriver.

hibernate.properties

#hibernate.connection.driver_class = com.mysql.jdbc.Driver
hibernate.connection.driver_class = com.p6spy.engine.spy.P6SpyDriver

  • Por ultimo, es necesario modificar el archivo spy.properties, agregando el driver class que fue remplazado y, en caso de que estemos utilizando Log4j, utilizar su appender. Y quizás sea necesario que elimine del registro los drivers.

spy.properties

# mysql Connector/J driver
 realdriver=com.mysql.jdbc.Driver

#specifies the appender to use for logging
appender=com.p6spy.engine.logging.appender.Log4jLogger

#the DriverManager class sequentially tries every driver that is
#registered to find the right driver.  In some instances, it's possible to
#load up the realdriver before the p6spy driver, in which case your connections
#will not get wrapped as the realdriver will "steal" the connection before
#p6spy sees it.  Set the following property to "true" to cause p6spy to
#explicitily deregister the realdrivers
deregisterdrivers=true

Dando como resultado en la siguiente salida:

Output

p6spy - 1275601495062|0|2|statement|select this_.iId as iId7_5_, ... , adeudos6_.mMonto as mMonto2_4_ from tblUsuario this_ left outer join tblTarjeta tarjetas2_ on this_.iId=tarjetas2_.iIdUsuario left outer join tblAbono abonos3_ on tarjetas2_.iId=abonos3_.iIdTarjeta left outer join tblAclaracion aclaracion4_ on tarjetas2_.iId=aclaracion4_.iIdTarjeta left outer join tblEntradasSalidas entradassa5_ on tarjetas2_.iId=entradassa5_.iIdTarjeta left outer join tblAdeudo adeudos6_ on tarjetas2_.iId=adeudos6_.iIdTarjeta where this_.vchUser=? and this_.bActivo=? order by this_.iId asc limit ?|select this_.iId as iId7_5_, ... , adeudos6_.mMonto as mMonto2_4_ from tblUsuario this_ left outer join tblTarjeta tarjetas2_ on this_.iId=tarjetas2_.iIdUsuario left outer join tblAbono abonos3_ on tarjetas2_.iId=abonos3_.iIdTarjeta left outer join tblAclaracion aclaracion4_ on tarjetas2_.iId=aclaracion4_.iIdTarjeta left outer join tblEntradasSalidas entradassa5_ on tarjetas2_.iId=entradassa5_.iIdTarjeta left outer join tblAdeudo adeudos6_ on tarjetas2_.iId=adeudos6_.iIdTarjeta where this_.vchUser='root' and this_.bActivo='true' order by this_.iId asc limit 1

Lo cual muestra la consulta original sin sustituir los valores, y la consulta creada por el framework sustituida por los valores utilizados.

Patrón de diseño Command

Mas que nada quise postear sobre este patrón de diseño, ya que en un reciente proyecto donde el requerimiento era utilizar RMI, al usarlo me pareció estupendo, ya que en lugar de crear varias interfaces y estarlas registrando, solo fue necesario crear una sobre la cual se centraba y administraba la ejecución de acciones. En este patrón existen varias clases que juegan un papel importante: Invoker, o el cliente que realiza la petición de ejecutar la acción; CommandManager, quien administra y controla la ejecución de acciones; AbstractCommand, como clase común para todas las acciones concretas y por ultimo ConcreteCommand, que es la implemetancion de cada acción.

A continuación muestro el diagrama de clases utilizado


Sesion.java

public class Sesion {
...
public Parametro ejecutar(Parametro p) throws AvException {
			try {
				p.setUsername(usuario.getUser());
				Parametro out = RemoteStubCache.getStubToRemoteObject(host).ejecutar(
							p);

				return out;
			} catch (RemoteException e) {
				throw new AvException(
						"Ha occurrido un error al intentar ejecutar "
								+ "la accion :" + p.getAccion(), e);
			}
		}// ejecutar
}//Sesion

Como CommandManager e implementando una interfaz para exponer métodos con RMI:

ServerRemoteImpl.java

public class ServerRemoteImpl implements ServerRemote, Unreferenced {
...
	public Parametro ejecutar(Parametro param) throws RemoteException {
		log.info("Inicio - ejecutar(Parametro param)");

		BaseAccion accion = BaseAccion.getAccion(param.getAccion());
		Parametro p = null;
		if (accion != null) {
			log.debug("Ejecutar : " + param.getAccion());
			try {
				p = accion.ejecutar(param);
			} catch (AvException e) {
				log
						.error("Ha ocurrido un error al intentar ejecutar la accion");
				throw new RemoteException("Ha ocurrido un error al intentar "
						+ "ejecutar la accion", e);
			}
		} else {
			log.warn("Accion no encontrada");
		}

		log.info("Fin - ejecutar(Parametro param)");

		return p;
	}// ejecutar
}// ServerRemoteImpl

En cuanto a AbstractCommand, cabe notar que agregue métodos para obtener las acciones y beans, estos métodos se basan en clases de Spring:

BaseAccion.java

public abstract class BaseAccion {
...

	public static Object getBean(String bean) {
		return getContext().getBean(bean);
	}// getBean

	public static BaseAccion getAccion(String accion) {
		if (accion != null && accion.trim().length() > 0) {
			return (BaseAccion) getContext().getBean(accion);
		}
		return null;
	}// getAccion

	public abstract Parametro ejecutar(Parametro parametro) throws AvException;
}// BaseAccion

Y por ultimo un ejemplo de alguna acción concreta o ConcreteCommand, que en este caso corresponde a la acción de agregar un usuario

AgregarAccion.java

public class AgregarAccion extends BaseAccion {

	private static Logger log = Logger.getLogger(AgregarAccion.class);

	@Override
	public Parametro ejecutar(Parametro parametro) throws AvException {
		log.info("Inicio - ejecutar(Parametro parametro)");

		UsuarioLayer ul = (UsuarioLayer) getBean(UsuarioLayer.BEAN_NAME);
		Usuario u = null;
		if (parametro.getValor(Tipo.INPUT) instanceof Usuario) {
			u = (Usuario) parametro.getValor(Tipo.INPUT);
			ul.agregar(u);
			parametro.setValor(Tipo.OUTPUT, u);
		}

		log.info("Fin - ejecutar(Parametro parametro)");

		return parametro;
	}// ejecutar
}// AgregarAccion

Ejemplo sencillo de RMI con Java 5

Actualmente estoy realizando un proyecto para la maestria donde se nos pide el uso de varias tecnologías, entre ellas RMI. Para mi sorpresa veo que a partir de la versión 5 de java, ya no es necesaria la ejecución del compilador de RMI (rmic).

A continuación escribo un ejemplo sencillo, donde utilizo el patrón de diseño de Command para la elaboración del servidor.

Como ingredientes para este ejemplo solo necesitaremos dos clases y una interfaz para tener una comunicación entre servidor y cliente, empezare por la interfaz.

ServerRemote.java

package com.av.rmi.interfaces;

import java.rmi.Remote;
import java.rmi.RemoteException;

import com.av.rmi.Parametro;

/**
 * Servicio o punto de entrada en el servidor para ejecucion de distintas
 * acciones
 *
 * @author electrocucaracha
 *
 */
public interface ServerRemote extends Remote {

	public static final String OBJECT_STUB_REGISTRY_NAME = "ServerRemote";

	/**
	 * Accion generica a ser ejecutada en el servidor remoto
	 * @param accion
	 */
	Parametro ejecutar(Parametro parametro) throws RemoteException;
}//ServerRemote

Como ya sabemos dicha interfaz debe de heredar de java.rmi.Remote y todos sus metodos deben lanzar la excepcion java.rmi.RemoteException.

Para finalizar la parte del servidor tenemos su implementacion

ServerRemoteImpl.java

package com.av.rmi.impl;

import java.io.File;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import org.apache.log4j.Logger;

import com.av.acciones.BaseAccion;
import com.av.exceptions.AvException;
import com.av.rmi.Parametro;
import com.av.rmi.interfaces.ServerRemote;

/**
 * Implementacion de punto de entrada a funciones promovidas en el servidor
 * 
 * @author electrocucaracha
 *
 */
public class ServerRemoteImpl implements ServerRemote {

	private static Logger log = Logger.getLogger(ServerRemoteImpl.class);
	private static final long serialVersionUID = 1L;
	private static final File f = new File("c:/av_server/bin/");
	
	protected ServerRemoteImpl() throws RemoteException {
		super();
		log.info("Inicio - ctor");
		
		log.info("Fin - ctor");
	}//ServerRemoteImpl
	
	@Override
	public Parametro ejecutar(Parametro param) throws RemoteException {
		log.info("Inicio - ejecutar(Accion accion)");
		
		BaseAccion accion = BaseAccion.getAccion(param.getAccion());
		Parametro p = null;
		if(accion != null){
			log.debug("Ejecutar : " + param.getAccion());
			try {
				p = accion.ejecutar(param);
			} catch (AvException e) {
				log.error("Ha ocurrido un error al intentar ejecutar la accion");
				throw new RemoteException("Ha ocurrido un error al intentar " +
						"ejecutar la accion", e);
			}
		}else{
			log.warn("Accion no encontrada");
		}
		
		log.info("Fin - ejecutar(Accion accion)");
		
		return p;
	}//ejecutar
	
	public static void main(String[] args){
		System.out.println("Starting server...");
		
		try {
			LocateRegistry.createRegistry(1099);
		} catch (RemoteException e) {
			log.error("Ha ocurrido un error al ejecutar el comando rmiregistry : " + 
					e.getMessage());
			e.printStackTrace();
		}
		
		System.setProperty ("java.rmi.server.codebase", f.toURI().toString());
		
		try{
			ServerRemoteImpl obj = new ServerRemoteImpl();
			ServerRemote stub = (ServerRemote)UnicastRemoteObject.
				exportObject(obj, 0);
			
			Registry r = LocateRegistry.getRegistry();
			r.bind(OBJECT_STUB_REGISTRY_NAME, stub);
			System.out.println("Server ready");
		}catch(Exception e){
			log.error("Ha ocurrido un error al registrar el servicio : " + 
					e.getMessage());
			e.printStackTrace();
		}
	}//main
}//ServerRemoteImpl

Hay muchos puntos que observar en esta implementación

1. Utilizo una instancia de File para obtener su URL, ya que Windows permite crear nombres de carpetas con espacios.

private static final File f = new File("c:/av_server/bin/");

2. Es necesario escribir el constructor por defecto

protected ServerRemoteImpl() throws RemoteException {

3. Esta linea es necesaria para evitar el tener que ejecutar el comando rmiregistry antes de la ejecución del servidor. Nota: Utilizo el puerto 1099 ya que es el puerto por defecto de para el servicio de rmiregistry de RMI.

LocateRegistry.createRegistry(1099);

4. Registro en codebase el path de los archivos que expondré sus métodos.

System.setProperty ("java.rmi.server.codebase", f.toURI().toString());

5. En caso de que el servicio o clase no herede direcamente de UnicastRemoteObject es necesario dinamicamente crear una instancia que lo realice. Nota: El segundo parámetro especifica el puerto que utilizara java para hacer las llamadas a nuestro servicio:

ServerRemote stub = (ServerRemote)UnicastRemoteObject.
				exportObject(obj, 0);

Para finalizar el ejemplo, basta crear un cliente que consuma los servicios, para esto he creado esta clase

Cliente .java

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

import com.av.db.dataobjects.Usuario;
import com.av.rmi.CatalogoAcciones;
import com.av.rmi.Parametro;
import com.av.rmi.Parametro.Tipo;
import com.av.rmi.interfaces.ServerRemote;

/**
 * Clase temporal para probar RMI
 * 
 * @author electrocucaracha
 *
 */
public class Cliente {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String host = (args.length < 1) ? null : args[0];
		Registry registry = null;
		ServerRemote stub = null;
		Parametro p = new Parametro();
		Usuario[] usuarios = null;
		try {
			registry = LocateRegistry.getRegistry(host);
		    stub = (ServerRemote) registry.
		    	lookup(ServerRemote.OBJECT_STUB_REGISTRY_NAME);
		    p.setAccion(CatalogoAcciones.OBTENER_TODOS_USUARIO);
		    
		    p = stub.ejecutar(p);
		    
		    if(p.getValor(Tipo.OUTPUT) != null && 
		    		p.getValor(Tipo.OUTPUT) instanceof Usuario[]){
		    	usuarios = (Usuario[])p.getValor(Tipo.OUTPUT);
		    	for(Usuario u : usuarios){
		    		System.out.println(u);
		    	}
		    }
		} catch (Exception e) {
		    System.err.println("Client exception: " + e.toString());
		    e.printStackTrace();
		}	}
}//Cliente

Una vez compiladas las clases, solo se requiere que se ejecute primero la clase del servidor y después la del cliente, sin algún parámetro adicional.