martes, 25 de septiembre de 2007

Ingenieria del Software: Ingenieria Inversa

Introducción sobre la utilización de la Ingeniería Inversa o Reverse Engineering.

En este caso y razón del motivo por el que fue creado este blog, trataré el tema de la Ingeniería Inversa sobre aplicaciones en Java. Es decir, veremos como a través de un proyecto Java obtendremos su UML.

Utilizaremos la nueva versión de NetBeans IDE 6.0, este entorno de desarrollo ha introducido una serie de herramientas que se utilizan para la creación de proyectos de software como son las siguientes:

- Diagrama de Paquetes
- Diagrama de Secuencia
- Modelo del Dominio
- Diagrama de Casos de Uso
- Diagrama de Clases
- Diagrama de Comunicación
- Diagrama de Clases de Diseño

A parte de estas versátiles herramientas de desarrollo de software, a través de la herramienta de ingeniería inversa obtendremos los diagramas creados a partir de un proyecto de una aplicación de Java existente.

En este caso la aplicación existente será una aplicación bancaria, crearemos un nuevo proyecto de UML escogiendo la opción de Reverse Engeened Java-Model Platform

Después de crear nuestro proyecto de UML mediante la Ingeniería Inversa tendremos la opción de generar los diferentes diagramas, en projects nos encontraremos con Model, aquí encontraremos las clases obtenidas en bankpack (en mi caso) y generaremos un diagrama en cualquiera (click derecho de ratón sobre la clase, new y elegir diagrama) en nuestro caso escogeremos un diagrama de clases de diseño de bankaccount:

Y una serie de diagramas de la aplicación en conjunto, simplemente seleccionando las clases que queremos representar en nuestro proyecto de UML y voilá


Al mismo tiempo y una gran ventaja, podemos generar también código fuente para un proyecto de Java que hayamos creado previamente, simplemente escogiendo la opción generate code y creará un archivo o archivos con código fuente en nuestro nuevo proyecto.

ej: BankAccount.java
package bankpack;


/**
* @author Administrator
*/

abstract class BankAccount implements Account {
private double balance;
private String accountNumber;
private double interestRate;
private History mHistory = new History();

/**
* Creates a new instance of BankAccount
*/

public BankAccount () {
}

public BankAccount (String accNumber, double initialAmount) {
}

public BankAccount (String accNumber, double initialAmount, double rate) {
}

public double getBalance () {
return 0.0;
}

public String getAccountNumber () {
return null;
}

public void withdraw (double val) throws bankpack.NoAvailableFundsException {
}

public void deposit (double val) {
}

public void setAccountNumber (String val) {
}

public boolean equals (Object o) {
return true;
}

public int hashCode () {
return 0;
}


public String toString () {
return null;
}

public History getHistory () {
return null;
}

public void setHistory (History val) {
}

public String getMessage () {
return null;
}

public double getInterestRate () {
return 0.0;
}

public void setInterestRate (double val) {
}

private void setBalance (double val) {
}

private void noAvailableFunds () throws bankpack.NoAvailableFundsException {
}

}



Un saludo a todos y espero que aprovechéis esta herramienta de Netbeans para crear proyectos UML de vuestras aplicaciones o otras como JEnigma...

One sign of a good programmer is their absolute need to understand



sábado, 1 de septiembre de 2007

MySQL: Niveles de aislamiento (Isolations levels)

En esta ocasión voy a abordar la temática de las bases de datos. Para ello me voy a referir a un DBMS tan extendido en uso como criticado por sus carencias con respecto a servidores de bases de datos comerciales: MySQL. Concretamente me voy a referir a una característica que a mí me ha encantado y es que MySQL nos permite especificar el nivel de aislamiento que deseamos para las transacciones.
Doy por hecho que la persona que está leyendo este artículo conoce, cuanto menos, lo que son las transacciones dentro de los SGBDR. Las especificaciones ANSI establecen que una transacción debe de cumplir con la norma ACID:

  • Atomic (Atomicidad): Establece que las operaciones que se realizan dentro de una transacción son efectuadas de manera atómica. Esto significa que la transacción bien se cometa o bien se anule siempre debe de ser al completo.

  • Consistent (Consistencia): Esta es una característica que más bien depende del programador del DBMS. Define que la base de datos debe de quedar de manera consistente tras la finalización (con éxito o no) de la transacción.

  • Isolation (Aislamiento): Determina que las distintas transacciones que se estén ejecutando lo hagan de forma aislada. Esto es, que los resultados de una transacción no son visibles por el resto de transacciones en curso hasta que dicha transacción finalize.

  • Durable (Durable): Esta regla impone que los cambios realizados por una transacción cometida deben de ser durables en el tiempo, deben de permanecer.
