.. contents::
Tabla de contenidos de org.gvsig.tools.service
Introducción
================
Una parte en la que se ha hecho mucho hincapié en lo que es la 2.0
ha sido la separación entre API e implementación. Para facilitar y
estandarizar la forma de trabajar con esto, en org.gvsig.tools hay
dos paquetes que presentan utilidades relacionadas con ello.
* **org.gvsig.tools.library**, que aporta los mecanismos de
carga y descubrimiento de librerías en tiempo de ejecución.
* **org.gvsig.tools.service**, que aporta el *entorno* para realizar
la separación entre *API/implementación/SPI/proveedores*.
Este documento va a tratar sobre este último punto, intentando dar
una visión ya no de qué es lo que aporta, si no, en la medida de lo
posible, de por qué se ha decidido hacer de esta forma.
Hay que tener en cuenta que este marco se ha ido confeccionando a lo
largo del desarrollo realizado entorno a gvSIG 2.0, con lo que
los desarrollos de la 2.0 que se empezaron en sus primeros momentos,
aunque ya aparecen los conceptos que aquí se formalizan, aún no usan
el *entorno* que se va a describir.
Cuando vamos a realizar un desarrollo podemos encontrarnos con
varios escenarios:
* **API/implementación**. Vamos a generar una librería con la
lógica de nuestro desarrollo en la que vamos a separar el API
de su implementación.
* **API/SPI/implementaciones múltiples de servicios (MSI)**. En este
caso vamos a tener la opción de tener diferentes implementaciones
de los servicios de nuestro API.
* **API/SPI/implementación basada en proveedores (PBI)**. En este escenario la implementación
del API puede llegar a tener varios proveedores de servicios.
Los conceptos que se abordan aquí van ligados a la
estructura de un proyecto para gvSIG 2.0, y conviene tener en
mente la estructura de proyecto multimódulo propuesta para los
desarrollos de gvSIG 2.0.
Una aproximación simple, API/implementación
=============================================
La primera aproximación que vamos a hacer consiste en que nuestra
librería presenta una serie de *servicios* que ofrece a través
de un API, pudiendo tener una o varias implementaciónes para estos.
Para ello crearemos un proyecto en el que dejaremos caer los interfaces
que definen nuestro API y otro con la implementación para ellos. Normalmente
tendremos un interface *manager* que actúa a modo de factoría de los distintos
servicios que vamos a ofrecer y un interface por cada servicio que vayamos
a ofrecer.
Para ir ilustrando esto partiremos de un proyecto ficticio llamado *FortuneCookie*.
Tendremos dos proyectos:
* org.gvsig.fortunecookie.lib.api, con los interfaces que exponga nuestro
desarrollo.
* org.gvsig.fortunecookie.lib.impl, con las clases que provean de una
implementación por defecto para ellos.
org.gvsig.fortunecookie.lib.api
----------------------------------
Para definir el API crearemos básicamente dos interfaces, **FortuneCookieManager**
y **FortuneCookieService**.
* **FortuneCookieManager**. Presenta un interface simple que se encarga de
instanciar un *FortuneCookieService*, y devolvérselo al cliente para que
trabaje con él.
Además de hacer de factoría, podría llevar la gestión de configuración general
de la librería.
* **FortuneCookieService**. Define el API a usar para obtener un *fortune cookie*.
En nuestro ejemplo sólo vamos a tener un método: *getMessage*.
.. code-block:: java
public interface FortuneCookieManager {
public FortuneCookieService getFortuneCookieService();
}
public interface FortuneCookieService {
public String getMessage();
}
Además de esto deberemos tener en cuenta el manejo de excepciones, así como
la creación del *locator* y *library* correspondiente para poder
acceder a estos servicios. Así tendremos:
* **FortuneCookieLibrary**. Extenderá la clase *AbtractLibrary* e
implementará los métodos "do\*", normalmente dejándolos vacíos.
En la parte del API el library en general no realiza
ninguna operación.
* **FortuneCookieLocator**. Derivará de la clase *AbstractLocator*
e implementará lo necesario para poder recuperar una implementación
del *FortuneCookieManager* que esté disponible. Consulte la documentación
sobre el *locator* para más informacion sobre él.
La compilación de este proyecto nos generará una librería
*org.gvsig.fortunecookie.lib.api.jar* (falta el número de versión), que contendrá
la definición del API de nuestro desarrollo. Esta librería es la que necesitarán
nuestros proyectos en tiempo de compilación para acceder a nuestro
servicio. Recordad que trabajando con maven podemos especificar
dependencias indicando si son de tiempo de compilación, ejecución o testing.
org.gvsig.fortunecookie.lib.impl
----------------------------------
Por otro lado tendremos el proyecto en el que residirá la implementación de
nuestro API. Siguiendo las recomendaciones en el nombrado de clases para
gvSIG 2.0, nos encontraremos con las clases:
* **DefaultFortuneCookieManager**. Esta clase implementará el interface
*FortuneCookieManager*, encargándose de la creación de las instancias
de nuestro *FortuneCookieService*.
* **DefaultFortuneCookieService**. Esta es la clase que contendrá la lógica
usada para conseguir los mensajes de nuestras *fortune cookie*.
.. code-block:: java
public class DefaultFortuneCookieManager implements FortuneCookieManager {
public FortuneCookieService getFortuneCookieService() {
return new DefaultFortuneCookieService(this);
}
}
public class DefaultFortuneCookieService implements FortuneCookieService {
private DefaultFortuneCookieManager manager;
private String msg;
public DefaultFortuneCookieService(DefaultFortuneCookieManager manager) {
this.manager = manager;
this.msg = null;
}
public String getMessage() {
if( this.msg != null ) {
return this.msg;
}
...
//Here we would generate the message
...
return this.msg;
}
}
Y al igual que en el proyecto del API aquí tendríamos el *library*
correspondiente, que se encargaría de registrar la implementación
de nuestro manager para que pudiese ser recuperada por el *locator*
del API.
Hay que tener en cuenta que el *manager* actúa a modo de *singleton*,
de forma que una vez recuperada una instancia de él siempre nos
devolverá la misma instancia, siendo global a la aplicación que
consuma nuestro servicio. Esto, en el ejemplo de nuestras *fortune cookies*,
no tiene especial relevancia, pero en otros escenarios más complejos
puede llegar a tenerla.
La compilación de este proyecto nos generará una librería
*org.gvsig.fortunecookie.lib.impl.jar* con una implementación de nuestro
API. Conviene recordar que las dependencias entre estos dos
proyectos, API e implementación, son, que la implementación depende del
API pero nunca al revés.
Conclusión
--------------
Como conclusión conviene observar que las utilidades de *org.gvsig.tools.service*
no nos han ofrecido ningún mecanismo especial para llevar a cabo nuestro
proyecto. Al final han sido una serie de *prácticas recomendadas* lo único
que hemos aplicado. Esto en gran parte debido a que el escenario de API
mas una implementación en *bloque* de la lógica de ese API no requiere
de más infraestructura.
API/SPI/implementaciones múltiples de servicios (MSI)
==============================================================================
.. note::
**TODO** Esta pendiente redactar esta seccion
Esquema general
-----------------
El siguiente diagrama nos muestra las relaciones de estos interfaces y clases
con nuestro escenario de *fortune cookie*.
.. figure:: images/org.gvsig.tools.service.msi-peq.png
:align: center
Esquema general de clases de *FortuneCookie*.
.. note::
Puedes descargarte el modelo en formato ArgoUML de
`API/SPI/implementaciones múltiples de servicios desde aqui `_
API/SPI/implementación basada en proveedores (PBI)
=================================================================
Cuando nos encontramos con un escenario en el que para dar el servicio
que ofrece nuestra API, además de la propia lógica de nuestra implementación
tenemos que servirnos de proveedores para algún servicio que puede usar
nuestra librería, la cosa se complica.
Básicamente se trata del escenario en el que la implementación de
nuestro servicio requiere a su vez de una serie de proveedores de otros
servicios.
Vamos a seguir con el ejemplo de nuestras *fortune cookies*, aunque
aquí está más cogido por los pelos, ya que nuestro servicio no ofrece
mucho más valor añadido del que puedan ofrecer los proveedores que use
el mismo.
Al igual que en el caso simple de separación entre API e implementación
tendremos, un manager y un servicio, pero ahora además necesitaremos algunas
cosas más. Entrarán en juego
* un proveedor de los servicios que precise nuestra
implementación.
* una factoría para instanciar a esos proveedores.
* Tendremos también un manager que gestione a los
proveedores
* Y alguien encargado de extender el API con las acciones
que puedan ser necesitadas por un proveedor.
Y además tendremos que separar la parte de la implementación que
usa y gestiona a los proveedores del API que ofrezcamos a estos
para interactuar con nuestra librería, el SPI.
org.gvsig.fortunecookie.lib.api
----------------------------------
En este proyecto nos encontraremos con la parte de API de nuestra librería.
Tendremos:
* **FortuneCookieManager**
* **FortuneCookieService**.
* **FortuneCookieLocator**
* **FortuneCookieLibrary**
Esta parte básicamente sigue presentando un aspecto similar
al del ejemplo simple. Si lo pensamos tiene su lógica, ya que
se identifica con los servicios que ofrece nuestra librería y
estos deberían ser independientes de como estén implementados.
org.gvsig.fortunecookie.lib.spi
----------------------------------
El SPI constituye el contrato que ofrece nuestra librería hacia
sus proveedores de servicios, así que como tal es similar a la
definición del API. Estará constituido principalmente por una
serie de interfaces y ocAsíonalmente alguna clase abstract que
facilite la implementación de un proveedor.
Nos encontraremos con:
* **FortuneCookieProvider**. Se tratará del interface que deben
cumplir los proveedores de servicios que usará nuestra librería.
* **FortuneCookieProviderServices**. Representa a los servicios
adicionales que debe proveer nuestro *FortuneCookieService* de cara
a un proveedor. Hay que tener en cuenta que los proveedores no
deben tener acceso a la implementación de la librería. Al igual
que un cliente de ésta sólo tendrá acceso a su API un proveedor
sólo tendrá acceso a su SPI, así que, si debemos proveer de
algún servicio a nuestro proveedor deberemos exponerlo a través
de esta interface.
* **FortuneCookieProviderFactory**. Por cada tipo de proveedor que
tenga nuestra librería necesitaremos una factoría que nos proporcione
instancias de ese tipo de proveedores, así que parece razonable
que exista un interface que nos proporcione el *contrato* que
deban cumplir esas factorías.
Normalmente, no deberemos preocuparnos por este interface, ya que
la librería provee del interface **ProviderFactory** que nos suministrará
todos los servicios que precisemos relacionados con la factoría de proveedores.
* **FortuneCookieProviderManager**. Necesitaremos mantener un
registro de las factorías de proveedores con las que vamos a
trabajar, así que crearemos un manager específico para gestionar
todo lo que tenga que ver con nuestros proveedores.
org.gvsig.fortunecookie.lib.impl
----------------------------------
Ahora veamos que nos quedará en la parte de implementación:
* **DefaultFortuneCookieManager**, similar al que ya teníamos en
nuestro escenario simple sin proveedores.
La principal diferencia con respecto al del escenario anterior
estará en que este necesita gestionar proveedores, así que
simplemente tendrá una referencia al *FortuneCookieProviderManager*
y algún método para recuperarla.
* **DefaultFortuneCookieProviderManager**, que tendrá la implementación
por defecto del manager encargado de la gestión de nuestros proveedores
de servicios.
* **DefaultFortuneCookieService**, que al igual que en el escenario simple
presentará la implementación del servicio que ofrece nuestra librería
a sus clientes. La principal diferencia respecto la anterior versión
estará en que para alguna de sus funciones, en lugar de implementarlas
directamente, delegará en un proveedor que le será suministrado
en el momento de su contrucción.
* **DefaultFortuneCookieProviderServices**. Esta clase implementará los
servicios que nuestra librería expone hacia los proveedores de esta.
* **DefaultFortuneCookieLibrary**, que al igual que en nuestro escenario
simple, se encargará de registrar esta implementación.
org.gvsig.fortunecookie.lib.prov.web
---------------------------------------
Por ultimo deberemos disponer de alguna implementación de un proveedor
de servicios de nuestra librería. En nuestro ejemplo, nos encontraremos:
* **FortuneCookieWebProvider**, que nos brindará los servicios de
obtención de nuestras *fortune cookies* obteniéndolas a través de un
servicio web.
* **FortuneCookieWebProviderFactory**, que será la implementación de
la factoría para la construcción de **FortuneCookieWebProvider**.
* **FortuneCookieWebLibrary**, que se encargará de registrar este
proveedor como un proveedor de servicios de nuestra librería.
Y por cada proveedor de servicios de nuestra librería aparecería
su implementación, su *factory* y su *library*, habiendo tantos
como proveedores de servicios distintos tengamos.
Esquema general
-----------------
Así como en el caso del escenario simple, en el que solo tenemos un API y
su implementación la librería *org.gvsig.tools* no nos ofrecía utilidades
para su implementación debido a la simplicidad de este, en el caso de
que hayan proveedores de servicios de nuestra librería sí que nos ofrece
todo un *entorno de trabajo* para facilitarnos la tarea. Encontraremos
interfaces de los que extender en nuestra definición del API y SPI, y
clases abstractas que extender a la hora de contruir nuestra implementación
o nuestros proveedores, librándonos de las faenas de registro de los proveedores,
o de recuperar instancias de estos cuando los precisemos.
El siguiente diagrama nos muestra las relaciones de estos interfaces y clases
con nuestro escenario de *fortune cookie*.
.. figure:: images/org.gvsig.fortunecookie.spi-peq.jpg
:align: center
Esquema general de clases de *FortuneCookie*.
.. note::
Puedes descargarte el modelo en formato ArgoUML de
`API/SPI/implementación basada en proveedores desde aqui `_
En el esquema podemos observar ocho bloques bien diferenciados. Cuatro con los
interfaces y clases que ofrece *org.gvsig.tools.service* y otros cuatro que
deberá aportar nuestro desarrollo.
Los siguientes bloques los aporta *org.gvsig.tools.service* para facilitarnos
nuestro desarrollo y homogeneizar la implementación de este escenario:
* **General API**. Este bloque contiene los interfaces que nos ayudaran a
definir el API de nuestro desarrollo.
* **General SPI**. Aquí encontraremos los interfaces que nos facilitarán
el definir el SPI de nuestro producto.
* **General API impl**. Nos aporta una serie de clases abtractas de cara
a ser extendidas por la implementación de nuestra librería para
definir el API de esta.
* **General SPI impl**. Nos aporta una serie de clases abtractas de cara
a ser extendidas por la implementación de nuestra librería para
definir el SPI de esta.
Los otros cuatro bloques representan código que deberemos aportar en nuestra
librería. Estos serían:
* **Domain specific API**. En nuestro ejemplo sería
*org.gvsig.fortunecookie.lib.api*, y estaría formado por
los interfaces que definen el API de nuestra librería.
* **Domain specific SPI**. Aqui tendríamos la parte de definición
del SPI de nuestra librería. Estaría relacionado con el código
que encontraríamos en *org.gvsig.fortunecookie.lib.api*.
* **Default implementación**. Estaría formado por la implementación
de la lógica de nuestra librería.
* **Concrete provider**. Representa las clases que habría que
crear para disponer de un proveedor de datos para nuestra librería.
Parámetros de un proveedor de servicios
------------------------------------------
Entes de pasar a resumir los pasos que deberíamos dar para implementar
este tipo de escenario, comentaremos algo sobre la construcción de los
proveedores de servicios de nuestra librería.
Normalmente cada proveedor de servicios, aunque exponga un contrato común
a todos los proveedores de servicios hacia nuestra implementación, precisará
de una serie de parámetros diferentes para cada proveedor. Así, siguiendo con nuestro ejemplo, *FortuneCookieWebProvider* podría precisar de
la *url* de acceso al servicio web que nos proporcione los distintos mensajes,
pero podríamos disponer de otro proveedor, *FortuneCookieDBProvider*, que fuese
a buscar los mensajes a una BBDD, y entre sus parámetros precise una cadena
de conexión a la BBDD y un nombre de tabla de la que extraer los mensajes.
Y también podríamos disponer de *FortuneCookieXMLFileProvider* que recibiese
la ruta de un fichero XML.
Básicamente cada proveedor, ofrece un interface
común a nuestra aplicación, *getMessage*, pero precisa de unos parámetros
particulares para su creación.
Para uniformizar esto, la factoría del proveedor dispondrá un método que nos
devuelva un *DynObject* en el que depositar estos parámetros y que luego
utilizará el proveedor para poder trabajar.
Así un cliente de nuestra librería, para empezar a trabajar con un servicio de los
que ofrezca nuestro desarrollo, lo primero que tendrá que hacer es pedirle al
*FortuneCookieManager* que le dé un *DynObject* en el que almacenar los
parámetros del proveedor con el que vayamos a trabajar a traves del método
*getProviderParameters(String providerName)*, para luego invocar a *getFortuneCookieService*
pasándole esos parámetros.
Contruyendo nuestro proyecto paso a paso
--------------------------------------------
A través de este apartado se irán enumerando las directrices a seguir para poder generar nuestro proyecto y comenzar el desarrollo.
En un primer paso, deberemos preparar el sistema de directorios para poder albergar el proyecto. Para ello, realizaremos las siguientes tareas:
1) Crear el directorio raíz que contendrá el proyecto. Siguiendo con la nomenclatura descrita hasta el momento, el proyecto deberá comenzar con *org.gvsig.* más el nombre del proyecto (en nuestro caso *org.gvsig.fortunecookies*).
2) Crear el subdirectorio LIB dentro del directorio raíz (en nuestro caso sería *org.gvsig.fortunecookies.lib*)
3) Creamos y editamos el fichero POM en *org.gvsig.fortunecookies*
- Añadimos el módulo LIB como hijo del directorio raíz, para que lo incluya en el proyecto
4) Creamos el proyecto API (*org.gvsig.fortunecookies.lib.api*) en *org.gvsig.fortunecookies/org.gvsig.fortunecookies.lib*
- Abrimos la consola y nos dirigimos al directorio en el que queremos generar el proyecto (*org.gvsig.fortunecookies/org.gvsig.fortunecookies.lib*).
- Ejecutamos la orden de Maven ->
::
mvn archetype:generate \
-DarchetypeGroupId=org.gvsig \
-DarchetypeArtifactId=org.gvsig.library-archetype \
-DgroupId=org.gvsig \
-DartifactId=org.gvsig.fortunecookies.lib.api \
-Dversion=1.0 \
-Dpackage=org.gvsig.fortunecookies.lib.api
- Indicamos como arquetipo la opción 17 y confirmamos la opción indicando *Yes* (valores por defecto)
5) Creamos y editamos el fichero POM en *org.gvsig.fortunecookies/org.gvsig.fortunecookies.lib*
- Especificamos el módulo padre
- Añadimos el módulo API como hijo del directorio *org.gvsig.fortunecookies.lib*, para que lo incluya en el proyecto
6) Editamos el fichero POM de *org.gvsig.fortunecookies/org.gvsig.fortunecookies.lib/org.gvsig.fortunecookies.lib.api*
- Especificamos que el módulo padre es *org.gvsig.fortunecookies.lib*
7) A través de la consola nos dirigimos al directorio raíz y lanzamos la orden Maven -> mvn eclipse:eclipse. Debiendo obtener una respuesta que muestre el correcto establecimiento de las dependencias entre los 3 módulos implicados
8) Repetir desde el paso 4 realizado para generar el proyecto *org.gvsig.fortunecookies.lib.api* para crear *org.gvsig.fortunecookies.lib.spi* (si se requiere) y *org.gvsig.fortunecookies.lib.impl*.
9) Si vamos a tener SPI, requeriremos de un provider que nos suministre los datos que necesitemos. Para ello, generaremos un paquete como *org.gvsig.fortunecookies.lib* creado en el punto 2 (que en este caso llamaremos *org.gvsig.fortunecookies.prov*), y añadiremos tantos providers como la aplicación utilice (en caso de tener varias fuentes de datos diferentes), que en nuestro caso se llamarán *org.gvsig.fortunecookies.prov.webprovider* y *org.gvsig.fortunecookies.prov.fileprovider* (la creación de éstos será igual que la de *org.gvsig.fortunecookies.lib.api*).
En este punto ya tenemos preparado nuestro proyecto para empezar a centrarnos en el desarrollo. En este segundo paso, tras configurar nuestro workspace con Eclipse, comenzaremos con la implementación del proyecto.
org.gvsig.fortunecookie.lib.api
....................................
Vamos a implementar los métodos del **API** mínimos necesarios para que nuestra aplicación funcione:
Empezaremos con los proyectos *FortuneCookieLocator* y *FortuneCookieLibrary* que, como se ha comentado previamente, permiten acceder a los servicios que proporcionará el API.
1) *FortuneCookieLibrary*:
.. code-block:: java
public class FortuneCookieLibrary extends AbstractLibrary{
protected void doInitialize() throws LibraryException {
}
protected void doPostInitialize() throws LibraryException {
}
}
2) *FortuneCookieLocator*:
.. code-block:: java
public class FortuneCookieLocator extends BaseLocator{
private static final String LOCATOR_NAME = "FortuneCookieLocator";
public static final String MANAGER_NAME = "FortuneCookieLocator.manager";
public static final String MANAGER_DESCRIPTION = "FortuneCookie Manager";
private static final FortuneCookieLocator instance = new FortuneCookieLocator();
public static FortuneCookieLocator getInstance() {
return instance;
}
public String getLocatorName() {
return LOCATOR_NAME;
}
public static FortuneCookieManager getManager() throws LocatorException {
return (FortuneCookieManager) getInstance().get(MANAGER_NAME);
}
public static void registerManager(Class clazz) {
getInstance().register(MANAGER_NAME, MANAGER_DESCRIPTION, clazz);
}
}
3) *FortuneCookieService*
.. code-block:: java
public interface FortuneCookieService extends Service{
public String getMessage() throws FortuneCookieMessageException;
}
4) *FortuneCookieManager*
.. code-block:: java
public interface FortuneCookieManager extends Manager {
public DynObject createServiceParameters(String serviceName) throws ServiceException;
}
5) *org.gvsig.tools.library.Library* (creado en *src/main/resources/META-INF/services*)
.. code-block:: java
org.gvsig.fortunecookies.FortuneCookieLibrary
org.gvsig.fortunecookie.lib.spi
...................................
Para la creación del **SPI**, requeriremos la interfaz del proveedor (*FortuneCookieProvider*) y del proveedor de servicios (*FortuneCookieServiceProvider*)
1) *FortuneCookieService*:
.. code-block:: java
public interface FortuneCookieProvider {
public String getMessage() throws Exception;
}
2) *FortuneCookieProviderServices*:
.. code-block:: java
public interface FortuneCookieProviderServices extends ProviderServices{
public DynObject getParameters();
}
org.gvsig.fortunecookie.lib.impl
.....................................
El desarrollo del proyecto de **Implementación** contendrá la lógica interna que facilita los servicios especificados en el API y en el SPI. Para ello tendremos una clase *DefaultFortuneCookieService* y *DefaultFortuneCookieManager* que implementará los respectivos interfaces del API y una *DefaultFortuneCookieProviderServices* que hará lo propio con la interfaz del SPI. Además, deberemos añadir un *Library* para que el Locator registre nuestro manager (*FortuneCookieDefaultImplLibrary*)
1) *DefaultFortuneCookieService*:
.. code-block:: java
public class DefaultFortuneCookieService extends AbstractService implements FortuneCookie{
private String message;
private DefaultFortuneCookieManager manager;
public DefaultFortuneCookieService(DefaultFortuneCookieManager manager, DynObject parameters,
ProviderServices providerServices) throws ServiceException {
this.manager = manager;
this.message = null;
super.init(parameters, providerServices);
}
/*
* (non-Javadoc)
* @see org.gvsig.tools.service.Service#getProviderManager()
*
* This method must be implemented in all Services
*/
protected ProviderManager getProviderManager() {
return this.manager.getProviderManager();
}
/*
* (non-Javadoc)
* @see org.gvsig.tools.service.Service#getManager()
*
* This method must be implemented in all Services
*/
public Manager getManager() {
return this.manager;
}
public String getMessage() throws FortuneCookieMessageException {
if(message==null){
FortuneCookieProvider provider = (FortuneCookieProvider)this.getProvider();
try {
this.message = provider.getMessage();
} catch (Exception e) {
throw new FortuneCookieMessageException(e);
}
}
return this.message;
}
}
2) *DefaultFortuneCookieManager*:
.. code-block:: java
public class DefaultFortuneCookieManager extends AbstractManager implements FortuneCookieManager {
public DefaultFortuneCookieManager() {
super(new DefaultProviderManager());
}
/*
* (non-Javadoc)
* @see org.gvsig.tools.service.Manager#getService(org.gvsig.tools.dynobject.DynObject)
*
* This method must be implemented in all Managers
*/
public Service getService(DynObject parameters) throws ServiceException {
DefaultFortuneCookieProviderServices providerServices = new DefaultFortuneCookieProviderServices();
DefaultFortuneCookie cookie = new DefaultFortuneCookie(this, parameters , providerServices);
return cookie;
}
}
3) *DefaultFortuneCookieProviderServices*:
.. code-block:: java
public class DefaultFortuneCookieProviderServices extends AbstractProviderServices{
}
4) *FortuneCookieDefaultImplLibrary*:
.. code-block:: java
public class FortuneCookieDefaultImplLibrary extends AbstractLibrary{
protected void doInitialize() throws LibraryException {
FortuneCookieLocator.registerManager(DefaultFortuneCookieManager.class);
}
protected void doPostInitialize() throws LibraryException {
}
}
5) *org.gvsig.tools.library.Library* (creado en *src/main/resources/META-INF/services*):
.. code-block:: java
org.gvsig.fortunecookies.impl.FortuneCookieDefaultImplLibrary
org.gvsig.fortunecookie.prov
.....................................
Por último, se implementará el provider. Según el caso, podemos tener más de un proveedor de datos, que se implementará replicando este apartado tantas veces como proveedores haya (en nuestro caso se llamarán *org.gvsig.fortunecookies.prov.webprovider* y *org.gvsig.fortunecookies.prov.fileprovider*). Para generarlo requeriremos el proveedor, la factoría y la librería que lo registra, quedando de la siguiente forma:
1) *DefaultFortuneProviderLibrary* registra la factoría del proveedor en el *postinitialize*:
.. code-block:: java
public class FortuneCookieFileProviderLibrary extends AbstractLibrary{
protected void doInitialize() throws LibraryException {
}
protected void doPostInitialize() throws LibraryException {
ProviderManager mgr = (ProviderManager) FortuneCookieLocator.getManager();
mgr.addProviderFactory(new FortuneCookieProviderFactory());
}
}
2) *FortuneCookieProviderFactory*
.. code-block:: java
public class FortuneCookieProviderFactory implements ProviderFactory{
public static final String PROVIDER_NAME = "Provider";
public static final String PROVIDER_NAME_PARAMS = "FortuneCookieParams";
public static final String PROVIDER_NAME_PARAMS_DESCRIPTION = "";
private DynClass dynclass;
public Provider create(DynObject parameters, ProviderServices services) throws ServiceException {
return new FortuneCookieProvider(services);
}
public DynObject createParameters() {
DynObject dynobject = ToolsLocator.getDynObjectManager().createDynObject(dynclass);
...
//Here allocate the necessary parameters
...
return dynobject;
}
public String getName() {
return PROVIDER_NAME;
}
public void initialize() {
dynclass = ToolsLocator.getDynObjectManager().createDynClass(PROVIDER_NAME_PARAMS, PROVIDER_NAME_PARAMS_DESCRIPTION);
...
//Here allocate the necessary parameters
...
}
}
3) Definiremos el proveedor (*DefaultFortuneCookieProvider*) que implementará los métodos establecidos en el **SPI**:
.. code-block:: java
public class DefaultFortuneCookieProvider extends AbstractProvider implements FortuneCookieProvider{
public String getMessage() throws Exception {
...
//Here would define the method to get the message
...
}
}
Cuando tenemos interface de usuario en nuestro desarrollo
============================================================
En el caso que queramos disponer de interfaz de usuario en nuestra aplicación, también resulta interesante estructurar nuestro código, realizando una separación entre API e implementación.
De esta forma, se consigue definir una serie de clases que representan unos interfaces y ofrecen unas determinadas operaciones o funcionalidades, haciéndola independiente de su lógica. Esto permite poder llegar a realizar varias implementaciónes para satisfacer una necesidad siempre que respete el API.
A la hora de llevar a cabo esto podemos encontrarnos con casos similares a los que nos encontramos
a la hora de trabajar con la logica. Asi la forma mas simple nos la encontraremos
cuando tengamos un API y una implementacion de ese API que no precisa de implementaciones
alternativas o de proveedores de servicios para dar sus servicios.
Para poder llevarlo a cabo, emplearemos una estructura muy similar a la utilizada en el API e implementación
de la librería. A continuación, desglosaremos los elementos que componen cada uno de estos paquetes:
org.gvsig.fortunecookie.swing.api
-----------------------------------------
Al igual que en la librería, en este API se definen el conjunto de operaciones o servicios que ofrecen las interfaces de usuario (en este caso sólo está el *JFortuneCookiePanel*, aunque podrían haber más). Dentro de este paquete tendremos los siguientes componentes:
* **JFortuneCookiePanel**. Desarrolla el API del panel principal que se mostrará con
la información de una instancia de *FortuneCookieService*. Entre los servicios que
podemos requerir será el de *getFortuneCookie* que nos devuelve la instancia
de *FortuneCookieService* que está representada en ese *JPanel*.
En general se tratara de una clase abstract que extiende a *JPanel* sin aportarle
nada mas que metodos abstractos. Siguiendo las mismas pautas que se han seguido
en la parte de logica deberia ser un interface que extendiese al interface de
swing del JPanel, pero como no disponemos de ese interface en swing lo simularemos
creando ka clase abstract.
* **FortuneCookieUIManager**. Se encarga de instanciar los distintos interfaces de
usuario, y devolvérselos al cliente para que trabaje con
ellos. Además de hacer de factoría.
Dispondra de metodos como *getJFortuneCookiePanel* que nos devolveran una instancia
de *JFortuneCookiePanel*.
* **FortuneCookieSwingLocator**. Al igual que en el *Locator* del *Library*
(*FortuneCookieLocator*), derivará de la clase *AbstractLocator* e implementará lo
necesario para poder recuperar una implementación del *FortuneCookieSwingManager*
que esté disponible.
* **FortuneCookieSwingLibrary**. Extenderá la clase *AbtractLibrary* e implementará
los métodos "do*", normalmente dejándolos vacíos. En la parte del API (al igual
que ocurría con el *FortuneCookieLibrary*), no realiza ninguna operación.
org.gvsig.fortunecookie.swing.impl
---------------------------------------
Presenta la lógica que desarrolla la funcionalidad de las operaciones que nos proporciona el API.
* **DefaultJFortuneCookiePanel**.
Extendera de *JFortuneCookiePanel* y aportara la implementacion del panel que nos muestra
un *FortuneCookieServices*.
* **DefaultFortuneCookieUIManager**. Esta clase implementará el interface *FortuneCookieUIManager*,
encargándose de la creación de las instancias de nuestros interfaces *JFortuneCookiePanel*.
* **DefaultFortuneCookieSwingImplLibrary**. Al igual que en el proyecto del API aquí
tendríamos este library, que se encargaría de registrar la implementación de nuestro
manager para que pudiese ser recuperada por el *Locator* del API de esta parte de *Swing*.
La compilación de este proyecto nos generará una librería *org.gvsig.fortunecookie.lib.api.jar*
y *org.gvsig.fortunecookie.lib.impl.jar* (faltará el número de versión, que forma parte
de cada uno de los nombres)
Aunque aqui hemos usado *JPanel* como clase base para los componentes base que ofrece nuestra
libreria, puede darse el caso de que en lugar de *JPanel* sea mas conveniente usar otro
componente de swing como base de los nuestro, y asi Usar por ejemplo un *JComponent* o
uno mas concreto como el *JEditPane*. Cuando decidamos usar componentes concretos de
swing deberemos tener en cuenta que vamos a exponer el componente entero como parte
del API de nuestra libreria, de forma que el cambio de este en futuras implementaciones
causara una perdida de compativilidad importante. Solo deberiamos usar un comppnente de
swing concreto cuando este claro que este forma parte del API que vamos a exponer y no
como un efecto secundario de la implementacion que aportamos.