Introducción
Creación de plugins
Funcionamiento del class loader
Servicios a los plugins
    Ejecución en segundo plano
    Acceso a las extensiones
    Persistencia de los plugins
    Pop-up menus
    Vistas
        SingletonView's

Introducción

    Andami es un framework orientado a plugins construido sobre swing que permite la construcción de forma rápida y extensible de aplicaciones MDI (Multiple Document Interface). Tiene ya implementadas muchas de las funcionalidades requeridas para este tipo de aplicaciones tales como un menú Ventana, en la que van apareciendo las ventanas que se abren, soporte para el traducciones, configuración personalizada para cada usuario, actualizaciones automáticas, tanto del propio Andami como de los plugins, persistencia del tamaño y posición de la ventana, del idioma, distintos tipos de vista, con la finalidad de facilitar la programación de ventanas especiales, ... Todo esto además de tener ya solventados los problemas típicos de la programación de un entorno como éste que no son pocos.

    Además, Andami está diseñada de forma que la propia lógica MDI puede ser reemplazada. Si en lugar de una aplicación estilo arcView, se quiere que cada vista que se añada, se haga en una ventana de windows nueva (de las que aparecen en la barra de estado) o una vista como la de Eclipse, no hay más que desarrollar el plugin adecuado.

    Andami está diseñada para ser amistosa con el usuario y para ello incorpora la posibilidad de añadir tooltips y enabletexts a los botones. El tooltip es el texto que aparece cuando el ratón se detiene sobre un botón o menú. El enableText es el texto que aparece en el caso anterior cuando el botón está desactivado, permitiendo mostrar al usuario qué es lo que debe hacer para que dicho botón se active (o cualquier otro mensaje).

    Por último, Andami tiene una gestión de errores que agradará a cualquier programador, ya que en casos de errores graves, el propio framework avisa de dicho fallo y aconseja al usuario salvar los cambios y reiniciar el programa, a parte de que toda excepción no capturada por el usuario se redirige a un fichero de log configurable en el que se escribirá su traza.

Creación de plugins

    Andami gira en torno al concepto de plugin. Andami mantiene un directorio como directorio de los plugins, que se puede cambiar en cualquier momento. Un plugin viene definido por la existencia de un subdirectorio dentro del directorio de los plugins, siendo el nombre del plugin el nombre de dicho directorio. Dentro de dicho directorio debe haber un fichero config.xml en el que se configuran los puntos de entrada y salida del plugin (menúes, barras de herramientas, etiquetas de la barra de estado, menúes contextuales), las librerías que va a usar, el paquete de traducciones, los plugins de los que depende, etc. El fichero plugin-config.xsd contiene el esquema que ha de seguir este fichero y están comentados todos los elementos. En el CorePlugin que viene con Adami se puede ver un ejemplo de config.xml.

    Las rutas de los directorios son siempre relativas al directorio del plugin y los textos de los menúes, tooltips y enabletext's son claves en el fichero de traducciones en caso de que haya y textos literales en caso de que no haya traducciones. El fichero de traducciones es un fichero de propiedades común: ver ResourceBundle en la API de Java.

    Dentro de los plugins aparece el concepto de extensión. Una extensión es instalada por los plugins mediante la implementación de la interfaz com.iver.andami.plugins.Extension y la instalación de unos controles geobernados por ésta en el fichero config.xml. Mediante esta implementación se le dice a Andami la condición que se debe cumplir para que los controles sean visibles o estén activos. Además se implementa la acción a llevar a cabo cuando se selecciona uno de los menúes o botones asociados a la extensión. Cabe resaltar que Andami crea una instancia de cada extensión configurada en config.xml, por lo que las clases que implementen la interfaz Extension deben de tener un constructor sin argumentos.

    En los tag 'extension' existe un atributo class-name en el que se especifica la clase que gobierna el punto de extensión que se está definiendo. Esta clase deberá implementar la interfaz com.iver.andami.plugins.Extension y será mediante ésta que gobernará los menúes y botones asociados a este punto de extensión

    Una problema común en este framework es no ver cómo se mantiene la información del proyecto concreto que se está desarrollando. El lugar adecuado es una de las extensiones instaladas. Por ejemplo en el caso de gvSIG está la extensión com.iver.cit.gvsig.ProjectExtension, en la cual hay un atributo Project que es la raíz del árbol jerárquico del cual penden las vistas, mapas, tablas, ... Además esta extensión tiene un método getProject que devuelve la referencia al proyecto, de manera que se puede acceder desde cualquier punto de la aplicación. Para ver el acceso a las instancias de las extensiones ver acceso a las extensiones


Funcionamiento del class loader

En Andami, el class loader de cada plugin delega primero en el classloader del sistemaes decir, que si se ejecuta desde eclipse buscará por todos los jars que haya en el classpath del proyecto, y si se ejecuta desde la linea de comandos, buscará en la variable de entorno CLASSPATH o en el argumento -classpath que se pase como parámetro a java.

Si el class loader del sistema no satisface la búsqueda, se buscará en los jars del directorio especificado por el config.xml del plugin que intenta cargar la clase y si no se encuentra en dichos jars, se buscará en los jars de los plugins de los cuales depende el plugin que intenta cargar la clase.

Servicios a los plugins


    Andami ofrece a los plugins distintos servicios a través de la clase PluginServices en cuyo javadoc se puede obtener información sobre como usarlos. Existen unos servicios genéricos que vienen dados por métodos estáticos de dicha clase y luego está el método getPluginServices que obtiene una instancia de esta clase específica del plugin, mediante la cual puede acceder a servicios concretos de cada plugin, traducciones, persistencia, directorio del plugin, ...
 