Pues bien, repasado este concepto ya podemos mostrar los problemas asociados a los distintos tipos de transacciones:

  • Dirty reads (Lecturas sucias): Es el problema más importante de todos. Supone que las transacciones en curso puedan leer el resultado de otras transacciones aún no confirmadas. Por ejemplo, vamos a suponer que tenemos dos transacciones activas (A y B). Inicialmente la transacción A lee un valor X de una tabla que, por ejemplo, es 0. Durante dicha transacción el valor de X se cambia a 10, pero aún la transacción no se ha cometido, por lo que en la tabla X = 0. Ahora la transacción B accede al valor X y obtiene ¡¡X = 10!! un valor que está usando A y que aún no se ha cometido. Supongamos que ahora se anula la transacción A. El resultado sería X = 0 en la tabla y X = 10 en la transacción B por lo que hemos llegado a un estado muy grave de inconsistencia.

  • Non-Repeatable reads (Lecturas no repetibles): Ocurre cuando una transacción activa vuelve a leer un dato cuyo valor difiere con respecto al de la anterior lectura. Lo vemos más claro con un ejemplo. Supongamos que una transacción activa, A, lee un valor X = 0. En este momento otra transacción B modifica el valor de X, por ejemplo X = 10, y se comete dicha transacción. Si ahora duracte la transacción A se vuelve a leer el valor X obtendríamos 10 en lugar del 0 que se esperaba. Aunque a primera vista este problema no parezca muy importante en realidad sí que lo es, sobre todo cuando X es una clave primaria o ajena. En este caso se puede originar una gran pérdida de consistencia en nuestra base de datos.

  • Phantom reads (Lecturas fantasma): Este supone el menor problema que se nos puede plantear con respecto a las transacciones. Sucede cuando una transacción en un momento lanza una consulta de selección con una condición y recibe en ese momento N filas y posteriormente vuelve a lanzar la misma consulta junto con la misma condición y recibe M filas con M > N. Esto es debido a que durante el intervalo que va de la primera a la segunda lectura se insertaron nuevas filas que cumplen la condición impuesta en la consulta.

Debido a estos problemas el ANSI establece diferentes niveles de aislamiento (isolations levels) para solventarlos. Hay que tener en cuenta que a mayor nivel de aislamiento mayores son los sacrificios que se hacen con respecto a la concurrencia y al rendimiento. Vamos a enumerar los niveles de aislamiento desde el menor hasta el mayor:

  • Read uncommitted (Lectura sin confirmación): En la práctica casi no se suele utilizar este nivel de aislamiento ya que es propenso a sufrir todos los problemas anteriormente descritos. En este nivel una transacción puede ver los resultados de transacciones aún no cometidas. Podemos apreciar que en este nivel no existe aislamiento alguno entre transacciones.

  • Read committed (Lectura confirmada): Es el predeterminado para la mayoría de gestores de bases de datos relacionales. Supone que dentro de una transacción únicamente se pueden ver los cambios de las transacciones ya cometidas. Soluciona el problema de las lecturas sucias, pero no el de las lecturas no repetibles ni tampoco el de las lecturas fantasmas.

  • Repeatable read (Lectura repetible): Define que cualquier tupla leída durante el transcurso de una transacción es bloqueada. De esta forma se soluciona, además de las lecturas sucias, el problema de las lecturas no repetibles. Aunque en dicho nivel se siguen dando las lecturas fantasmas.

  • Serializable (Lecturas en serie): Soluciona todos los problemas descritos. Para ello ordena las transacciones con el objetivo de que no entren en conflicto. Este nivel de aislamiento es bastante problemático ya que es, con diferencia, el que más sacrifica en rendimiento y concurrencia.

Hasta aquí hemos llegado con la teoría, ahora vamos a ver como MySQL nos permite trabajar con cualquiera de estos niveles de aislamiento (eso sí, siempre que utilicemos el motor de tabla InnoDB). Si deseamos conocer el nivel de aislamiento con el que actualmente está trabajando nuestro servidor no tenemos más que lanzar la sentencia: SHOW VARIABLES LIKE 'tx_isolation'. De esta manera la salida que obtendremos será similar a la siguiente:

En mi caso con MySQL 5 aparece que el nivel de aislamiento es el de REPEATABLE-READ. Para cambiarlo directamente desde la línea de comando de MySQL lo podemos hacer con la sentencia:

SET {GLOBAL | SESSION} TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITED | REPEATABLE READ | SERIALIZABLE}

A través de ella podemos especificar el nivel de aislamiento tanto para la sesión actual (SESSION) como para el servidor (GLOBAL). También es posible establecer el nivel de aislamiento al arranzar el servidor con el argumento --transation-isolation o modificando el fichero de configuración del servidor my.cfg.
Para terminar sólo quiero especificar que esta cualidad de mysql es válida a partir de la versión 4.0.5 y al igual que en el artículo anterior estoy totalmente abierto a sugerencias, críticas y demás. Un saludo.

jueves, 9 de agosto de 2007

Generación e impresión de informes en formato PDF desde Java con software libre

