Personal tools
You are here: Home gvSIG Projects gvSIG Desktop Documentation Developers documentation org.gvsig.tools 2.1.0 org.gvsig.tools.library
gvSIG Desktop
gvSIG Desktop

Cached time 11/21/13 11:24:39 Clear cache and reload

 
Document Actions

org.gvsig.tools.library

by Cèsar Ordiñana last modified 2010-07-13 15:57

Inicialización de librerías

En gvSIG tenemos una serie de proyectos que actúan como librerías para aportar una funcionalidad determinada. Por lo general, muchas de esas librerías suelen requerir algún tipo de inicialización: definir valores por defecto, cargar un archivo, registrar clases, etc.

Anteriormente, dicha inicialización no estaba definida, por lo que en cada librería nos podíamos encontrar con un mecanismo distinto: código estático, singleton, etc.

Con el fin de normalizar este proceso se ha definido un API para la inicialización y configuración de librerías. En realidad, para cualquier tipo de proyecto, como por ejemplo una extensión de gvSIG.

El API es muy sencillo, consiste en un interfaz Library que define dos métodos muy parecidos a los del interfaz IExtension de Andami, y una clase abstracta AbstractLibrary para facilitar el desarrollo:

images/library_classes.png

La idea es que primero se llamará a todas los initialize de los Library de la aplicación, y a continuación todos los postInitialize, al igual que se hace con las extensiones de Andami. De hecho, dentro de gvSIG, los initialize de las Library se invocará justo antes que los initialize de las extensiones, y lo mismo con el método postInitialize.

Además se ha desarrollado una clase AbstractLibrary para facilitar el desarrollo de estas clases, incluyendo un control de ejecución para que una Library se inicialice sólo una vez dentro de una misma aplicación, aunque se invoque varias veces a sus métodos post/initialize.

Uno de los casos de uso más habituales será el registro de implementaciones en los Locator. Cada librería que emplee el mecanismo del Locator deberá implementar un Library para su API, y otro para su implementación (ver ejemplo en org.gvsig.tools.locator).

Librería de API:
El Library implementará el método doPostInitialize(), dentro del cuál se hará la comprobación de que alguna implementación haya sido registrada.
Librerías de implementación:
El Library, en su método doInitialize(), se encargará de registrar las implementaciones que aporta la librería a través del locator de su correspondiente librería de API.

En ambos casos, los métodos del Library también podrán ser usados para realizar otros tipos de inicializaciones, como definir variables de librería, cargar configuraciones, obtener recursos, etc.

Inicialización automática

Se ha desarrollado un mecanismo que facilite la inicialización automática de las librerías de una aplicación, como por ejemplo gvSIG o cualquier test unitario.

Para ello se ha definido un interfaz LibrariesInitializer y una implementación basada en el mecanismo de registro de servicios disponible en el JDK. Dicho mecanismo varía según el JDK empleado, pasando de una clase interna (sun.misc.Service), a formar parte del API público a partir de Java 1.6 (java.util.ServiceLoader).

Podemos encontrar más información en la especificación de los archivos JAR, bien de la versión 1.4.2, 1.5, o 1.6.

A partir de la versión 1.6 de Java disponemos también del API de la clase ServiceLoader

La implementación realizada se basa en el uso de reflection para buscar una implementación disponible, independientemente de la versión de JDK empleado, por lo que podremos usarla en cualquier versión de Java >= 1.3.

El uso de esta inicialización automática, una vez los jars de las librerías contienen la información necesaria y se encuentran en el classpath de ejecución, consiste en instanciar e invocar al LibrariesInitializer.

Este contiene los siguientes métodos:

  • initialize: invoca a todos los initialize de las librerías encontradas.
  • postInitialize: invoca a todos los postInitialize de las librerías encontradas.
  • fullInitialize: método de utilidad que llama primero a todos los initialize, y a continuación a todos los postInitialize.

En gvSIG, andami se encargará de llamar al LibrariesInitializer correspondiente, creando uno por cada plugin cargado. Entonces llamará al initialize del LibrariesInitializer antes de llamar al de las extensiones de cada plugin. A continuación, hará lo mismo con los postInitialize.

En los tests unitarios, por ejemplo, se empleará el método fullInitialize directamente desde el método setUp. Ej:

protected void setUp() throws Exception {
        super.setUp();
    new DefaultLibrariesInitializer().fullInitialize();
    ....
}

Para facilitar el desarrollo de tests unitarios, con la inicialización automática de librerías, se ha creado una clase AutoLibsTestCase que se encargará de realizar la inicialización anterior. En este caso, la inicialización de nuestros tests la haremos a través del método doSetUp.

Configuración de la inicialización automática

Para que funcione la inicialización automática de librerías, con la implementación que se ha desarrollado, en cada proyecto deberemos registrar las implementaciones de Library que se aportan, para que el inicializador sea capaz de encontrarlas.

Dicho mecanismo, tal y como se explica en el punto anterior, se basa en el registro de servicios de Java. En nuestro caso concretamente consistirá en un archivo de texto llamado org.gvsig.tools.library.Library dentro de la carpeta META-INF/services, cuyo contenido será el nombre o nombres de las clases que implementan Library en nuestro proyecto, e incluirlo en el jar a generar o en el classpath de ejecución.

A continuación se detallan los pasos a realizar, según la estructura de proyecto que tengamos, o si nuestro proyecto genera un único jar o varios:

  • Crearemos los directorios donde incluir el archivo de registro de nuestras implementaciones:

    Si nuestro proyecto genera un único jar:

    • Si tenemos estructura de maven, crearemos el directorio:

      src/main/resources/META-INF/services
      
    • Si NO tenemos estructura de maven, crearemos el directorio:

      resources/META-INF/services
      

    Si nuestro proyecto genera varios jars:

    Crearemos un directorio por cada jar que se genere e incluya algún Library

    • Si tenemos estructura de maven, por ejemplo, si nuestro proyecto genera un jar con el API y otro con la implementación, crearemos los directorios:

      src/main/resources-api/META-INF/services
      src/main/resources-impl/META-INF/services
      
    • Si NO tenemos estructura de maven, siguiendo el ejemplo anterior los directorios serían:

      resources/api/META-INF/services
      resources/impl/META-INF/services
      
  • Incluiremos un archivo org.gvsig.tools.library.Library en cada uno de los directorios creados en el paso anterior.

  • Editaremos cada uno de los archivos anteriores y añadiremos las clases que implementan Library según corresponda al jar en el que van a ir includas.

  • Configuraremos el pom.xml para que se incluyan los archivos anteriores en los archivos .jar generados y en el classpath de ejecución de los tests unitarios:

    Si nuestro proyecto genera un único jar:

    • Si tenemos estructura de maven, no es necesario añadir nada, ya se incluye por defecto el directorio src/main/resources.

    • Si NO tenemos estructura de maven, deberemos incluir el directorio resources añadiendo el siguiente código dentro del apartado <build></build>:

      <resources>
          <resource>
              <directory>${basedir}/resources</directory>
              </resource>
      </resources>
      

    Si nuestro proyecto genera varios jars:

    Deberemos emplear el plugin maven-antrun-plugin para incluir cada archivo en su correspondiente archivo jar generado. Además incluiremos los directorios resources en el classpath de ejecución de los tests unitarios. Siguiendo con el ejemplo de un proyecto que genera un jar de API y otro de implementación, haremos lo siguiente:

    • Si tenemos estructura de maven, incluiremos dentro del apartado <build><plugins></plugins></build>:

      <plugin>
          <artifactId>maven-antrun-plugin</artifactId>
          <executions>
                  <execution>
                              <id>services</id>
                              <phase>package</phase>
                              <goals>
                                      <goal>run</goal>
                              </goals>
                              <configuration>
                                      <tasks>
                                              <jar destfile="${project.build.directory}/${project.build.finalName}.jar"
                                                       update="true">
                                  <fileset dir="${basedir}/src/main/resources-api/" />
                                              </jar>
                                              <jar destfile="${project.build.directory}/${project.build.finalName}-impl.jar"
                                                       update="true">
                                                      <fileset dir="${basedir}/src/main/resources-impl" />
                                              </jar>
                                      </tasks>
                              </configuration>
                      </execution>
              </executions>
      </plugin>
      <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-surefire-plugin</artifactId>
              <configuration>
                      <additionalClasspathElements>
                              <additionalClasspathElement>${basedir}/src/main/resources-api</additionalClasspathElement>
                              <additionalClasspathElement>${basedir}/src/main/resources-impl</additionalClasspathElement>
                      </additionalClasspathElements>
              </configuration>
      </plugin>
      
    • Si NO tenemos estructura de maven, será lo mismo, cambiando la ubicación de las carpetas a incluir:

      <plugin>
              <artifactId>maven-antrun-plugin</artifactId>
              <executions>
                      <execution>
                              <id>services</id>
                              <phase>package</phase>
                              <goals>
                                      <goal>run</goal>
                              </goals>
                              <configuration>
                                      <tasks>
                                              <jar destfile="${project.build.directory}/${project.build.finalName}.jar"
                                                       update="true">
                                                      <fileset dir="${basedir}/resources/api" />
                                              </jar>
                                              <jar destfile="${project.build.directory}/${project.build.finalName}-impl.jar"
                                                       update="true">
                                                      <fileset dir="${basedir}/resources/impl" />
                                              </jar>
                                      </tasks>
                              </configuration>
                      </execution>
              </executions>
      </plugin>
      <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-surefire-plugin</artifactId>
              <configuration>
                      <additionalClasspathElements>
                              <additionalClasspathElement>${basedir}/resources/api</additionalClasspathElement>
                              <additionalClasspathElement>${basedir}/resources/impl</additionalClasspathElement>
                      </additionalClasspathElements>
              </configuration>
      </plugin>
      
  • Finalmente configuraremos eclipse para que tenga en cuenta los archivos de registro de implementaciones de Library en el classpath de ejecución de nuestro proyecto (por ejemplo, para poder emplear la inicialización automática de librerías desde los tests unitarios):

    • Si nuestro proyecto genera un único jar: al generar el proyecto de eclipse desde maven, ya se configurará correctamente, incluyendo la carpeta resources en el classpath del proyecto.

    • Si nuestro proyecto genera varios jars: por ahora no hemos conseguido que en este caso se configure correctamente el proyecto de eclipse al generarlo desde maven, ya que no se incluyen las carpetas resources- en el classpath. Por lo tanto tendremos que incluirlas a mano.

      Para ello, una vez generado y cargado el proyecto de eclipse, accederemos a las propiedades de proyecto, y dentro de estas seleccionaremos el apartado Java Build Path. Seleccionaremos la pestaña libraries y pulsaremos el botón Add Class Folder, seleccionando cada una de las carpetas resources de nuestro proyecto.

Tests unitarios

En los tests unitarios, para emplear la inicialización automática bastará con:

  • Incluir en el classpath de ejecución los jars necesarios, en los que se incluyan las librerías que necesitemos. Básicamente habrá que incluir como dependencias las librerías necesarias dentro del archivo pom.xml de nuestro proyecto, para maven, y regenerar el proyecto de eclipse.

    En algunos casos tendremos dependencia de compilación en nuestro proyecto con una librería de API determinada, por ejemplo la de geometrías (libFmap_geom), por lo cuál la tendremos que tener como una dependencia más en el archivo pom.xml de maven. Además, para poner en funcionamiento nuestros tests, necesitaremos también alguna implementación. En dichos casos, incluiremos la dependencia también en el archivo pom.xml, pero indicando que sólo es para la ejecución de los tests, mediante el atributo scope. Ej:

     <dependency>
        <groupId>org.gvsig</groupId>
        <artifactId>org.gvsig.fmap.geometry</artifactId>
        <version>2.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.gvsig</groupId>
        <artifactId>org.gvsig.fmap.geometry</artifactId>
        <version>2.0-SNAPSHOT</version>
        <classifier>impl</classifier>
        <scope>test</scope>
    </dependency>
    
  • Hacer que nuestra clase de test extienda la clase:

    org.gvsig.tools.junit.AbstractLibraryAutoInitTestCase
    

    Dicha clase se encarga de realizar la inicialización de todas las librerías que se encuentran en el classpath de ejecución del test de forma automática.

    Al extender esta clase ya no podremos emplear el método setUp para inicializar nuestro test, sino que tendremos que pasar a emplear el método doSetUp.

    Además necesitaremos incluir el jar de tests de libTools como dependencia en nuestro proyecto, incluyendo lo siguiente:

    <dependency>
        <groupId>org.gvsig</groupId>
        <artifactId>org.gvsig.tools</artifactId>
        <version>2.0-SNAPSHOT</version>
        <classifier>tests</classifier>
        <scope>test</scope>
    </dependency>
    

Notas adicionales

  • Las excepciones del Library son de tipo RuntimeException por lo que, aunque declaradas en su API, no obligan a ser capturadas mediante un try ... catch. A nivel de aplicación, sí que habrá que hacer un tratamiento de estas excepciones, para mostrar al usuario algún tipo de información sobre el error producido.

    No se han definido excepciones normales, ya que son errores graves y no se puedan tratar desde código. Así no obligamos a capturarlas en cada lugar donde se use un Library, y se pueden tratar en un nivel superior.


Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: