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