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 17:20:33 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:

.. figure:: images/library_classes.png
  :align: center

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`__).

__ 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`__.

__ http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Service%20Provider
__ http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider
__ http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider

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

__ http://java.sun.com/javase/6/docs/api/java/util/ServiceLoader.html

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:

.. code-block:: java

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

        
            
    	        ${basedir}/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 **::

        
            maven-antrun-plugin
            
    		    
    				services
    				package
    				
    					run
    				
    				
    					
    						
                                    
    						
    						
    							
    						
    					
    				
    			
    		
    	
    	
    		org.apache.maven.plugins
    		maven-surefire-plugin
    		
    			
    				${basedir}/src/main/resources-api
    				${basedir}/src/main/resources-impl
    			
    		
    	

  - Si NO tenemos estructura de maven, será lo mismo, cambiando la ubicación de las carpetas a incluir::

    	
    		maven-antrun-plugin
    		
    			
    				services
    				package
    				
    					run
    				
    				
    					
    						
    							
    						
    						
    							
    						
    					
    				
    			
    		
    	
    	
    		org.apache.maven.plugins
    		maven-surefire-plugin
    		
    			
    				${basedir}/resources/api
    				${basedir}/resources/impl
    			
    		
    	


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

     
        org.gvsig
    	org.gvsig.fmap.geometry
        2.0-SNAPSHOT
    
    
        org.gvsig
    	org.gvsig.fmap.geometry
        2.0-SNAPSHOT
    	impl
        test
    

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

    
        org.gvsig
        org.gvsig.tools
        2.0-SNAPSHOT
        tests
        test
    


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.

View source document


Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: