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.

ComboBox con checkboxes como items usando XAML

Me parececio bastante sencillo la forma de crear un nuevo control utilizando XAML, en este caso necesitaba una especie de filtro en el cual se pudieran agregar de una lista de opciones, varias. Aunque existe algun otro control que lo permite, me parecio bastante elegante utilizar un ComboBox que permitiera seleccionar varias opciones.

El resultado que se obtiene es como el siguiente:

Para lo cual necesitaremos agregar en nuestro archivo con extension *.xaml el siguiente codigo

        
            
                
                    
                        
                    
                
            
        

y para el control de los eventos, ya sea con C# o VB .Net el siguiente codigo.

VB .Net

Private Sub chk_Checked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
   ' TODO : Agregar el codigo para el manejo del evento
End Sub

Private Sub chk_Unchecked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
   ' TODO : Agregar el codigo para el manejo del evento
End Sub

C#

        private void chk_Checked(object sender, RoutedEventArgs e)
        {
            // TODO : Agregar el codigo para el manejo del evento
        }

        private void chk_Unchecked(object sender, RoutedEventArgs e)
        {
            // TODO : Agregar el codigo para el manejo del evento
        }

Personalizacion de mensajes de error para JSF

En ocasiones es necesario personalizar los mensajes de error estándar que son mostrados en el cliente, estos mensajes pueden tener un formato comenzando con un prefijo de código de error seguidos por el código del mismo, y por ultimo una descripción del mensaje de error como puede ser algo como lo siguiente:

Validation Error: Value is greater than allowable maximum…

Para poder personalizar dicho mensaje, se necesitan dos cosas, la primera es necesario agregar la ruta del archivo de propiedades que sobreescribira los mensajes estándar, esto se define como un «message-bundle» en el archivo de configuracion de faces-config.xml (el nombre del archivo depende del sistema)

messages

y segundo, como es de suponerse, crear el archivo de propiedades con la ruta que especificamos agregando los valores que sobrescribiremos.

messages.properties

javax.faces.converter.IntegerConverter.INTEGER=Requiere un valor de tipo numerico

En esta pagina puedes encontrar todos los valores posibles para las claves de los mensajes estándar.

Modificadores de acceso – Default

El siguiente modificador de acceso sera Default, algo enredoso, pero muy valioso de tener en cuenta.

Como veremos a continuacion este modificador va de la mano con el paquete al cual pertenece la clase.

Escenario – Declaracion en la clase

  1. Pueden existir un sin fin de clases con el declarador de acceso por defecto pero solo una puede existir una clase publica
  2. ClasePublica.java

    package mipaquete;
    
    public class ClasePublica{
    }
    
    class OtraClase{
    }
    
    class OtraClaseNoPublica{
    }
  3. Puede referenciarse desde clases dentro mismo paquete
  4. OtraClasePublica.java

    package mipaquete;
    
    public class OtraClasePublica {
    	public void invocar(){
    		OtraClase clase = new OtraClase();
    	}
    }
  5. Puede heredarse por clases dentro mismo paquete
  6. ClaseHijaPublica.java

    package mipaquete;
    
    public class ClaseHijaPublica extends OtraClase{
    
    }
  7. No puede referenciarse desde clases de distintos paquetes.
  8. OtraClasePublica.java

    package otropaquete;
    
    import mipaquete.ClasePublica;
    
    public class OtraClasePublica {
    	public void invocar(){
    		OtraClase clase = new OtraClase();
    	}
    }

    OtraClasePublica.java:3: mipaquete.OtraClase is not public in mipaquete; cannot
    be accessed from outside package

  9. No puede heredarse por clases de distintos paquetes.
  10. ClaseHijaPublica.java

    package otropaquete;
    
    import mipaquete.OtraClase;
    
    public class ClaseHijaPublica extends OtraClase{
    }
    }

    ClaseHijaPublica.java:3: mipaquete.OtraClase is not public in mipaquete; cannot
    be accessed from outside package

Como resumen cuando omitamos el operador, y sea aplicado el operador por defecto tendremos que siempre mirar al paquete que pertenece la clase.

Modificadores de acceso – Public

Este sera el comienzo de una serie de posts en los cuales tratare de profundizar en el tema, tratando de abarcar la mayoria de los escenarios posibles.

Comenzare con el modificador de acceso Publico, que por ser menor restrictivo puede ser considerado facil de comprender.

Escenario – Declaracion en la clase

  1. Solo puede existir una clase publica por archivo *.java
  2. ClasePublica.java

    package mipaquete;
    
    public class ClasePublica{
    }
    
    class OtraClase{
    }
    
    class OtraClaseNoPublica{
    }
  3. Puede referenciarse desde clases dentro mismo paquete
  4. OtraClasePublica.java

    package mipaquete;
    
    public class OtraClasePublica {
    	public void invocar(){
    		ClasePublica clase = new ClasePublica();
    	}
    }
  5. Puede heredarse por clases dentro mismo paquete
  6. ClaseHijaPublica.java

    package mipaquete;
    
    public class ClaseHijaPublica extends ClasePublica {
    
    }
  7. Puede referenciarse desde clases de distintos paquetes siempre y cuando la clase sea importada
  8. OtraClasePublica.java

    package otropaquete;
    
    import mipaquete.ClasePublica;
    
    public class OtraClasePublica {
    	public void invocar(){
    		ClasePublica clase = new ClasePublica();
    	}
    }
  9. Puede heredarse por clases de distintos paquetes siempre y cuando la clase sea importada
  10. ClaseHijaPublica.java

    package otropaquete;
    
    import mipaquete.ClasePublica;
    
    public class ClaseHijaPublica{
    	public void invocar(){
    		ClasePublica clase = new ClasePublica();
    	}
    }

Con esto creo cubrir la mayoria de las preguntas que pudiesen surgir acerca de este modificador

java.lang.OutOfMemoryError‏

Hace tiempo me tope que esta excepcion y buscando en varios foros sugerian la ejecucion de las clases enviandoles parametros especiales( java -Xmx[memoria]) ya que la maquina virtual de java utiliza 100 Mb para la ejecucion de clases.

Nunca me parecio razonable la solucion, por dos razones la primera, la arquitectura debe de ser dirigida a aprovechar los recursos del sistema, no a malgastarlos;  la segunda, al ser un equipo de desarrollo esta solucion no podria ser aceptable.

Mi solucion:  Subir un servlet que como primera instruccion llamara al recolector de basura

System.gc();

Con esto parece ser que libero la memoria necesaria para que el web server en mi caso iPlanet, respondiera.