Archivo de la categoría ‘Nivel Avanzado’

Generación de código fuente con Maven

Jueves, 23 de junio de 2011

En este artículo veremos como mediante la capacidad de filtrado de archivos que dispone Maven podremos generar una clase java que posteriormente se compilará formando parte del proyecto como si fuera un fichero de código normal.

Este articulo requiere una serie de conocimientos sobre Maven. Para más información puedes visitar la página del proyecto de Maven.

Como ejemplo útil vamos a generar una clase con la información del nombre de proyecto y la versión que se han indicado en el fichero POM que luego podremos usar en nuestro código normal y así conseguir que ajustando la configuración estándar de Maven, automáticamente estos cambios queden presentes en el código fuente.

Estructura de directorios

La estructura de directorios del proyecto sera como la de cualquier proyecto Maven solo que con una carpeta añadida para contener los ficheros con las plantillas para los fuentes generados.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.
├── pom.xml
└── src
    └── main
        ├── java
        │   └── es
        │       └── excelsit
        │           └── sourceGenerationExample
        │               └── ClaseNormal.java
        └── source-templates
            └── es
                └── excelsit
                    └── sourcesGenerationExample
                        └── Version.java

En la línea número 10 del listado de directorios anterior vemos una carpeta llamada source-template. Esta será la carpeta que contendrá las plantillas.

Configuración de Maven

Para que Maven se entere de la existencia de esta carpeta hay que configurarlo en el POM.

1
2
3
4
5
6
7
8
9
<resources>
  [...]
  <resource>
    <directory>src/main/source-templates</directory>
    <filtering>true</filtering>
    <targetPath>../generated-sources/filtering</targetPath>
  </resource>
  [...]
</resources>

Mediante el código anterior incluiremos una carpeta de recursos que usará el filtrado de propiedades de Maven (línea 5) y que el resultado se copiará en la carpeta /target/generated-sources/filtering (línea 6). Para el elemento targetPath hay que tener en cuenta que la ruta es relativa al directorio de compilación (más información sobre la estructura de directorios estandar de Maven
) y que por defecto en el la generación de codigo se realiza dentro del directorio /target/generated-sources/NOMBRE_PLUGIN

El proximo paso es indicarle al plugin de compilación que existe un nuevo directorio con fuentes que también debe tener en cuenta.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<plugins>
    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <executions>
            <execution>
                <id>add-source</id>
                <phase>generate-sources</phase>
                <goals>
                    <goal>add-source</goal>
                </goals>
                <configuration>
                    <sources>
                        <source>${basedir}/target/generated-sources/filtering</source>
                    </sources>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>

De la línea 12 a la 16 vemos la parte de configuración del plugin de compilación de Maven donde le hemos indicado una nueva carpeta con fuentes.

Proyecto de ejemplo.

Para que tengais una visión de como quedaría el proyecto aquí tenéis el ejemplo sobre generación de código mediante el filtrado de Maven completo. Para ver el resultado podeis usar el siguiente comando:

mvn -Dexec.classpathScope=runtime "-Dexec.args=-classpath %classpath es.excelsit.sourceGenerationExample.ClaseNormal" -Dexec.executable=/usr/lib/jdk/bin/java process-classes org.codehaus.mojo:exec-maven-plugin:1.2:exec

 

Etiquetas: , , ,

Trabajar con juegos de resultados de gran tamaño.

Jueves, 26 de febrero de 2009

Adaptive response buffering es una nueva funcionalidad de la versión 1.2 driver JDBC de Sql Server que permite reducir el uso de memoria y los retrasos en la ejecución de consultas que devuelven un conjunto de resultados grande.

Mediante este método, el controlador JDBC obtiene los resultados de la consulta a la base de datos según se van necesitando, en lugar de todos de una vez. Además, el controlador JDBC elimina automaticamente de la memoria los resultados que dejan de ser accesibles por la aplicación.

