Entradas con la etiqueta ‘java’

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: , , ,

Creación de una plantilla para proyecto con Maven

Sábado, 9 de abril de 2011

Como ya hemos dicho, Maven es una herramienta para compilar y gestionar proyectos.  Es muy usada en proyectos Java, aunque es genérica y podría ser usada para cualquier lenguaje. Está basada en que el proyecto tiene una estructura de directorios específica que veremos posteriormente, aunque es totalmente configurable.

Creación de una plantilla vacía.

Una plantilla para Maven tiene la siguiente estructura:

archetype
|-- pom.xml
`-- src
    `-- main
        `-- resources
            |-- META-INF
            |   `-- maven
            |       `--archetype.xml ó archetype-metadata.xml
            `-- archetype-resources
                |-- pom.xml
                `-- src
                    |-- main
                    |   `-- java
                    `-- test
                        `-- java

Como puedes ver, sigue la estructura de cualquier proyecto Maven. Hay varios ficheros que hay que tener en cuenta:

  • pom.xml: En el directorio raíz se encuentra el fichero POM con la información para gestionar el proyecto para generar la plantilla.
  • pom.xml: Será el POM del proyecto que se genere usando la plantilla que estamos creando. Podemos decir que el contenido de la carpeta archetype-resources será el contenido del proyecto que generemos con esta platilla.
  • archetype.xml ó archetype-metadata.xml: Describe los ficheros que contendrá el proyecto que se genere con esta plantilla. El primero (archetype.xml) es para la versión 1 del plugin, mientras que el segundo (archetype-metadata.xml) se usa en la versión 2 o superiores.

En el directorio archetype-resources estarán los ficheros que contendrá el proyecto que generemos con esta plantilla.

Puedes crear esa estructura a mano, pero es más sencillo usar el plugin Archetype (página del plugin Archetype) que te permite crear esa estructura sin esfuerzo. Para esto usaremos el siguiente comando:

mvn archetype:generate

Creación de una plantilla basándose en un proyecto existente.

Si ya tienes un proyecto sobre el que quieras crear un modelo para poder generar proyectos similares puedes ejecutar el siguiente comando:

mvn archetype:create-from-project

Una vez que Maven haya finalizado su ejecución, en la carpeta target/generated-source tendrás la plantilla para tu proyecto con los ficheros pom.xml, archetype.xml y archetype-metadata.xml generados de forma automática.

Es posible que necesites realizar alguna modificación en los ficheros para adaptar mejor la plantilla a tus necesidades. Por ejemplo:

  • Organizar el código en un paquete
  • Definir variables en el contenido del fichero para adaptar ciertos nombres a tus necesidades.
  • Personalizar los nombres de ficheros según unas variables.

Organizar los ficheros en paquetes

Dentro del fichero archetype-metadata.xml encontraremos una serie de descriptores de los directorios en los que se compone nuestro proyecto. Un ejemplo sería:

<fileSet filtered="true" packaged="true" encoding="UTF-8">
   <directory>src/main/java</directory>
   <includes>
      <include>**/*.java</include>
   </includes>
</fileSet>

Como vemos es bastante descriptivo, pero vamos a explicar la parte que nos interesa.  En el elemento fileSet vemos el atributo packaged="true", mediante este atributo indicamos a maven que al prosesar el contenido de ese directorio use una estructura de paquetes. Si por ejemplo, el contenido de ese directorio es

src/main/java
          |-- common
          |    `-- Objeto.java
          |-- servicio
                `-- Objeto.java

en el proyecto resultado estará dentro de una estructura de paquetes indicado mediante la variable ${package}. Si por ejemplo el contenido de esta variable es es.excelsit.miProyecto el resultado sería:

src/main/java
    es
    `--excelsit
        `-- miProyecto
              |-- common
              |   `-- Objeto1.java
              |-- servicio
                  `-- Objeto2.java

El fichero para la clase Objeto1 y Objeto2 deberá contener la instrucción package es.excelsit.miProyecto;. Para ello usaremos la siguiente sintaxis para la definición del paquete:

package ${package};

Uso de variables en la plantilla.

Si abrimos alguno de los ficheros de nuestra plantilla veremos que todos comienzan con algo asi:

#set ( $symbol_pound = '#' )
#set ( $symbol_dollar = '$' )
#set ( $symbol_escape = '\' )

Como vemos por su sintaxis, el plugin usa de forma interna Velocity para la generación y adaptación de los ficheros de nuestra platilla de proyecto. Esto nos permite mucha libertad a la hora de crear el contenido del fichero. Hay que tener en cuenta que como  los símbolos almoadilla (#), dolar ($) y barra invertida (\) son caracteres especiales, si los queremos usar deberemos hacerlo mediante sus variables $symbol_pound, $symbol_dollar y $symbol_escape correspondientes.

Añadir propiedades propias.

Podremos crear propiedades propias de la siguiente forma:

#set ( $miPropiedad = 'valor' )

En este punto tendremos toda la libertad que ofrece Velocity para concatenar cadenas, obtener la fecha del sistema, etc. Estas propiedades que creemos las podemos usar dentro del contenido del fichero mediante:

$miPropiedad

También tenemos la posibilidad de renombrar ficheros según nuestras necesidades durante el proceso de generación de nuestro proyecto a partir de la plantilla. Para poder usar variables en el nombre del fichero usaremos dos careateres guión bajo (_) rodeando el nombre de la varible para indicar que se debe sustituir por su vlaor. Por ejemplo:

__miPropiedad__App.java
__artifactId__-context.xml
__groupId__App.java

Personalizar el nombre de ficheros con variables.

De forma predeterminada tenemos usa serie da variables ya predefinidas quepodemos usar para renombrar ficheros de nuestra plantilla, pero nos puede interesar crear variables en el momento de generar el proyecto para renombrar ficheros. Para poder hacer esto podemos valernos de que el primer fichero que se procesa es el pom.xml y crear en el las variebles con el valor que necesitemos. Posteriormente podremos hacer referencia a esas variables definidas en el nombre del fichero. Por ejemplo podemos definir el fichero pom.xml de la siguiente forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#set ( $customProperty = 'Excelsit' )
#set ( $theArtifactId = $artifactId )
#set ( $thegroupId = $groupId )
#set ( $theVersion = $version )
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>$thegroupId</groupId>
 <artifactId>$theArtifactId</artifactId>
 <version>$theVersion</version>
 <dependencies>
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>3.8.1</version>
 <scope>test</scope>
 </dependency>
 </dependencies>
</project>

En la línea 1 hemos definido una propiedad a la que le hemos dado cierto valor. Pero recuerda que internamente funciona con Velocity, por lo que podremos generar ese valor de cualquier forma. Podremos usar esa propiedad para renombrar ficheros según la forma que hemos visto anteriormente __customProperty_App.java

Usar la plantilla.

Para usar la plantilla lo más sencillo es realizar una instalación local. Usaremos el siguiente comando dentro de la carpeta de nuestro proyecto:

$mvn install

Una vez instalado podremos usarlo meniante archetype:generate. Por defecto el plugin buscará en catalogo central de Maven. Para modificar este comportamiento usaremos el parámetro archetypeCatalog=local:

mvn archetype:generate -DarchetypeCatalog=local

Si no aparece nuestra plantilla quizás deberamos actualizar el catalogo mediante el comando:

mvn archetype:crawl

Código de ejemplo

Descarga el ejemplo de plantilla: excelsit-example-archetype.

Busquedas para encontrar este articulo.

como crear un archetype de maven

maven creacion archetype

maven archetype

Etiquetas: , , , ,

Configurar el modo debug en OC4J

Jueves, 26 de marzo de 2009

Mediante la variable de entorno OC4J_JVM_ARGS podemos añadir parámetros a la máquina virtual java al iniciar el servidor.

Añadiendo a la variable de entorno OC4J_JVM_ARGS el sigueite valor

-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=4000

se iniciará el servidor de aplicaciones en modo debug y podremos usar el Eclipse para conectar el debug en el puerto 4000.

Con este método, se puede agregar cuaquier tipo de parámetros que deseemos indicar a la máquina virtual de java. Por ejemplo podemos aumentar la memoria reservada para el cargador de clases:

-XX:PermSize=512m -XX:MaxPermSize=512m

Y para la para la pila:

-Xms128m -Xmx1024m

Etiquetas: , , , ,

Eliminar la validación del servidor en conexiones HTTPS

Lunes, 23 de marzo de 2009

Al realizar una conexión segura (https), se valida que el servidor es quien dice ser mediante el envio de un certificado que el cliente tiene que conocer. Si lo que quieres es confiar siempre en todos los certificados, este código te puede ser util.

// Crear un gestor de certificados que no valida si el certificado esta en el
// almacen de certificados de confianza.
TrustManager[] trustAllCerts = new TrustManager[]{
   new X509TrustManager() {
      public java.security.cert.X509Certificate[] getAcceptedIssuers() {
         return null;
      }
 
      public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
      }
 
      public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
      }
   }
};
 
// Instalar el gestor de certificados.
try {
   SSLContext sc = SSLContext.getInstance("SSL");
   sc.init(null, trustAllCerts, new java.security.SecureRandom());
   HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
}

Etiquetas: , , , , ,

Struts: Crear un enlace con varios parámetros.

Jueves, 12 de marzo de 2009

Mediante la etiqueta <jsp:useBean> creamos un objeto de tipo HashMap. Mediante <c:set>añadimos propiedades al HashMap.

<jsp:useBean id="paramsMap" class="java.util.HashMap"/>
<c:set target="${paramsMap}" property="nombre1" value="valor1"/>
<c:set target="${paramsMap}" property="nombre2" value="valor2"/>
<html:link action="accion.do" name="paramsMap">
   Enlace
</html>

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: , ,

Concatenar 2 vectores

Miércoles, 4 de febrero de 2009

Como concatenar 2 vectores en java.

Object[] concat(Object[] A, Object[] B) {
   Object[] C= new Object[A.length+B.length];
   System.arraycopy(A, 0, C, 0, A.length);
   System.arraycopy(B, 0, C, A.length, B.length);
 
   return C;
}

Etiquetas: ,