Web Services – Interoperabilidad Java (Server) y .Net (Clients)

Por razones de trabajo me he encontrado algunas veces con este escenario, y después de haber tenido un par de problemas no muy graves pero que consumían tiempo se me ocurrió un esquema que hasta el momento me funciona, y es lo que voy a compartir en el post.
Minimalismo, si esa es la respuesta a esto, ya que regresamos a lo mas simple al exponer los web services retornando un string siempre como resultado del método, y así mismo recibir datos primitivos como parámetros, reemplazando a la estructuras de datos complejas, pero no en su totalidad ya que en ese dato primitivo en realidad contiene una estructura compleja serializada en Json y comprimida con un algoritomo Zip estándar y convertido a Hexadecimal.
La solución puede ser aplicada no solo a cliente .Net sino también a clientes Java, pero vamos a enfocar el post en clientes .net.
Para el lado del server Java vamos a necesitar los siguientes componentes adicionales al JDK:

  • Google Gson (Ya he utilizado esta librería en otros posts, recomendada hasta que el JDK no tenga incluya una implementación estándar de manipulación de Json)
  • Apache Commons Codec

Para los clientes de .net van los siguientes componentes adicionales al framework:

Ya con esto listo, creamos un proyecto web en java en su IDE preferido, yo voy a utilizar eclipse.

Para el ejemplo tengo 3 clases; un bean con una propiedad, luego un bean un poco mas complejo y finalmente uno que contiene a todo para mostrar una clase con una estructura no tan simple como prueba de concepto, aquí el código de cada uno:

public class SuperSimpleBean {
	private int number;

	public SuperSimpleBean(int number){
		this.number = number;
	}

	public SuperSimpleBean(){

	}

	public int getNumber() {
		return number;
	}

	public void setNumber(int number) {
		this.number = number;
	}
}
public class SimpleBean{

	private String code;
	private Date datetime;
	private List superSimpleBeans;

	public SimpleBean(){
		superSimpleBeans = new ArrayList();
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public Date getDatetime() {
		return datetime;
	}

	public void setDatetime(Date datetime) {
		this.datetime = datetime;
	}

	public List getSimpleBeans() {
		return superSimpleBeans;
	}

	public void setSimpleBeans(List superSimpleBeans) {
		this.superSimpleBeans = superSimpleBeans;
	}

}
public class ComplexBean {
	private List simpleBeans;
	private String name;

	public ComplexBean(){
		simpleBeans = new ArrayList();
	}

	public List getSimpleBeans() {
		return simpleBeans;
	}

	public void setSimpleBeans(List simpleBeans) {
		this.simpleBeans = simpleBeans;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

El objeto que va a devolver el web service en string va a pasar por los siguientes pasos:

  • Serializacion a Json
  • Compresion del Json
  • Convertir a un String hexadecimal los bytes comprimidos

Las clases que nos van a ayudar con ese cometido son las siguientes:

public class ZipUtil {

	private static String toHexString(byte[] bytes) {
		return Hex.encodeHexString(bytes);
	}

	private static byte[] toByteArray(String hexString) {
		try {
			return Hex.decodeHex(hexString.toCharArray());
		} catch (DecoderException e) {
			e.printStackTrace();
		}
		return null;
	}

	private static byte[] fromGByteToByte(byte[] gbytes) {
		ByteArrayOutputStream baos = null;
		ByteArrayInputStream bais = new ByteArrayInputStream(gbytes);
		try {
			baos = new ByteArrayOutputStream();
			GZIPInputStream gzis = new GZIPInputStream(bais);
			byte[] bytes = new byte[1024];
			int len;
			while ((len = gzis.read(bytes)) > 0) {
				baos.write(bytes, 0, len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return (baos.toByteArray());
	}

	private static byte[] fromByteToGByte(byte[] bytes) {
		ByteArrayOutputStream baos = null;
		try {
			ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
			baos = new ByteArrayOutputStream();
			GZIPOutputStream gzos = new GZIPOutputStream(baos);
			byte[] buffer = new byte[1024];
			int len;
			while ((len = bais.read(buffer)) >= 0) {
				gzos.write(buffer, 0, len);
			}
			gzos.close();
			baos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return (baos.toByteArray());
	}

	public static String compressAndEncodeText(String text) {
		return toHexString(fromByteToGByte(text.getBytes()));
	}

	public static String decompressAndDecode(String data) {
		data = data.replace("-", "");
		byte[] zippedBytes;
		try {
			zippedBytes = toByteArray(data);
			byte[] decompressed = fromGByteToByte(zippedBytes);
			return new String(decompressed);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";

	}
}
public class ParserUtil {

	public static String parseAndCompress(Object o){
		String json = new Gson().toJson(o);
		return ZipUtil.compressAndEncodeText(json);
	}

}

Y ahora en la clase que se va a exponer como web service creamos objetos dummy con datos y devolvemos el resultado serializado y comprimido:

public class WebService {

	public String getData(){

		ComplexBean cBean = new ComplexBean();
		cBean.setName("A complex bean");

		for(int i=0; i<=1000; i++){
			SimpleBean sBean = new SimpleBean();
			sBean.setCode(String.format("Code %s", i));
			sBean.setDatetime(Calendar.getInstance().getTime());

			for(int j=0; j<=10;j++){
				SuperSimpleBean ssBean = new SuperSimpleBean(j);
				sBean.getSimpleBeans().add(ssBean);
			}

			cBean.getSimpleBeans().add(sBean);
		}

		return ParserUtil.parseAndCompress(cBean);
	}
}

Publicamos el web service, lo probamos con soapUI y el resultado es el siguiente:

soapUI

1f8b0800000000000000dddcbd8aa4d71586d15b693a9ea0cefe39679fce2c83338……… un string hexadecimal.

Si devolviera json en el web service, el tamaño de los datos de respuesta seria de 216kb, mientras que la respuesta comprimida es de 6.34kb, ustedes pueden calcular el porcentaje de compresión.

Del lado del cliente .net simplemente hay que hacer las clases con la misma estructura, algo que aun no he tenido tiempo para revisar es que al momento de deserializar el json las clases de .net en compact framework deben tener declarados los campos, en lugar de tener propiedades automáticas, honestamente no he encontrado en la librería Json for CF algún parámetro para que sea case insensitive, pero para el framework estándar no hay problemas:

public class SuperSimpleClassInfo
    {
        public int Number { get; set; }

        public SuperSimpleClassInfo()
        {
        }

        public SuperSimpleClassInfo(int number)
        {
            this.Number = number;
        }
    }
public class SimpleClassInfo
    {
        public string Code { get; set; }
        public DateTime DateTime { get; set; }
        public List<SuperSimpleClassInfo> SuperSimpleBeans { get; set; }

        public SimpleClassInfo()
        {
            SuperSimpleBeans = new List<SuperSimpleClassInfo>();
        }

    }
public class ComplexClassInfo
    {
        public string Name { get; set; }
        public List<SimpleClassInfo> SimpleBeans { get; set; }

        public ComplexClassInfo()
        {
            SimpleBeans = new List<SimpleClassInfo>();
        }
    }

Necesitamos una clase para convertir el texto hexadecimal a bytes, descomprimir y luego “parsear” el json a nuestra estructura de objetos, para descomprimir el texto creamos una clase ZipUtil similar a la que tenemos del lado de java:

public class ZipUtil
    {
        public static byte[] Decompress(byte[] compressed)
        {
            byte[] buffer = new byte[4096];
            using (MemoryStream ms = new MemoryStream(compressed))
            using (GZipStream gzs = new GZipStream(ms, CompressionMode.Decompress))
            using (MemoryStream uncompressed = new MemoryStream())
            {
                for (int r = -1; r != 0; r = gzs.Read(buffer, 0, buffer.Length))
                    if (r > 0) uncompressed.Write(buffer, 0, r);
                return uncompressed.ToArray();
            }
        }

        public static byte[] Compress(byte[] raw)
        {
            using (MemoryStream memory = new MemoryStream())
            {
                using (GZipStream gzip = new GZipStream(memory, CompressionMode.Compress, true))
                {
                    gzip.Write(raw, 0, raw.Length);
                }
                return memory.ToArray();
            }
        }

        public static string CompressAndEncode(string text)
        {
            byte[] compressed = Compress(System.Text.ASCIIEncoding.UTF8.GetBytes(text));
            string hex = BitConverter.ToString(compressed);
            return hex;
        }

        public static byte[] ToByteArray(string hexString)
        {
            hexString = hexString.Replace("-", "");
            int NumberChars = hexString.Length;
            byte[] bytes = new byte[NumberChars / 2];
            for (int i = 0; i < NumberChars; i += 2)
                bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
            return bytes;
        }

        public static string DecompressAndDecode(string text)
        {
            byte[] textBytes = ToByteArray(text);
            byte[] uncompressed = Decompress(textBytes);
            string result = System.Text.ASCIIEncoding.ASCII
                    .GetString(uncompressed, 0, uncompressed.Length);
            return result;
        }
    }

Listo, con todo ya preparado agregamos la referencia al web service:

addreference

Una vez agregada la referencia es recomendable actualizar en el archivo de configuración el tamaño máximo de datos permitidos en las propiedades: maxBufferSize, maxReceivedMessageSize y maxStringContentLength con el valor 2147483647.

Listo, procedemos a invocar el metodo del web service, a descomprimir y luego con la libreria Json a convertir los datos a un objeto:

JavaWS.WebServiceClient client = new JavaWS.WebServiceClient();
            string data = client.getData();
            string json = ZipUtil.DecompressAndDecode(data);
            ComplexClassInfo complex = JsonConvert.DeserializeObject<ComplexClassInfo>(json);

El resultado es el objeto complex cargado con toda la información  para enviar datos en el mismo formato se aplica el proceso inverso desde .net a java.

Descargar fuentes

Happy Coding!

Saludos,
gish@c

FileUpload con Eclipse, Tomcat y Primefaces

Devolviendo algo de conocimiento a la comunidad, este ejemplo sencillo pero útil para ademas de cargar archivos con el fileupload de primefaces poder leer parámetros adicionales que se envíen en el formulario.

Para poder iniciar se debe configurar JSF en eclipse, deben descargar una implementacion que puede ser Mojarra o MyFaces, una manera de descargar es en el proyecto web Properties->Project Faces -> Java Server Faces -> Download Library (El icono del lado derecho) y ahí escogen la implementacion, y posteriormente agregar el jar de jstl-1.2 a la carpeta WebContent/WEB-INF/lib.

Para usar primefaces basta con agregar el jar a la carpeta WEB-INF/lib , pero para usar el fileUpload si es necesario agregar los jars de commons-fileupload y commons-io los cuales deben revisar en la documentación de primefaces que versión corresponde según la versión de prime y configurar un par de lineas en el web.xml:

<filter>
		<filter-name>PrimeFaces FileUpload Filter</filter-name>
		<filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>PrimeFaces FileUpload Filter</filter-name>
		<servlet-name>Faces Servlet</servlet-name>
	</filter-mapping>

En la vista vamos a tener el siguiente código:

<f:view>
	<h:head>
		<meta http-equiv="Content-Type"
			content="text/html; charset=ISO-8859-1" />
		<title>File Upload Sample</title>
	</h:head>
	<body>
		<h:form id="myform" enctype="multipart/form-data">
			<h:outputStylesheet library="css" name="style.css" />
			<p:growl id="messages" showDetail="true" />
			<p:fileUpload
				fileUploadListener="#{fileuploadcontroller.uploadAttachment}"
				mode="advanced" update="messages" multiple="true"
				sizeLimit="900000000"
				allowTypes="/(\.|\/)(gif|jpe?g|png|txt)$/"
				uploadLabel="Cargar" cancelLabel="Cancelar" label="Buscar archivo"
				required="true" />
			<br />
			<br />
			<strong>Mensaje</strong> <br/><br />
			<p:inputText id="txtField" style="width:250px" maxlength="40">
				<f:validateRequired for="txtField"></f:validateRequired>
			</p:inputText>
		</h:form>
	</body>
</f:view>

En la configuración del fileUpload definimos el modo advanced para que aparezca con ese estilo de menubar, el tamaño máximo del archivo, que permita varios uploads a la vez , los tipos de archivo permitidos y por ultimo el método del controlador para gestionar la carga del archivo; también podemos notar que tenemos un inputText pero que no esta asociado en la propiedad value a ningun bean, esto sucede ya que al form lo hemos definido como enctype=”multipart/form-data” esto causa que lleguen los valores null al controlador, y la manera en que debemos obtener los parámetros es a través del objeto request que tiene el mapa de parámetros del formulario.

FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("form:parametro")

El codigo del controlador:

@ManagedBean(name="fileuploadcontroller")
@RequestScoped
public class FileUploadController {

	public void uploadAttachment(FileUploadEvent event){
		UploadedFile file = event.getFile();
		ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
		String txtField = ec.getRequestParameterMap().get("myform:txtField");
		String filePath = ec.getRealPath(String.format("/resources/img/%s",file.getFileName()));
		try {
			FileOutputStream fos = new FileOutputStream(filePath);
			fos.write(file.getContents());
			fos.flush();
			fos.close();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		FacesContext context = FacesContext.getCurrentInstance();
		context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,String.format("Archivo cargado: %s ", file.getFileName()),
				String.format("Mensaje: %s", txtField)));

	}

}

El método uploadAttachment es el que esta asociado en el fileUpload en la propiedad fileUploadListener, dentro de este como vemos podemos obtener el parámetro del inputText a través de :myform:txtField, que corresponden al id del fomulario <form> y el inputText respectivamente, el resultado del ejemplo es cargar el archivo en la carpeta /resources/img y presentar un mensaje con el texto ingresado en el input, que obviamente puede ser utilizado con otros fines acorde al caso que se les presente.

sample

Descargar proyecto eclipse.

Saludos,

@gishac

La falta de respeto a los Proyectos de Software

Trabajas como consultor de proyectos de software, y te llaman de un potencial cliente para una reunión ya que requieren un desarrollo de un sistema para automatizar un proceso de negocio; después de una reunión de una hora y de conocer (si tuviste suerte de que el que te explica el tema lo maneja en su totalidad) el proceso en macro, lo primero que te preguntan después de la reunión es: “Y cuánto tiempo va a tomar desarrollar el proyecto?” Oh si, es la típica pregunta que te hacen incluso antes de preguntarte el referente económico, obviamente si ya has pasado por situaciones similares dirás que tienes que analizar con tu equipo de trabajo, ver la disponibilidad de tus colaboradores, aterrizar el requerimiento, y que luego enviarás un documento con un cronograma en macro, advirtiendo con letra en negritas que va a variar obviamente después de levantar el detalle del proceso. Y sí, es totalmente razonable que la empresa o cliente que requiere el desarrollo del proyecto quiera saber cuanto va a demorar el trabajo a realizar, pero veamos los siguientes puntos que no siempre se toman en cuenta:

  • Por Dios es la primera reunión!
  • Por lo general en esa etapa no participa el personal que va a utilizar realmente el sistema (conocido en el mundo de sistemas como el personal operativo), quien realmente tiene el detalle del día a día de lo que se realiza, y todos los escenarios que la herramienta debe contemplar para que Él, el usuario final sienta que tiene una herramienta que realmente lo ayuda en su trabajo
  • No se puede estimar a ciencia cierta cuanto puede tomar todo un proyecto en esa instancia, y Dios te proteja de no abrir la boca equivocadamente y dar un número de días/meses (porque si dices años, olvidalo… nunca te asignarán el proyecto incluso en algunos casos si estás participando por desarrollar un ERP) ya que si das un estimado equivocado, cuando propongas un cronograma más aterrizado que se pasa en uno o dos meses, vas a tener el estimado inicial pegado en la frente, y va a ser el arma principal con la cual van a arrinconarte para que reduzcas los tiempos de tu proyecto (No lo olviden, los desarrolladores de software se sacan una pantalla de mantenimiento del bolsillo)
  • En muchos de los casos no necesariamente es personal técnico el que participa en la reunión, sino gente del área del negocio y que presiona muchísimo sin conocer realmente todo lo que implica desarrollar un proyecto de software

Entonces… Que implica desarrollar un proyecto de software? Al igual que en muchas otras profesiones donde el trabajo intelectual y la creatividad prevalecen, el desarrollar un proyecto de software implica plasmar a través del uso de la tecnología la automatización de algún proceso, crear una nueva herramienta de trabajo, una plataforma de entretenimiento, en fin es todo un gran proceso que varia de acuerdo a la metodología de trabajo que se tenga, pero por lo general un proyecto con un grado aceptable de formalidad está dirigido mas o menos así:

  • Recopilación de información sobre lo que se va a desarrollar (Al detalle)
  • Análisis de las tecnologías que pueden utilizarse para facilitar el desarrollo del proyecto
  • Planteamiento de la arquitectura en base a la tecnología a utilizar
  • Identificar los módulos del proyecto y sus dependencias
  • Definir prioridades
  • Establecer hitos con entregables parciales (Claro, porque si el cliente no ve algo pronto cree que no estás haciendo nada)
  • Presentación al cliente de toda tu fantástica propuesta y conseguir su aceptación
  • Al fin, empezar el desarrollo
  • Pruebas internas de desarrollo
  • Pruebas verticales y horizontales
  • Estabilización (A corregir todo lo que dejaste mal hecho por tus propios errores)
  • Pruebas con usuarios finales
  • Estabilización II (A corregir todo lo que te dijeron que era de una manera, pero que finalmente se hacia de otra)
  • Implementación (El día que no duermes por poner en producción el proyecto)
  • Soporte en producción (Las semanas que no duermes ya que te llaman todo el día a cualquier hora por todo lo que pase en el sistema)
  • Documentación (Suele ir en paralelo, pero aceptemos la realidad, por lo general dejamos la documentación para el final)
  • Cierre del proyecto (El día que no duermes porque finalmente estás festejando con tu equipo de trabajo que terminaron el proyecto)

Aún sigues creyendo que la gente de sistemas estornuda y aparece una dll? (Es un eufemismo a como realmente se dice… ustedes entienden)… Y por qué está en negritas la palabra arquitectura? Porque sí, estimad@s lectores… Un proyecto de software es un símil a un proyecto de construcción civil:

  • Se hace todo el dimensionamiento y el estudio de la obra
  • Se escoge y se calcula el material a utilizar
  • Se estima el tiempo que va a llevar la obra
  • Se construyen las bases, se rellena el terreno, etc etc
  • Se monta la estructura que va a sostener la obra
  • Se empieza a construir
  • Se compra material si la estimacion fue errónea, se ajustan detalles etc.

No soy conocedor de como llevar una obra de construcción, pero estoy tratando de darle un orden lógico no necesariamente el óptimo ni el deber ser, sólo es un ejemplo para llevarlo al símil del tema del post, así que favor ahorrarse el troleo 🙂

Existe el término, muy utilizado, y ampliamente conocido por el universo de usuarios y x todos nosotros: “SE CAYÓ EL SISTEMA“? Porque (perdonen si lo repito tanto) si volvemos a la obra civil, y está mal construida… Se cae la construcción!!! No les parece algo sumamente grave? Desconozco la ley de mi país con respecto a éste tema, pero por lo que he escuchado en las noticias en otros países, si a un Arquitecto se le cae una obra puede ir a parar a prisión!

Por eso para nosotros, la gente que está detrás de un proyecto de software, estimar el tiempo necesario, hacer nuestro trabajo asegurando un grado de calidad sobre el trabajo que va a reflejar nuestra imagen, es muy importante (obvio no queremos ir a prisión 🙂 ). Por eso nos estresamos si por motivos fuera de nuestras manos hay que acortar los plazos, cambian los requerimientos a mitad del proyecto, u ocurre cualquier eventualidad que nos afecta en nuestro trabajo.

Estimad@s tod@s, yo les pregunto:

  • Si un médico lo está operando luego de indicarle que el proceso va a durar 3 horas… Sus familiares se meten a la sala de operación a pedirle que acorte el tiempo de la operación?
  • Si un Arquitecto ya tiene los planos de un edificio, y cuando ha construido la mitad de los pisos… Usted se arrepiente de lo que pidió y le pide que cambie el diseño y los planos? O le llama a decirle que se olvidó de que quería unas escaleras en la mitad?

Si la respuesta es no (Si es si, cierre ésta ventana y siga jugando farmville)… Entonces por qué se cree que a los proyectos de software se los puede exprimir, acortar, desarmar, etc etc etc sin pensar en el impacto que puede llegar a tener no sólo en los tiempos de entrega, sino también en la calidad del trabajo QUE ES NUESTRA RESPONSABILIDAD entregar?

El motivo de éste post es el acumulado de experiencias propias y ajenas en las cuales he visto como se trata de minimizar o faltarle el respeto a todo un trabajo que es mucho más que “un simple programita” como le suelen llamar… La gente que desarrolla proyectos de software a diferencia de otros profesionales tiene que ponerse en los zapatos de otros y entender el área sobre lo que está trabajando:

  • Si hace un software contable, tiene que saber de contabilidad
  • Si hace un software para forecasting, tiene que entender los modelos matemáticos
  • Si hace un software para graficar planos, tiene que entender de arquitectura

Desarrollar proyectos de software es una actividad dinámica, muy exigente y a su vez satisfactoria cuando sabes que resolviste un problema, que entregaste un trabajo de calidad, que el cliente que respetó tu trabajo es un cliente feliz que te volverá a llamar para desarrollar otro proyecto juntos; por eso y muchas otras razones la gente que trabajamos en y con tecnología, y nuestro trabajo, estamos en un nivel que merece un alto respeto por parte de cualquier mortal 🙂

Feliz día del programador!

Saludos,

gish@c

Java Series – Authentication y Authorization en J2EE

Si hay algo de lo que no se pueden quejar las personas que desarrollan en web con asp.net es sobre lo fácil que es el manejo de seguridad a recursos protegidos (authorization) y lo intuitivo de los tipos de autenticación que ofrece .net (authentication), pero suele ocurrir que por razones laborales/educativas etc dan el salto a otra plataforma de desarrollo y oh! sorpresa… donde esta esa sección de configuración para proteger los accesos??? Y así empieza la odisea … El post de hoy expone justamente el manejo de seguridad en J2EE.
Para el ejemplo vamos a crear un proyecto web (El proyecto está con JSF 2) y vamos a definir directorios donde van a estar las páginas de administración para 2 roles de usuario: Administrador y Super Administrador, controlando el acceso al recurso que le corresponda. La estructura para el ejemplo es la siguiente:

Como se visualiza en el gráfico están claramente definidas las carpetas, en las cuales se supondría que están las páginas a las que sólo se puede tener acceso con ciertos privilegios.
Adicionalmente el proyecto cuenta con la página forbidden.xhtml que para el caso es la página a la que es redirigido el request cuando se trata de acceder a una sección protegida sin tener las credenciales requeridas, y la página index.xhtml que es la página de login.
El estándar casi por omisión para seguridad en J2EE es Spring Security, pero para alguien que viene de otra plataforma le introduce otro millón mas de conceptos (lo cual no es malo, sólo que a veces son tan amplios que nos apartan totalmente el camino de lo que realmente queremos implementar), así que desde la especificación de Servlets 2.3 tenemos un feature que permite interceptar los requests y de esa manera determinar quien está tratando de acceder a los recursos de la aplicación; este feature son los Filters.
Muchos son los casos de uso en los que nos sirve interceptar requests, pero vamos a enfocarnos en autenticacion y autorización.
Para definir un filtro simplemente, creamos una clase que implemente la interfaz javax.servlet.Filter, e implementamos los métodos que nos exige:

public void init(FilterConfig filterConfig);

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain);

public void destroy();

Tenemos definido el filtro y para interceptar los requests tenemos que configurarlo en el web.xml y mapear las rutas en las que va actuar el mismo, para hacerlo colocamos la siguiente configuración:


    <filter>
        <filter-name>AuthAdminFilter</filter-name>
        <filter-class>demo.security.AuthAdminFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthAdminFilter</filter-name>
        <url-pattern>/administration/*</url-pattern>
    </filter-mapping>

El primer elemento filter-name es referencial, lo que si es importante es en filter-class colocar el nombre completo de la clase que está implementando el filtro, luego en el siguiente filter-name se hace referencia a la declaración anterior y colocamos la ruta que queremos que el filtro administre, al colocar * cualquier página que esté dentro de ese contexto será validada.
Para validar en el filtro si el usuario está autenticado y el rol que tiene, en la pantalla de login, una vez validadas las credenciales vamos a almacenar una variable de sesión con un objeto de tipo UserBean, que basicamente tiene el id del usuario y el rol:

        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext extContext = context.getExternalContext();
        String url = "";
        if(isAdmin(user, password))
        {
            url = extContext.encodeActionURL(
                    context.getApplication().getViewHandler().getActionURL
                    (context, "/administration/adminPanel.jspx"));
            extContext.getSessionMap().put(USER_KEY, new UserBean(user, "admin"));
            extContext.redirect(url);
            return;

        }
        if(isSuperAdmin(user, password))
        {
            url = extContext.encodeActionURL(
                    context.getApplication().getViewHandler().getActionURL
                    (context, "/superadmin/superAdminPanel.jspx"));
            extContext.getSessionMap().put(USER_KEY, new UserBean(user, "superadmin"));
            extContext.redirect(url);
            return;
        }

        }

En el código mostrado se valida si las credenciales son válidas, si es así lo redirige a la sección de administración correspondiente, y finalmente pone en una variable de sesión el objeto de usuario.

Como tenemos 2 secciones que queremos validar los requests vamos a crear 2 filtros, uno para validar si es “admin” y otro para validar si es “super admin”, por lo tanto configuramos otro filtro en el web.xml:


     <filter>
        <filter-name>AuthSuperAdminFilter</filter-name>
        <filter-class>demo.security.AuthSuperAdminFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthSuperAdminFilter</filter-name>
        <url-pattern>/superadmin/*</url-pattern>
    </filter-mapping>

Hasta ahora tenemos el mapeo de las url’s a los filtros que van a validar los request, ahora veamos el desarrollo de las clases que implementan el Filtro de los requests:

Filtro para Sección de Administradores

public class AuthAdminFilter implements Filter {

    private FilterConfig configuration;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.configuration = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (((HttpServletRequest) request).getSession().getAttribute(
                AuthBean.USER_KEY) == null) {
            ((HttpServletResponse) response).sendRedirect("../forbidden.jspx");
        } else {
            chain.doFilter(request, response);
        }

    }

    @Override
    public void destroy() {
        configuration = null;
    }

Filtro para Sección de Super Administradores

public class AuthSuperAdminFilter implements Filter {

    private FilterConfig configuration;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.configuration = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        if (((HttpServletRequest) request).getSession().getAttribute(
                AuthBean.USER_KEY) == null || !((UserBean)((HttpServletRequest) request).getSession().getAttribute(
                AuthBean.USER_KEY)).getRole().equals("superadmin") ) {
            ((HttpServletResponse) response).sendRedirect("../forbidden.jspx");
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
        this.configuration = null;
    }

}

Ambas implementaciones son casi idénticas salvo por el caso de que en la de superadmin validamos que además de que exista en sesión la variable que nos indica que está autenticado, el usuario tenga el rol de “superadmin”, en caso que no cumpla la condición se lo redirige a la sección de acceso restringido.
Bueno, eso es todo en el post 😀 para ver el ejemplo pueden descargar el código fuente desde aquí, es un proyecto netbeans 6.9 con glassfish.

Saludos,
gish@c

Lista Expansible Dinámica con jQuery & AspNet MVC – AspNet WebForms

Uff largo tiempo sin bloguear!!! Había estado viviendo la vida de los mortales por un tiempo, a veces durmiendo temprano, descansando, jugando Wii, en fin… de vez en cuando no está mal ser un ser humano común y silvestre xD

Pero aquí regresando al mundo bloggero empiezo dedicando este post a jQuery, la librería de javascript por defecto en cualquier aplicación web bajo cualquier plataforma. La problemática del post es el uso de jQuery para crear listas que se pueden expandir o colapsar para mostrar información de cualquier tipo y darle un mejor uso al espacio en pantalla, lo cual le da al usuario el control sobre lo que ve en su entorno de trabajo.
Decidi hacer el ejemplo bajo 2 paradigmas: AspNet Webforms y AspNet MVC3, aunque el esquema es de una plantilla puede aplicar para cualquier plataforma (java, go, scala, etc), ya que el “truco” esta del lado del cliente con javascript.
Ok, el caso es el siguiente:

Tenemos contenido dinamico que lo queremos ocultar o mostrar al hacer click en un link, que tiene la logica para mostrar u ocultar lo que contiene.
El html es el siguiente:

<div>
<div class="expandable">
<div>
<div>
                    <strong>Expand ME!!!!</strong></div>
<div class="content">
                    Aqui va el contenido
                    <img src="imagen1.jpg" alt="" /></div>
</div>
</div>
<div id="read-more" class="read-more"></div>
</div>

Esta es la plantilla de html para cualquier elemento que desee implementar la funcionalidad de poder expandirse/contraerse. Lo importante en este html:

  • El div principal que contiene los datos tiene como estilo la clase “expandible”, MUY importante ya que como veremos mas adelante con DOM obtendremos todos los elementos html que tengan ese estilo para darle la funcionalidad requerida.
  • Hay 1 div sin contenido: read-more que ademas del id estan enlazados al css por la clase indicada. En este elemento es donde vamos a agregar dinamicamente un link con el evento y el handler para expandir/colapsar el detalle

Ok, si lo vemos sin ningun esfuerzo adicional es un div que tiene otro div con mas html… entonces como le damos la funcionalidad de esconder el html? Comenzamos dandole ciertas propiedades a través de CSS a los elementos de la plantilla:

.expandable
{
    position: relative;
    overflow: hidden;
}

Para evitar cualquier desorden de UI colocamos como relative la posicion de los elementos, y el overflow es la que nos da el truco de esconder el detalle del elemento a mostrar, ya que al evaluar que el espacio en altura es menor a lo que ocupa lo que se quiere mostrar simplemente esconde todos los elementos.
Ahora vamos al javascript:


$(function doExpandable() {
    var items = $('.expandable');
    items.each(function () {
        var defHeight = $(this).height();
        var element = $(this);
        var slideHeight = 25;
        if (defHeight >= slideHeight) {
            element.css('height', slideHeight + 'px');
            var moreElement = element.siblings().filter(function () {
                return $(this).attr('id') == "read-more";
            });
            moreElement.append('<a id="link" href="#">more</a>');
            moreElement.children().click(function () {

                var curHeight = element.height();
                if (curHeight == slideHeight) {
                    element.animate({
                        height: defHeight
                    }, "normal");
                    $(this).html('close');
                } else {
                    element.animate({
                        height: slideHeight
                    }, "normal");
                    $(this).html('more');
                }
                return false;
            });
        }
    }
    );

});

Haciendo uso de los selectors por clase (atributo class) de jQuery, obtenemos todos los elementos configurados como “expandable”
y para cada elemento se hacen los siguienes procesos:

  • Se calcula si la altura es mayor al valor inicial (el valor que tendra el elemento estando colapsado:25)
  • Si es asi modificamos la propiedad heigth del elemento
  • Accedemos a los nodos adyacentes (siblings) y filtramos los que tengan el id “read-more”, que hasta este momento es un div vacio
  • Dinamicamente se agrega un link al div “read-more”
  • Agregamos el handler al click en el link donde animanos el cambio de tamaño usando el animate() de jQuery, y cambiamos el texto del link dependiendo si colapsa o expande el elemento.

Hay ciertos detalles importantes en el javascript, por ejemplo el almacenar en var element el elemento expansible que se esta iterando, ya que es la manera en la que puede ser referenciado por los metodos anónimos.

Creando un UserControl

Uniendo la plantilla html y un control Repeater podemos hacer un user control para crear una lista dimámica. Primero creamos una clase con las propiedades necesarias:

public class Item
{
    public int ID { get; set; }
    public string Title { get; set; }
    public string Text { get; set; }
    public string Image { get; set; }

	public Item()
	{
	}
}

Y creamos el control ascx con la plantilla:

<asp:Repeater ID="dataRepeater" runat="server">
 <ItemTemplate>
 <div>
 <div class="expandable" id="<%# Eval("ID")%>">
 <div>
 <div>
 <strong>
 <%# Eval("Title")%></strong></div>
 <div class="content">
 <%# Eval("Text")%>
 <img src='<%#Eval("Image")%>' alt=""/>
 </div>
 </div>
 </div>
 <div id="read-more" class="read-more">
 </div>
 </div>
 </ItemTemplate>
</asp:Repeater>

Del lado del server le ponemos una propiedad datasource para hacer el bind al repeater

public IEnumerable<Item> Datasource { get; set; }

Luego simplemente colocamos el control en cualquier aspx y o llenamos de datos para crear la lista:

protected void Page_Load(object sender, EventArgs e)
{
 if (!Page.IsPostBack)
 {
 var data = new List<Item>();
 data.Add(new Item { ID = 1, Title = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr",
 Text = "Item 1 text",
 Image = "images/image1.jpg"
 });
 expandableList.Datasource = data;
 }
 }

Ahora en MVC

En aspnet mvc es muy sencillo ya que el esquema de plantillas que se utiliza nos facilita el trabajo.
En el Controller cargamos la lista de datos que pasamos a la vista


public ActionResult Index()
 {
var data = new List<Item>();
data.Add(new Item
{ ID = 1, Title = "Expand Me!!!", Text = "Item 1 Text", Image = "images/image1.jpg"
});
return View(data);
 }

Y en la vista


@model IEnumerable<JQueryExpandableMVC.Models.Item>
<script src="../../Scripts/expandable.js" type="text/javascript"></script>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />

@foreach (JQueryExpandableMVC.Models.Item item in @Model)
{
<div>
<div class="expandable">
<div>
<div>
<strong>@item.Title</strong>
</div>
<div class="content">@item.Text
<img src='@item.Image' alt=""/>
</div>
</div>
</div>
<div id="read-more" class="read-more">
</div>
</div>
}

Bueno, con eso termino este post y dejo el link para descargar el código fuente.

Db4o Series – Collections Basics

Despues del DVP 2010 y la gorra xD tenia que dedicarle un post a db4o jeje.
Db4o puede manejar la persistencia de las listas de una manera natural, incluso dándonos incluso colecciones especiales con implementación para manejar persistencia y activación automática (Transparent Persistence y Transparent Activation). En este post voy a tratar de explicar las bases de como trabajar con colecciones y las operaciones comunes de base de datos con estas estructuras, no es el objetivo mostrar como optimizar el uso de colecciones o utilizar persistencia/activacion automática, sino comprender lo básico con la configuración necesaria para que funcione, es decir sin tunning.
Teniendo el siguiente modelo:

Donde la clase Project es la que tiene una lista de recursos, pudiendo ser del tipo Developer o ProjectLeader, vamos a manejar las operaciones en la coleccion de manera normal y vamos a dejar que db4o se encargue del resto 🙂
Las consideraciones que debemos tener para estas estructuras son:

  • Si no estamos utilizando Transparent Persistence debemos configurar la actualizacion en cascada (cascade on update) si vamos a modificar las propiedades de los objetos dentro de la colección (para este ejemplo vamos a hacerlo y tendremos que activar este feature).
configuration.common().objectClass(Project.class).
     cascadeOnUpdate(true);
  • No necesitamos código adicional para grabar los objetos agregados a las colecciones
  • Cuando se elimina un objeto de la lista y se graban los cambios, el objeto no es eliminado de la base de datos, simplemente la referencia es eliminada de la lista.
  • Cuando se elimina la referencia de un objeto de la base de datos y este estaba referenciado por una lista, se va la referencia a NULL ya que la lista nunca se entero que el objeto fue eliminado de la base de datos, tenemos que tener mucho cuidado para tener integridad en los objetos, una manera de solventar este tema es utilizando los callbacks y teniendo un diseño en ambas direcciones (el objeto de la colección tiene una referencia al objeto padre y se puede eliminar la referencia cuando se detecta que va a ser eliminado de la base de datos)

Con el modelo presentado anteriormente, queremos crear un proyecto con varios desarrolladores:

Project db4o = new Project("Db4Objects");
// Agregamos los recursos a la colección
db4o.getResources().add(new ProjectLeader("John Smith", 95000));
db4o.getResources().add(new Developer("Anne Lu", 35000));
db4o.getResources().add(new Developer("Marie Perez", 32000));
db4o.getResources().add(new Developer("Carl", 33000));
db4o.getResources().add(new Developer("Rodrigo", 35000));
container.store(db4o);

Para cualquiera que haya programado en Java le son natural las 6 primera lineas, solo la ultima linea corresponde al store de db4o que se encarga de guardar en la base de datos los objetos creados, todo el árbol de objetos desde el proyecto hasta los recursos, debido a la configuración de almacenamiento en cascada.

Así mismo podemos actualizar las listas añadiendo o removiendo objetos, o actualizando propiedades de los objetos en la colección.
Esto es lo que tiene la base de datos antes de hacer algún cambio:

Project: Db4Objects
John Smith – 95000.0
Anne Lu – 35000.0
Marie Perez – 32000.0
Carl – 33000.0
Rodrigo – 35000.0

Hacemos las actualizaciones

Query query = container.query();
query.constrain(Project.class);
//Consultamos el proyecto db4o para obtener el objeto y modificarlo
query.descend("name").constrain("Db4Objects");
ObjectSet result = query.execute();
Project db4o = (Project) result.next();
// Removemos un recurso del proyecto
db4o.getResources().remove(3);
// Actualizamos el salario de un recurso
Resource developer = db4o.getResources().get(1);
developer.setSalary(developer.getSalary() + 1000);
// Agregamos un nuevo desarrollador al proyecto
db4o.getResources().add(new Developer("Adam Pereiro", 50000));
//Guardamos los cambios
container.store(db4o);

Y el resultado es el siguiente:

Project: Db4Objects
John Smith – 95000.0
Anne Lu – 36000.0
Marie Perez – 32000.0
Rodrigo – 35000.0
Adam Pereiro – 50000.0

Nada complicado, ahora si queremos obtener el objeto principal a partir de un elemento de la colección, es decir si quisiéramos saber el proyecto de un recurso en especifico podemos utilizar Native Queries por ejemplo. Para el caso si tenemos varios proyectos y queremos obtener solo los que tienen recursos con un salario menor o igual a 30000.
Agregamos 2 proyectos adicionales

Project eclipseIDE = new Project("EclipseIDE");
eclipseIDE.getResources().add(new ProjectLeader("Carlos Field", 85000));
eclipseIDE.getResources().add(new Developer("Gian Lopez", 40000));
eclipseIDE.getResources().add(new Developer("Tania Mellas", 28000));
eclipseIDE.getResources().add(new Developer("Sara Parker", 37000));
eclipseIDE.getResources().add(new Developer("Neil Smith", 35000));

Project netBeans = new Project("Netbeans");
netBeans.getResources().add(new ProjectLeader("Karen Bowler", 23000));
netBeans.getResources().add(new Developer("Juan Perez", 18000));
container.store(netBeans);
container.store(eclipseIDE);

El query seria algo asi:

final double maxSalary = 30000;
List<Project> projects = container.query(new Predicate<Project>() {
 public boolean match(Project project) {
  for (Resource resource : project.getResources()) {
   if (resource.getSalary() <= maxSalary)
    return true;
   }
   return false;
  }
});

Lo cual nos daría como resultado:
…Projects with salary <= 30000…
Project: EclipseIDE
Project: Netbeans

Como podemos ver no tenemos que preocuparnos por nada mas que lo que en este caso Java provee con el api de Collections y tan solo con un store lo hacemos persistir en la base de datos! En un post posterior mejorare el modelo para que tenga Activación y Persistencia Automática y el manejo del borrado de referencias.

Saludos,
gish@c

Descargas (Proyecto Eclipse para Db4o7.12)

DVP 2010

Hoy quería agradecer a la gente de db4o por el reconocimiento de Db4o Most Valued Professional 2010 (DVP). Es muy interesante contribuir a ésta comunidad y aprender siempre algo nuevo del paradigma ODBMS, sobre todo si es un producto tan bueno como db4o (si aún no lo han revisado se los recomiendo al 100%).
Fue muy gratificante contribuir en alguno que otro feature del Object Manager (Java Version) (No nos fue tan mal, las 2 versiones liberadas suman 6000 descargas!!!), lamentablemente no da más el tiempo para seguir aportando de esa manera, y por otro lado desde hace bastante tiempo el Object Manager Enterprise es free, así que hay un buen producto de exploración de objetos; aunque sinceramente no estaría mal si algún miembro de la comunidad inicia un proyecto para crear otro explorer, en lo personal por el tipo de aplicación creo que encajaría perfecto con Netbeans Platform como base xD

En fin, muchas gracias por el reconocimiento!!! A partir de hoy agrego el badge de DVP 2010 al blog junto a los de los años anteriores 😀

Saludos,
gish@c

Java Series – Desmitificando Multithreading

Uuu ya con ese titulo ni ganas de leer este post verdad? xD  Pero tranquilidad que por la misma razón se llama “Desmitificando”, porque no es algo tan del otro mundo tener una aplicacion de esas caracteristicas en JAVA.
Hace algunos anhos cuando la tecnología de múltiples procesadores empezó a ubicarse en las maquinas de escritorio, no solo genero un impacto a nivel de hardware, sino también a nivel de software, ya que de alguna u otra manera nos dio la posibilidad de dar mas procesamiento a los equipos clientes, permitiéndonos mezclar el paradigma de Thin client, con un aprovechamiento real de todos los recursos de hardware sobre los que corren nuestras aplicaciones.
A estas alturas de la vida, el no utilizar al máximo los ciclos de CPU en nuestras aplicaciones no es solo un crimen tecnológico sino también un atentado al medio ambiente, y de esta premisa nacen conceptos como el Grid Computing, donde pones a disposición el tiempo ocioso de tu CPU para ser aprovechado por otros. En fin, el problema de trasfondo es que el hardware esta evolucionando mucho mas rápido que el software, y no todas las aplicaciones están preparadas para aprovechar la infraestructura sobre la que están siendo utilizadas, generando desuso de recursos.
En una experiencia previa, teníamos un cliente con un servidor de 8 procesadores y una cantidad considerable de RAM, y resulta que su personal de IT nos reporto como un Issue el hecho de que nuestra aplicación del lado servidor no estaba utilizando todos los recursos disponibles, es decir; le hicieron un monitoreo al uso de los procesadores cuando se ejecutaban procesos masivos y vieron que solo uno se utilizaba al máximo, el resto eran ciclos ociosos de cpu… Así que como buenos Ingenieros y Arquitectos de Software nos toco reestructurar la aplicación e identificar los procesos que se podían hacer de manera paralela para lanzar múltiples procesos y sacarle el jugo (como decimos por acá)  a los procesadores.
Si recordamos esas divertidas clases de Sistemas Operativos, se nos viene a la mente el gráfico del ciclo de un proceso donde la premisa era que un solo proceso corría a la vez, pero ahora eso varia dependiendo de la capacidad de los procesadores (Quad Core (4), I7(6), etc) y pues, como hacemos que nuestra aplicación utilice todos estos recursos??? A traves de Threads (Hilos).
(No podía omitir toda la historia anterior como introducción al tema, pero ya estamos en la parte interesante: el ejemplo! )
La mayoría de los lenguajes nos proveen las herramientas necesarias para cumplir con el objetivo de hacer aplicaciones multithreading (.Net, Scala, Google’s GO, etc, etc) y Java no es la excepción con sus clases para manejar threading.
El uso de threads implica básicamente identificar los procesos que se pueden ejecutar en paralelo, y una vez identificado no es complicado implementarlo; los dolores de cabeza se producen cuando tenemos que mezclar ese modelo con reacciones en UI (que levanten la mano los que alguna vez vimos una RED CROSS OF DEATH, o en .net el DataTable Index Corrupted) ya que los controles, y ciertas estructuras solo pueden ser manipuladas por un hilo a la vez.
El ejemplo de este post toma 2 procesos para realizar operaciones con threads:

  • Ejecutar asincronicamente una busqueda en iTunes de canciones por artista (Evita congelar el UI)
  • Simular un proceso de almacenamiento masivo de los resultados (Multithreading)

Es una molestia que se congele el UI mientras esperamos respuesta a la ejecución de un proceso, y un usuario es feliz si su aplicación le dice que esta haciendo, mientras que si se queda la pantalla congelada dirá “se colgó esta vaina” y seguramente le hara un kill como sea posible y llamara a service desk. Este caso esta representado en ejemplo de buscar a través del iTunes Store Search API, debido a que es una búsqueda en un servidor externo a través de la web, y dependemos básicamente de la latencia en la transmisión de datos para que la respuesta congele el UI hasta que se complete el request.
Hacemos la consulta esperando como resultado un texto en JSon y con la libreria Gson lo desarmamos en un abrir y cerrar de ojos, para luego mostrarlo en un JTable, así que fácilmente podemos identificar la parte del proceso que debe ser asincronica: La consulta al servidor web y el procesamiento de los resultados hasta que están en el formato esperado por el modelo del JTable.
Hasta ahí cumplimos con la primera parte de la premisa que era identificar la parte del proceso que podemos hacer asincronica, ahora la segunda parte es interactuar con el UI, para lo cual JAVA se ha encargado de abstraernos de este problema y desde su versión 6 incluye oficialmente al SwingWorker, el cual se encarga de ejecutarse en un contexto en el cual se ejecuta en background sin congelar el UI y permitiéndonos interactuar con los controles; la declaracion de la clase debe heredar de SwingWorker

public class iTunesSearchHandler extends SwingWorker<Object, Object>

Lo que queremos que se ejecute asincronicamente lo colocamos en el metodo doInBackground y para hacer alguna acción cuando termina el proceso utilizamos el metodo done

@Override
    protected Object doInBackground() throws Exception {
        label.setVisible(true);
        URL url = new URL(searchUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setReadTimeout(15 * 1000);
        connection.connect();
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        StringBuilder stringBuilder = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line + "\n");
        }
        parseJsonToList(stringBuilder.toString());

        return "";
    }

@Override
    protected void done() {
        super.done();
        label.setVisible(false);
    }

Las lineas donde cambia la visibilidad de un label es para mostrar el tipico “loading” en la pantalla mientras se ejecuta el request y así sabemos que esta haciendo la aplicación.
Como indique anteriormente la otra parte del ejemplo es simular un procesamiento masivo de la información que tenemos en memoria y típicamente es almacenarla en algún lugar; es importante identificar estas operaciones de base de datos como las típicas que podemos realizar paralelamente, (si son datos dependientes o referenciados entre si es vital manejar sincronizacion de procesos) ya que de nuestro DBMS esperamos un modelo ACID que nos garantice la integridad de la información. Por lo cual de manera descabellada vamos a lanzar un thread para grabar, el cual lanzara un nuevo thread por cada registro a guardar! 🙂

@Override
    protected Object doInBackground() throws Exception {
        label.setVisible(true);
        for(iTunesSearchResult item : data)
        {
            item.setRowIndex(data.indexOf(item));
            new iTunesSaveHandler(item).execute();
        }
        return "";
    }

Que conseguimos con eso? Pues que como ningún registro es dependiente del otro cada uno se ejecuta como una transacción independiente y ponemos a trabajar los procesadores al tener que ejecutar muchos procesos simultáneos 😛

Para mostrar el resultado del proceso, podemos ver con el jConsole los hilos que se ejecutan y ademas en el output vemos los mensajes donde el orden de almacenamiento no es necesariamente el orden en el que se fueron ejecutando los hilos:

Pantalla de aplicacion demo


Monitoreo antes de proceso masivo


Monitoreo despues de proceso masivo


Log de transacciones

Como conclusiones de los prints anteriores, podemos ver que en el monitoreo después de ejecutar la simulación de almacenamiento masivo se incrementan la cantidad de threads en la aplicación y el uso de cpu; mientras que en el log de transacciones podemos ver que los registros se van procesando no necesariamente en el orden que fueron lanzados, ya que cada transacción es independiente y finalmente depende de la prioridad que le asigne el cpu al thread para su ejecución.

Saludos,
gish@c

Descargar Proyecto Netbeans 6.8

Hágalo usted mismo!!!

El pan de cada día en el mundo del desarrollo de aplicaciones es encontrarnos con un problema que alguien ya solucionó… El día de hoy he sumado un punto más a la estadística ya que necesitaba generar un organigrama dinámicamente… Haciendo una búsqueda rápida en google encontre un excelente artículo en CodeProject http://www.codeproject.com/KB/cs/TreeGenerator.aspx.
Pero, como buenos consumidores siempre encontramos algo para estar inconformes, y por eso el origen de esta entrada en el blog; ya que al hechar un vistazo al artículo de cp lo primero que noté fue que a pesar de parecer muy funcional no estaba muy bonito gráficamente hablando… Y si está el código disponible… xq no añadirle un pequeño fix para que se vea como yo quiero? Muchas veces somos del grupo miro, utilizo y no toco… Como parte de la gran comunidad del “alguien ya lo hizo” no esta mal de vez en cuando ser ese que tuvo que hacerlo y lo publicó para el uso de los demás, lamentablemente algo no muy profesado en EC…
Yendo al grano, bastó remembrar mis andanzas por el mundo del GDI+ y darle un pequeño smooth a los cuadros del organigrama con unas cuantas líneas:

gr.CompositingQuality = CompositingQuality.HighQuality;
gr.SmoothingMode = SmoothingMode.AntiAlias
//Comentando lo hecho x el autor original
//gr.DrawRectangle(boxPen, currentRectangle);
//gr.FillRectangle(new SolidBrush(_BoxFillColor), currentRectangle);
GraphicsPath path = CornerRendererHelper.RoundRectangle(currentRectangle, 20);
gr.FillPath(new LinearGradientBrush(currentRectangle,_BoxFillColor, _BoxFillColor2, LinearGradientMode.BackwardDiagonal), path);

Aquí con la brocha gradient el organigrama se dibuja mucho mejor, y utilizando una utilería hecha por mi buen amigo John (seguramente ni se acuerda que hizo esa clase) el rectangulo se redondea…

He aquí las diferencias:

Code Project:

Luego de las mejoras

Go ahead! Hágalo usted mismo.

Los fuentes modificados de la librería se pueden encontrar aqui

Salu2
gish@c