Imaginemos que tenemos que hacer una consulta que devuelve una columna con un campo LOB de 600MB.  Seria inviable mantener un buffer para acceder mediante los métodos definids en la interfaz de los tipos Blob/Lob ya que estos métodos necesitan que el campo al completo este cargado en memoria. En su lugar, podemos acceder mediante un ImputStream obtenido usando los metodos getBinaryStream, getCharacterStream, o getAsciiStream del ResultSet. De esta forma, solo se necesita una cantidad de memoria pequeña y de un tamaño fijo para poder leer ese campo.

Hay varias formas para indicar al controlador JDBC que use Adaptative Response Buffering:

  • Añadir “responseBuffering=adaptive” a la URL de conexión
     Connection connection = null;
    try {
        String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
        String url = "jdbc:sqlserver://localhost:1433;database=BBDD;responseBuffering=adaptive";
        String username = "usuario";
        String password = "clave";
     
        // Cargar el Driver
        Class.forName(driverName);
     
        // Crear la conexion
        connection = DriverManager.getConnection(url, username, password);
    } catch (ClassNotFoundException e) {
        // No se ha encontrado el driver.
        e.printStackTrace();
    } catch (SQLException e) {
        // No se ha podido conectar con la base de datos
        e.printStackTrace();
    }
  • Usar el método SQLServerDataSource.setResponseBuffering(“adaptive”) del DataSource

Si nuestra aplicación siempre accede de manera consecutiva  a las filas de nuestro ResultSet y no necesita acceder a el de manera aleatoria (es decir, recorremos el juego de resultados una única vez y de pricipio a fin), podemos usar el siguiente código para indicar al controlador que descarte las filas por las que ya se han pasado:

ps = con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY );

Etiquetas: , , , , , ,

log4j: nivel de prioridad personalizado

Martes, 17 de febrero de 2009

Por defecto, Log4j tiene 6 niveles de prioridad para las trazas. Por orden de detalle de mayor a menor se definen:

  • trace: Permite mayor nivel de detalle.
  • debug: Trazas que permiten la depuración de errores. Normalmente este nivel solo se usa en las fases de desarrollo de la aplicación.
  • info: Se usa para trazas meramente informativas.
  • warn: Se usa para trazas de alerta. No tienen por que afectar al correcto funcionamiento de la aplicación.
  • error: Se usa para guardar trazas de errores de los que la aplicación puede recuperarse.
  • fatal: se usa para guardar trazas antes de que la aplicación aborte la ejecución. Son errores de los que la aplicación no puede recuperarse.

Además se definen 2 niveles especiales:

  • all: habilita todos niveles de detalle
  • off: inhabilita el envío de trazas.

Es importante tener en cuenta que al habilitar un nivel de detalle mayor, se habilitan todos los niveles que contiene.

ALL>TRACE>DEBUG>INFO>WARN>ERROR>FATAL>OFF

Antes de crear un nuevo nivel de prioridad hay que cuestionarse si es realmente necesaro. En muchas ocasiones se confunden los niveles de prioridad con categorías en las trazas.

Por ejemplo, si se quieren diferenciar las trazas que se producen cuando un usuario inicia sesión de otras que se producen cuando el usuario realiza ciertos accesos a determinados sistemas, se pueden usar un log con un determinado nombre.

Logger logSesion = Logger.getLogger("sesion");
Logger logAcceso = Logger.getLogger("acceso");

El parámetro que se pasa al crear el logger no tiene por que ser estrictamente el nombre de la clase, sino cualquier otro nombre que nos pueda ser descriptivo.

Si aun sabiendo esto decides a crear un nuevo nivel, esto se realiza heredando de la clase Level.

package es.excelsit.blog.log;
import org.apache.log4j.Level;
/**
* My own {@link org.apache.log4j.Level} for logging.
*
* @author fernando
*
*/
public class MiNivel extends Level {
   /**
    * El valor para este nivel.
    */
   public static final int MI_NIVEL_INT = DEBUG_INT - 10;
 
   /**
    * Texto identificativo para el nivel.
    */
   public static final String MI_NIVEL_STR = "MI_NIVEL";
 
   /**
    * {@link Level} representa a este nivel
    */
   public static final Level MI_NIVEL = new MiNivel(MI_NIVEL_INT, MI_NIVEL_STR, 7);
 
   /**
    * Constructor
    *
    * @param arg0 entero que indica el nivel
    * @param arg1 cadena identificativa de este nivel
    * @param arg2 nivel de syslog asociado
    */
   protected MiNivel(int arg0, String arg1, int arg2) {
      super(arg0, arg1, arg2);
   }
 
   /**
    * Comprueba si s es "MI_NIVEL". Si es afirmativo devuelve
    * {@link MiNivel#MI_NIVEL}, sino se llama a
    * {@link MiNivel#toLevel(String, Level)} con el nivel por defecto.
    * {@link Level#DEBUG}
    *
    * @see Level#toLevel(java.lang.String)
    * @see Level#toLevel(java.lang.String, org.apache.log4j.Level)
    *
    */
   public static Level toLevel(String s) {
      if ( s != null &amp;&amp; s.toUpperCase().equals("MY_TRACE")) {
         return MI_NIVEL;
      }
      return (Level) toLevel(s, Level.DEBUG);
   }
 
   /**
    * Comprueba que val es igual a {@link MiNivel#MI_NIVEL_INT}. Si es afirmativo devuelve
    * {@link MiNivel#MI_NIVEL}, sino se llama a {@link MiNivel#toLevel(int, Level)}
    * pasando como argumento el nivel por defecto {@link Level#DEBUG}
    *
    * @see Level#toLevel(int)
    * @see Level#toLevel(int, org.apache.log4j.Level)
    *
    */
   public static Level toLevel(int val) {
      if (val == MI_NIVEL_INT) {
         return MI_NIVEL;
      }
      return (Level) toLevel(val, Level.DEBUG);
   }
 
   /**
    * Comprueba que val es igual a {@link MiNivel#MI_NIVEL_INT}. Si es
    * afirmativo devuelve
    * {@link MiNivel#MI_NIVEL}, sino se llama a
    * {@link Level#toLevel(int, org.apache.log4j.Level)}
    *
    * @see Level#toLevel(int, org.apache.log4j.Level)
    */
   public static Level toLevel(int val, Level defaultLevel) {
      if (val == MI_NIVEL_INT) {
         return MI_NIVEL;
      }
      return Level.toLevel(val, defaultLevel);
   }
 
   /**
    * Comprueba si s es "MI_NIVEL". Si es afirmativo devuelve
    * {@link MiNivel#MI_NIVEL}, sino se llama a
    * {@link Level#toLevel(java.lang.String, org.apache.log4j.Level)}
    *
    * @see Level#toLevel(java.lang.String, org.apache.log4j.Level)
    */
   public static Level toLevel(String sArg, Level defaultLevel) {
      if (sArg != null &amp;&amp; sArg.toUpperCase().equals("MI_NIVEL")) {
         return MI_NIVEL;
      }
      return Level.toLevel(sArg, defaultLevel);
   }
}

En el constructor vemos que se necesitan 3 parámetros:

  • El primero es un número que indica la posicion en la que se encuentra este nuevo nivel. Podemos usar cualquier numero a nuestra elección entre los predefinidos para los niveles por defecto:
    • ALL = -2147483648
    • DEBUG = 10000
    • ERROR = 40000
    • FATAL  = 50000
    • INFO = 20000
    • OFF = 2147483647
    • WARN = 30000
  • El segundo es una cadena que identifica este nivel.
  • El tercero es el nivel equivalente para Syslog de UNIX
    #define LOG_EMERG       0       /* system is unusable */
    #define LOG_ALERT       1       /* action must be taken immediately */
    #define LOG_CRIT        2       /* critical conditions */
    #define LOG_ERR         3       /* error conditions */
    #define LOG_WARNING     4       /* warning conditions */
    #define LOG_NOTICE      5       /* normal but significant condition */
    #define LOG_INFO        6       /* informational */
    #define LOG_DEBUG       7       /* debug-level messages */

Etiquetas: , ,