root / tags / v1_2_Build_1207 / docs / Andami developer guide.html @ 38141
History | View | Annotate | Download (16.6 KB)
1 |
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
---|---|
2 |
<html>
|
3 |
<head>
|
4 |
<meta content="text/html; charset=ISO-8859-1" |
5 |
http-equiv="content-type"> |
6 |
<title>Andami developer guide</title> |
7 |
<link href="style.css" rel="stylesheet" type="text/css"> |
8 |
</head>
|
9 |
<body>
|
10 |
<table width="90%" border="0"> |
11 |
<tr>
|
12 |
<td class="Nivel2"><a href="#Introduccion"><strong><font color="#FFFFFF">Introducción</font></strong></a></td> |
13 |
</tr>
|
14 |
<tr>
|
15 |
<td class="Nivel2"><a href="#Creacion_de_plugins"><font color="#FFFFFF"><strong>Creación |
16 |
de plugins</strong></font></a></td> |
17 |
</tr>
|
18 |
<tr>
|
19 |
<td class="Nivel2"><a href="#Funcionamiento_del_class_loader"><strong><font color="#FFFFFF">Funcionamiento |
20 |
del class loader</font></strong></a></td> |
21 |
</tr>
|
22 |
<tr>
|
23 |
<td class="Nivel2"><a href="#Servicios_a_los_plugins"><font color="#FFFFFF"><strong>Servicios |
24 |
a los plugins</strong></font></a></td> |
25 |
</tr>
|
26 |
<tr>
|
27 |
<td><strong> <a |
28 |
href="#Ejecucion_en_segundo_plano">Ejecución en segundo plano</a></strong></td> |
29 |
</tr>
|
30 |
<tr>
|
31 |
<td><strong> <a href="#Acceso_a_las_extensiones">Acceso |
32 |
a las extensiones</a></strong></td> |
33 |
</tr>
|
34 |
<tr>
|
35 |
<td class="urbanismo"><strong> <a href="#Persistencia_de_los_plugins">Persistencia |
36 |
de los plugins</a></strong></td> |
37 |
</tr>
|
38 |
<tr>
|
39 |
<td><strong> <a href="#popupmenus">Pop-up menus</a></strong></td> |
40 |
</tr>
|
41 |
<tr>
|
42 |
<td><strong> <a href="#Vistas">Vistas</a></strong></td> |
43 |
</tr>
|
44 |
<tr>
|
45 |
<td> <a href="#SingletonView">SingletonView's</a></td> |
46 |
</tr>
|
47 |
</table>
|
48 |
|
49 |
|
50 |
|
51 |
<p> <strong><font size="+1"><a name="Introduccion"></a>Introducción</font></strong></p> |
52 |
<p> Andami es un framework orientado a plugins construido sobre |
53 |
swing que permite la construcción de forma rápida y extensible |
54 |
de aplicaciones MDI (Multiple Document Interface). Tiene ya implementadas muchas |
55 |
de las funcionalidades requeridas para este tipo de aplicaciones tales como |
56 |
un menú Ventana, en la que van apareciendo las ventanas que se abren,
|
57 |
soporte para el traducciones, configuración personalizada para cada usuario,
|
58 |
actualizaciones automáticas, tanto del propio Andami como de los plugins,
|
59 |
persistencia del tamaño y posición de la ventana, del idioma, |
60 |
distintos tipos de vista, con la finalidad de facilitar la programación
|
61 |
de ventanas especiales, ... Todo esto además de tener ya solventados
|
62 |
los problemas típicos de la programación de un entorno como éste |
63 |
que no son pocos.<br>
|
64 |
<br>
|
65 |
Además, Andami está diseñada de forma |
66 |
que la propia lógica MDI puede ser reemplazada. Si en lugar de una aplicación |
67 |
estilo arcView, se quiere que cada vista que se añada, se haga en una
|
68 |
ventana de windows nueva (de las que aparecen en la barra de estado) o una vista |
69 |
como la de Eclipse, no hay más que desarrollar el plugin adecuado.<br> |
70 |
<br>
|
71 |
Andami está diseñada para ser amistosa con |
72 |
el usuario y para ello incorpora la posibilidad de añadir tooltips y
|
73 |
enabletexts a los botones. El tooltip es el texto que aparece cuando el ratón
|
74 |
se detiene sobre un botón o menú. El enableText es el texto que |
75 |
aparece en el caso anterior cuando el botón está desactivado, |
76 |
permitiendo mostrar al usuario qué es lo que debe hacer para que dicho
|
77 |
botón se active (o cualquier otro mensaje).<br> |
78 |
<br>
|
79 |
Por último, Andami tiene una gestión de errores |
80 |
que agradará a cualquier programador, ya que en casos de errores graves,
|
81 |
el propio framework avisa de dicho fallo y aconseja al usuario salvar los cambios |
82 |
y reiniciar el programa, a parte de que toda excepción no capturada por
|
83 |
el usuario se redirige a un fichero de log configurable en el que se escribirá
|
84 |
su traza.<br>
|
85 |
</p>
|
86 |
<p> <strong><font size="+1"><a name="Creacion_de_plugins"></a>Creación |
87 |
de plugins</font></strong></p> |
88 |
<p> Andami gira en torno al concepto de plugin. Andami mantiene |
89 |
un directorio como directorio de los plugins, que se puede cambiar en cualquier |
90 |
momento. Un plugin viene definido por la existencia de un subdirectorio dentro |
91 |
del directorio de los plugins, siendo el nombre del plugin el nombre de dicho |
92 |
directorio. Dentro de dicho directorio debe haber un fichero config.xml en el |
93 |
que se configuran los puntos de entrada y salida del plugin (menúes,
|
94 |
barras de herramientas, etiquetas de la barra de estado, menúes contextuales),
|
95 |
las librerías que va a usar, el paquete de traducciones, los plugins
|
96 |
de los que depende, etc. El fichero plugin-config.xsd contiene el esquema que |
97 |
ha de seguir este fichero y están comentados todos los elementos. En
|
98 |
el CorePlugin que viene con Adami se puede ver un ejemplo de config.xml.<br>
|
99 |
<br>
|
100 |
Las rutas de los directorios son siempre relativas al directorio |
101 |
del plugin y los textos de los menúes, tooltips y enabletext's son claves
|
102 |
en el fichero de traducciones en caso de que haya y textos literales en caso |
103 |
de que no haya traducciones. El fichero de traducciones es un fichero de propiedades |
104 |
común: ver ResourceBundle en la API de Java.<br> |
105 |
<br>
|
106 |
Dentro de los plugins aparece el concepto de extensión. |
107 |
Una extensión es instalada por los plugins mediante la implementación |
108 |
de la interfaz com.iver.andami.plugins.Extension y la instalación de
|
109 |
unos controles geobernados por ésta en el fichero config.xml. Mediante
|
110 |
esta implementación se le dice a Andami la condición que se debe |
111 |
cumplir para que los controles sean visibles o estén activos. Además |
112 |
se implementa la acción a llevar a cabo cuando se selecciona uno de los
|
113 |
menúes o botones asociados a la extensión. Cabe resaltar que Andami |
114 |
crea una instancia de cada extensión configurada en config.xml, por lo
|
115 |
que las clases que implementen la interfaz Extension deben de tener un constructor |
116 |
sin argumentos.<br>
|
117 |
<br>
|
118 |
En los tag 'extension' existe un atributo class-name en el |
119 |
que se especifica la clase que gobierna el punto de extensión que se
|
120 |
está definiendo. Esta clase deberá implementar la interfaz com.iver.andami.plugins.Extension |
121 |
y será mediante ésta que gobernará los menúes y |
122 |
botones asociados a este punto de extensión<br> |
123 |
<br>
|
124 |
Una problema común en este framework es no ver cómo |
125 |
se mantiene la información del proyecto concreto que se está desarrollando. |
126 |
El lugar adecuado es una de las extensiones instaladas. Por ejemplo en el caso |
127 |
de gvSIG está la extensión com.iver.cit.gvsig.ProjectExtension, |
128 |
en la cual hay un atributo Project que es la raíz del árbol jerárquico |
129 |
del cual penden las vistas, mapas, tablas, ... Además esta extensión |
130 |
tiene un método getProject que devuelve la referencia al proyecto, de
|
131 |
manera que se puede acceder desde cualquier punto de la aplicación. Para
|
132 |
ver el acceso a las instancias de las extensiones ver <a href="#Acceso_a_las_extensiones">acceso |
133 |
a las extensiones</a></p> |
134 |
<p><br> |
135 |
<font size="+1"><strong><a name="Funcionamiento_del_class_loader"></a>Funcionamiento |
136 |
del class loader</strong></font></p> |
137 |
<p> En Andami, el class loader de cada plugin delega primero en el classloader
|
138 |
del sistemaes decir, que si se ejecuta desde eclipse buscará por todos
|
139 |
los jars que haya en el classpath del proyecto, y si se ejecuta desde la linea |
140 |
de comandos, buscará en la variable de entorno CLASSPATH o en el argumento
|
141 |
-classpath que se pase como parámetro a java.<br> |
142 |
<br>
|
143 |
Si el class loader del sistema no satisface la búsqueda, se buscará |
144 |
en los jars del directorio especificado por el config.xml del plugin que intenta |
145 |
cargar la clase y si no se encuentra en dichos jars, se buscará en los
|
146 |
jars de los plugins de los cuales depende el plugin que intenta cargar la clase.<br>
|
147 |
<br>
|
148 |
<strong><font size="+1"><a name="Servicios_a_los_plugins"></a>Servicios |
149 |
a los plugins</font></strong></p> |
150 |
<p><br> |
151 |
Andami ofrece a los plugins distintos servicios |
152 |
a través de la clase PluginServices en cuyo javadoc se puede obtener
|
153 |
información sobre como usarlos. Existen unos servicios genéricos |
154 |
que vienen dados por métodos estáticos de dicha clase y luego |
155 |
está el método getPluginServices que obtiene una instancia de |
156 |
esta clase específica del plugin, mediante la cual puede acceder a servicios
|
157 |
concretos de cada plugin, traducciones, persistencia, directorio del plugin, |
158 |
...<br>
|
159 |
<br> |
160 |
<strong><a name="Ejecucion_en_segundo_plano"></a>Ejecución en segundo |
161 |
plano</strong><br> |
162 |
Es conveniente que la interfaz gráfica esté |
163 |
siempre en funcionamiento, nunca bloqueada, aunque sea sólo para mostrar
|
164 |
al usuario que el programa está procesando. Para ello hay que realizar
|
165 |
las tareas que puedan tomar demasiado tiempo en un thread a parte. La clase |
166 |
PluginServices proporciona un método estático backgroundExecution |
167 |
al cual se le pasa un objeto Runnable, que es ejecutado en segundo plano, dejando |
168 |
el thread de la interfaz libre para responder pero con sus eventos bloqueados |
169 |
con el fin de que la interfaz responda y se redibuje, pero se ignoren los eventos |
170 |
que produce el usuario mientras se procesa la petición<br> |
171 |
<br>
|
172 |
<a name="Acceso_a_las_extensiones"></a><strong>Acceso a las extensiones</strong><br> |
173 |
Para acceder a la instancia de una extensión |
174 |
se puede usar simplemente el método de PluginServices getExtension(Class)
|
175 |
a la cual habrá que pasar como parámetro la clase de la extensión |
176 |
a la que se quiere acceder. Dicho método retorna un objeto Extensión |
177 |
y por tanto habrá que hacer casting a la clase concreta de dicha extensión, |
178 |
habiendo obtenido así la referencia a la instancia de la extensión |
179 |
deseada.<br>
|
180 |
A la hora de desarrollar habrá que tener en el build |
181 |
path del entorno de desarrollo que se use, el jar del plugin dentro del cual |
182 |
está la extensión que se quiere obtener, para poder pasarle como |
183 |
parámetro a getExtension(Class) la clase de la misma.<br> |
184 |
<br>
|
185 |
<strong><a name="Persistencia_de_los_plugins"></a>Persistencia de |
186 |
los plugins</strong><br> |
187 |
Uno de los servicios que ofrece Andami a los |
188 |
plugins es la facilidad de guardar datos genéricos de los mismos en el
|
189 |
directorio del usuario de manera que cada usuario mantiene su propia configuración
|
190 |
de los plugins. Para ello, las instancias de PluginServices contienen una propiedad |
191 |
persistentXML que puede ser obtenida y asignada y que es de tipo XMLEntity, |
192 |
pudiendo añadir información de tipo básico (String, int, |
193 |
long, ...) a dicha instancia y siendo esta información persistente entre
|
194 |
ejecuciones.<br>
|
195 |
<br>
|
196 |
<strong><a name="popupmenus"></a>Pop-up menus</strong><br> |
197 |
Otro servicio proporcionado por Andami es el de pop-up |
198 |
menu's extensibles. Mediante el XML se puede definir un pop-up menu con un nombre |
199 |
y unas entradas al igual que cualquier otro menú, con la única |
200 |
diferencia que para mostrar el popup menú habrá que registrar |
201 |
un listener de la siguiente manera:<br>
|
202 |
</p>
|
203 |
<div style="text-align: center;"> |
204 |
<pre><>public void addPopupMenuListener(String name, Component c, ActionListener listener)</><br></pre> |
205 |
</div>
|
206 |
Lo cual hará que al pinchar con el |
207 |
botón derecho sobre el componente 'c' aparezca el menú |
208 |
'name' del fichero de configuración del plugin y al seleccionar
|
209 |
cualquier entrada se ejecutará el ActionListener que se pasa
|
210 |
como parámetro.<br> |
211 |
<br>
|
212 |
Estos menúes son extensibles porque cualquier |
213 |
otro plugin puede añadir entradas a dicho menú en su |
214 |
propio fichero config.xml referenciando el menú al que quiere
|
215 |
extender mediante el nombre del plugin más el nombre del
|
216 |
menú. Un ejemplo. Tenemos un plugin llamado com.iver.cit.gvsig
|
217 |
que instala un menú de la siguiente manera:<br> |
218 |
<pre> <popupMenu name="cascada"><br> <entry text="Cascada" <br> tooltip="cascada_tooltip" <br> enableText="cascada_enable" actionCommand="CASCADA"/><br> <entry text="Tile" <br> tooltip="tile_tooltip" <br> enableText="cascada_enable" actionCommand="CASCADA"/><br> </popupMenu><br></pre> |
219 |
y tenemos otro plugin que quiere añadir una entrada a dicho
|
220 |
menú. Para ello deberá de incluir un fragmento similar a |
221 |
este en su fichero config.xml:<br>
|
222 |
<pre> <popupMenu name="com.iver.cit.gvsig.cascada"><br> <entry text="Nueva entrada" actionCommand="NUEVA"/><br> </popupMenu><br></pre> |
223 |
y además deberá de registrarse como listener de la manera |
224 |
que se explicó anteriormente.<br> |
225 |
<br>
|
226 |
<strong><a name="Vistas"></a>Vistas</strong><br> |
227 |
El servicio más importante que |
228 |
proporciona Andami es el de añadir vistas al marco principal.
|
229 |
Podemos calsificar las vistas por varios criterios:<br>
|
230 |
<ul>
|
231 |
<li>Por su modalidad</li> |
232 |
<ul>
|
233 |
<li>Modales</li> |
234 |
<li>No modales</li> |
235 |
</ul>
|
236 |
</ul>
|
237 |
<ul>
|
238 |
<li>Por su contenido</li> |
239 |
<ul>
|
240 |
<li>Singleton (Contenido identificable)</li> |
241 |
<li>Normales (Contenido no identificable)</li> |
242 |
</ul>
|
243 |
</ul>
|
244 |
Para crear una vista hay que hacer una clase que |
245 |
derive de JPanel en la que se pone toda la funcionalidad como si de un |
246 |
diálogo normal se tratara. Además esta clase ha de |
247 |
implementar la interfaz View la cual proporciona un método
|
248 |
getViewInfo que es invocado una vez en la vida de la vista. Este objeto |
249 |
contiene las características que tendrá la vista: |
250 |
maximizable, resizable, title, ... y puede ser actualizado en cualquier |
251 |
momento, reflejándose estos cambios de manera automática |
252 |
en la interfaz de usuario. Una vez implementada la vista, hay que |
253 |
añadirla al manger MDI, que es el encargado de manejar toda la
|
254 |
lógica relacionada con las vistas. Para obtener una referencia
|
255 |
al manager MDI la clase PluginServices tiene un método
|
256 |
getMDIManager, el cual a su vez tiene un método addView mediante
|
257 |
el cual se puede añadir la vista.<br> |
258 |
<br>
|
259 |
Si durante la ejecución se quiere cambiar |
260 |
alguna propiedad de la vista tal como el tamaño, la
|
261 |
posición o el
|
262 |
título, sólo hay que acceder al objeto ViewInfo de la |
263 |
misma y realizar los cambios de la manera deseada. El siguiente trozo |
264 |
de código cambia el título de una ventana (se supone que |
265 |
es ejecutado desde la clase que implementa View).<br>
|
266 |
<pre> PluginServices.getMDIManager().getViewInfo(this).setTitle("Nuevo título");<br></pre> |
267 |
Puede ser necesario que algunas vistas realicen |
268 |
algún tipo de procesamiento al ser activadas pero esto no se
|
269 |
sabe cuando ocurre ya que lo que se entrega al manager MDI es un |
270 |
JPanel. Para recibir los eventos sobre las vistas, además de
|
271 |
implementar View hay que implementar ViewListener la cual |
272 |
proporcionará los métodos que serán invocados |
273 |
cuando sucedan los eventos oportunos en las vistas.<br>
|
274 |
<br>
|
275 |
<big><a name="SingletonView"></a>SingletonView<br> |
276 |
</big> Un tipo especial de vistas son las |
277 |
SingletonView. Su principal característica es que se les define
|
278 |
el contenido de las mismas, de manera que cuando hay una SingletonView |
279 |
minimizada en el proyecto y se intenta añadir un JPanel con el
|
280 |
mismo contenido, en lugar de aparecer otra ventana en el escritorio, lo |
281 |
que sucede es que la ventana que estaba minimizada se restaura a su |
282 |
posición anterior. Para esto, el método addView devuelve |
283 |
una referencia a la vista que se muestra, sea esta la que se |
284 |
está añadiendo o la que ya está añadida. |
285 |
Además, al cerrar dicha ventana se guardan las dimensiones y la
|
286 |
posición de las mismas, de manera que al volverla a abrir se
|
287 |
recuerdan estos datos.<br>
|
288 |
</body>
|
289 |
</html>
|