Hola Mundo!
Bueno, como se puede apreciar, quiero comenzar con este blog con buen humor y con un tema que, a mi parecer, resulta muy interesante. Hace unos días me topé con la tediosa labor de abordar el desarrollo de una aplicación java que fuera capaz de generar informes en formato PDF, y lo más difícil, imprimirlos. Tras muchos calentamientos de cabeza (sobre todo con el asunto de la impresión bajo Java) y muchísimas horas googleando llegué a encontrar la solución a este problema, y lo que es mejor aún, a través del uso de software libre ;-) . Seguidamente detallo el proceso.
Lo primero que necesitamos es descargar la herramienta de generación de informes DataVision (http://datavision.sourceforge.net). Esta herramienta, aunque sencilla, es de gran flexibilidad y se integra a la perfección con Java. Una vez descargada la descomprimimos y accedemos al directorio que se ha generado. Llegados aquí vemos varios archivos, los más importantes datavision.bat y datavision.sh. Ejecutaremos el que corresponda en función a nuestro sistema operativo y nos aparecerá un cuadro de diálogo inicial en el que se nos muestran las opciones iniciales:


Aquí le diremos a DataVision que deseamos crear un nuevo informe, por lo que pinchamos sobre el botón Start a New Report. Con ello este cuadro de diálogo se cerrará para dejar paso a una ventana que, a priori, nos puede intimidar ya que parece que nos pide gran cantidad de información. No hemos de asustarnos ya que ese no es el objetivo de esta última ventana, sino que lo que nos requiere es información acerca de la conexión con el DBMS (Servidor de Bases de Datos Relacionales). Habremos de cumplimentar dicha información:

Para el que ande un poco perdido:
Nombre de Clase del Controlador: Nombre de la clase que sirve como driver para JDBC. En mi caso, como estoy utilizando el SGBDR MySQL, utilizo la clase com.mysql.jdbc.Driver que previamente descargué de http://www.mysql.org. Por cierto, nunca hay que olvidar añadir el paquete jar que contiene dicha clase a la variable de entorno CLASSPATH. Información de la Conexión: Es la cadena de conexión JDBC. La que he usado yo es jdbc:mysql://localhost/blog. Ya que estoy accediendo, a través de JDBC, a la base de datos blog de un servidor MySQL que tengo instalado en mi máquina local.
Nombre de la Base de Datos, Nombre de Usuario y Contraseña: Bueno, con respecto a esto creo que hay poco que decir.
Proporcionados los datos correctos pulsamos sobre el botón Aceptar y, si todo ha ido bien, nos aparecerá la siguiente ventana:


En ella nos aparecen las secciones del informe y una barra de menú, a través de la cual se puede acceder a las distintas opciones. Si no estamos familiarizados con las secciones de un informe podemos consultar la documentación de DataVision (en formato HTML). En mi caso voy a realizar el informe para una tabla de alumnos, de los que almaceno su DNI, nombre, sexo y fecha de nacimiento.
Podemos comenzar añadiendo al encabezado del informe un título, como por ejemplo: Informe sobre los alumnos. Para ello seleccionamos Insertar->Texto. Pinchamos la sección Encabezado del reporte y escribimos el texto. Si deseamos modificar el formato del texto no tenemos más que acceder a Formato->Formato de campo. Ahora vamos a insertar lo que verdaderamente nos importa: los datos de nuestra tabla. Para hacer esto pulsamos en Insertar->Campo de Base de datos. Nos parece una nueva ventana en la cual accedemos a la tabla que deseemos y arrastramos los campos necesarios sobre la sección Detalle del informe. Una vez situados, si es necesario, podemos acceder al menú Formato el cual nos posibilita poner a punto ciertos aspectos tales como la alineación y el tamaño. Finalmente, en el pie de pagina vamos a insertar dos campos especiales: numero de página y nombre del autor. Para ello procedemos de forma similar a la que hemos hecho para insertar los campos de la tabla. Si hemos seguido estas indicaciones el aspecto final del informe es el siguiente:


Para guardar este informe y que pueda ser accedido a través de Java vamos a pulsar sobre Archivo->Guardar. Elegimos la ruta donde queremos almacenarlo y le nombraremos, por ejemplo, alumnos.xml. Antes de guardar el fichero también es interesante acceder al menú Archivo->Tamaño de papel y modificar el valor dado, ya que lo más normal es generar el informe para tamaño A4.
Llegados hasta aquí ya estamos listos para generar nuestro informe en formato PDF desde Java. A continuación se muestra un sencillo programa que genera el informe anterior en PDF y establece el nombre del autor del informe en tiempo de ejecución:

import jimm.datavision.*;
import jimm.datavision.layout.pdf.PDFLE;
import java.sql.Connection;
import java.sql.DriverManager;
import java.io.FileOutputStream;
import java.io.File;

public class Ejemplo{
public static void main(String[] args){
Report r = new Report();
Connection con = null;

try{
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost/blog",
"root", "passwd");

r.setDatabaseConnection(con);
}
catch(Exception e){
System.err.println(e.getMessage());
System.exit(-1);
}

FileOutputStream pdf = null;
File xml = null;

try{
pdf = new FileOutputStream("C:\\alumnos.pdf");
xml = new File("C:\\alumnos.xml");
}
catch(Exception e){
System.err.println(e.getMessage());
System.exit(-2);
}

try{ r.read(xml); }
catch(Exception e){ System.exit(-3); }

r.setAuthor("Juan Ángel Gascón Moya");
r.setLayoutEngine(new PDFLE(pdf));
r.runReport();
}
}
Para poder compilar este programa es necesario tener en el CLASSPATH los ficheros jar que contiene el directorio bin de DataVision y el driver del DBMS. Como se aprecia en el fuente DataVision nos proporciona una clase, Report, que es la que vamos a utilizar para generar nuestros informes. Una vez creado un objeto de dicha clase debemos de proporcionarle la información necesaria para que sea capaz de acceder a la base de datos. Existen varias maneras, pero en este ejemplo se ha optado por llamar al método setDatabaseConnection(java.sql.Connection) el cual recibe como parámetro un objeto Connection que esté inicializado. Lo siguiente que hay que hacer es indicarle al objeto Report cuál es el fichero XML que generamos anteriormente con DataVision. Para hacer esto utilizamos el método read(java.io.File). Dicha operación recibe un objeto de tipo File que ya esté inicializado con el fichero XML al que se va a acceder. Ahora hemos de establecer el formato con el que deseamos que se genere el informe, por eso hacemos uso del método setLayoutEngine(jimm.datavision.layout.LayoutEngine). DataVision nos permite generar informes en múltiples formatos como HTML, texto separado por comas, XML, LaTeX... En nuestro caso como vamos a crear un informe en formato PDF utilizamos la clase jimm.datavision.layout.pdf.PDFLE. El único constructor de esta clase recibe como argumento un objeto de la clase abstracta java.io.OutputStream. En este caso concreto se ha utilizado un objeto FileOutputStream que indica el fichero PDF que se va a generar. También se puede apreciar que se ha establecido en tiempo de ejecución el nombre del autor del informe. Es importante que los métodos como este, que establecen ciertas propiedades del informe, sean llamados tras la ejecución del método read(java.io.File) ya que en otro caso no surtirán ningún efecto. En la última línea se hace una llamada al método runReport() el cual es el encargado de realizar la generación del informe a partir de los datos que previamente le hemos proporcionado. Además de este método existe el método run() que realiza exactamente la misma función, pero en un hilo (thread) a parte.
Pues bien lo único que nos queda es compilar y luego ejecutar este programa para obtener nuestro informe en formato PDF. Como ya se ha dicho antes, es muy importante que todos los ficheros jar utilizados estén en el CLASSPATH. Si hacemos uso de un buen entorno de desarrollo nuestra labor se verá bastante simplificada. Como opinión personal recomiendo el IDE netbeans (http://www.netbeans.org) el cual considero como un excelente entorno de desarrollo para java, y además es software libre.

Bueno, hasta aquí hemos llegado para realizar la generación de informes en formato PDF, pero ahora viene la parte más complicada: imprimir un archivo PDF desde java. Digo que esta parte es complicada y el que halla tenido que abordar el tema de impresión desde java, y en especial, la impresión de ficheros en formato PDF sabe porque lo digo. Para realizar esta labor vamos a hacer uso de, como no, software libre: PDFBox (http://www.pdfbox.org/).

Los propios autores de PDFBox lo definen como una librería open source para java trabajar con documentos PDF. Además incluye un conjunto de programas (hechos en java, por supuesto) para trabajar con ficheros PDF desde la línea de comando. Estos programas van desde encriptadores y extractores de texto hasta conversores de PDF a imagen y, el más importante en el tema que abordamos, impresores de PDF. Concretamente el programa que nos permite imprimir un fichero PDF desde el prompt de nuestra línea de comando es el PrintPDF. Este comando, además de las opciones que se desee incluir, recibe un único argumento: el documento PDF a imprimir. Si deseamos imprimir el informe PDF generado anteriormente operaremos de la siguiente forma:
  • Una vez descargado PDFBox lo descomprimimos.
  • Accedemos a través de una terminal al directorio lib.
  • Lanzamos el comando para imprimir. En mi caso, que me he descargado la versión 0.7.3 y estoy utilizando windows, lo hago de la siguiente manera:
 prompt> java -cp ..\external\FontBox-0.1.0-dev.jar;
.\PDFBox-0.7.3.jar; org.pdfbox.PrintPDF
c:\alumnos.pdf
Nótese que además de incluir el fichero jar de PDFBox en el CLASSPATH también he añadido el paquete FontBox ya que es requerido por el anterior. También hay que tener cuidado si estamos en Linux ya que el separador que debemos de usar para la variable de entorno es : en lugar del ; que se utiliza en Windows. Al lanzar el comando nos aparecerá una ventana similar a esta:


A partir de aquí podremos imprimir el documento. Las formas de integrar esta funcionalidad en nuestra aplicación java son varias. Podemos usar directamente el comando mediante Runtime.getRuntime().exec(java.lang.String), o, como al ser open source el código fuente, está incluido, podemos adaptar el programa PrintPDF a nuestra aplicación codificándolo como una clase. Esto es una decisión personal del programador por lo que no indagaré más en este asunto.

Y ya poco más que decir. Tanto DataVision como PDFBox son compatibles con apache ant (http://ant.apache.org). Esto hace mucho más fácil la modificación y recompilación del fuente. También, quiero decir que en este artículo he expuesto una mínima parte, tanto de DataVision como de PDFBox. Ambas aplicaciones constan de multitud de clases y librerías, además son muchas sus funcionalidades. Por ello animo a cualquiera que tenga interés en este software que pase por las webs de estas respectivas aplicaciones y consulte su documentación.

Espero que este artículo halla sido de utilidad y estoy abierto a cualquier comentario. Un saludo.