Ejecución en segundo plano
    Es conveniente que la interfaz gráfica esté siempre en funcionamiento, nunca bloqueada, aunque sea sólo para mostrar al usuario que el programa está procesando. Para ello hay que realizar las tareas que puedan tomar demasiado tiempo en un thread a parte. La clase PluginServices proporciona un método estático backgroundExecution al cual se le pasa un objeto Runnable, que es ejecutado en segundo plano, dejando el thread de la interfaz libre para responder pero con sus eventos bloqueados con el fin de que la interfaz responda y se redibuje, pero se ignoren los eventos que produce el usuario mientras se procesa la petición

Acceso a las extensiones
    Para acceder a la instancia de una extensión se puede usar simplemente el método de PluginServices getExtension(Class) a la cual habrá que pasar como parámetro la clase de la extensión a la que se quiere acceder. Dicho método retorna un objeto Extensión y por tanto habrá que hacer casting a la clase concreta de dicha extensión, habiendo obtenido así la referencia a la instancia de la extensión deseada.
    A la hora de desarrollar habrá que tener en el build path del entorno de desarrollo que se use, el jar del plugin dentro del cual está la extensión que se quiere obtener, para poder pasarle como parámetro a getExtension(Class) la clase de la misma.

Persistencia de los plugins
    Uno de los servicios que ofrece Andami a los plugins es la facilidad de guardar datos genéricos de los mismos en el directorio del usuario de manera que cada usuario mantiene su propia configuración de los plugins. Para ello, las instancias de PluginServices contienen una propiedad persistentXML que puede ser obtenida y asignada y que es de tipo XMLEntity, pudiendo añadir información de tipo básico (String, int, long, ...) a dicha instancia y siendo esta información persistente entre ejecuciones.

Pop-up menus
     Otro servicio proporcionado por Andami es el de pop-up menu's extensibles. Mediante el XML se puede definir un pop-up menu con un nombre y unas entradas al igual que cualquier otro menú, con la única diferencia que para mostrar el popup menú habrá que registrar un listener de la siguiente manera:

<>public void addPopupMenuListener(String name, Component c, ActionListener listener)
    Lo cual hará que al pinchar con el botón derecho sobre el componente 'c' aparezca el menú 'name' del fichero de configuración del plugin y al seleccionar cualquier entrada se ejecutará el ActionListener que se pasa como parámetro.

    Estos menúes son extensibles porque cualquier otro plugin puede añadir entradas a dicho menú en su propio fichero config.xml referenciando el menú al que quiere extender mediante el nombre del plugin más el nombre del menú. Un ejemplo. Tenemos un plugin llamado com.iver.cit.gvsig que instala un menú de la siguiente manera:
		<popupMenu name="cascada">
<entry text="Cascada"
tooltip="cascada_tooltip"
enableText="cascada_enable" actionCommand="CASCADA"/>
<entry text="Tile"
tooltip="tile_tooltip"
enableText="cascada_enable" actionCommand="CASCADA"/>
</popupMenu>
y tenemos otro plugin que quiere añadir una entrada a dicho menú. Para ello deberá de incluir un fragmento similar a este en su fichero config.xml:
		<popupMenu name="com.iver.cit.gvsig.cascada">
<entry text="Nueva entrada" actionCommand="NUEVA"/>
</popupMenu>
y además deberá de registrarse como listener de la manera que se explicó anteriormente.

Vistas
    El servicio más importante que proporciona Andami es el de añadir vistas al marco principal. Podemos calsificar las vistas por varios criterios:
    Para crear una vista hay que hacer una clase que derive de JPanel en la que se pone toda la funcionalidad como si de un diálogo normal se tratara. Además esta clase ha de implementar la interfaz View la cual proporciona un método getViewInfo que es invocado una vez en la vida de la vista. Este objeto contiene las características que tendrá la vista: maximizable, resizable, title, ... y puede ser actualizado en cualquier momento, reflejándose estos cambios de manera automática en la interfaz de usuario. Una vez implementada la vista, hay que añadirla al manger MDI, que es el encargado de manejar toda la lógica relacionada con las vistas. Para obtener una referencia al manager MDI la clase PluginServices tiene un método getMDIManager, el cual a su vez tiene un método addView mediante el cual se puede añadir la vista.

    Si durante la ejecución se quiere cambiar alguna propiedad de la vista tal como el tamaño, la posición o el título, sólo hay que acceder al objeto ViewInfo de la misma y realizar los cambios de la manera deseada. El siguiente trozo de código cambia el título de una ventana (se supone que es ejecutado desde la clase que implementa View).
	PluginServices.getMDIManager().getViewInfo(this).setTitle("Nuevo título");
    Puede ser necesario que algunas vistas realicen algún tipo de procesamiento al ser activadas pero esto no se sabe cuando ocurre ya que lo que se entrega al manager MDI es un JPanel. Para recibir los eventos sobre las vistas, además de implementar View hay que implementar ViewListener la cual proporcionará los métodos que serán invocados cuando sucedan los eventos oportunos en las vistas.

SingletonView
    Un tipo especial de vistas son las SingletonView. Su principal característica es que se les define el contenido de las mismas, de manera que cuando hay una SingletonView minimizada en el proyecto y se intenta añadir un JPanel con el mismo contenido, en lugar de aparecer otra ventana en el escritorio, lo que sucede es que la ventana que estaba minimizada se restaura a su posición anterior. Para esto, el método addView devuelve una referencia a la vista que se muestra, sea esta la que se está añadiendo o la que ya está añadida. Además, al cerrar dicha ventana se guardan las dimensiones y la posición de las mismas, de manera que al volverla a abrir se recuerdan estos datos.