svn-gvsig-desktop / tags / v1_0_2_Build_892 / frameworks / _fwAndami / src / com / iver / andami / plugins / PluginClassLoader.java @ 10278
History | View | Annotate | Download (13.3 KB)
1 | 1104 | fjp | /* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
|
---|---|---|---|
2 | *
|
||
3 | * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
|
||
4 | *
|
||
5 | * This program is free software; you can redistribute it and/or
|
||
6 | * modify it under the terms of the GNU General Public License
|
||
7 | * as published by the Free Software Foundation; either version 2
|
||
8 | * of the License, or (at your option) any later version.
|
||
9 | *
|
||
10 | * This program is distributed in the hope that it will be useful,
|
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
13 | * GNU General Public License for more details.
|
||
14 | *
|
||
15 | * You should have received a copy of the GNU General Public License
|
||
16 | * along with this program; if not, write to the Free Software
|
||
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
|
||
18 | *
|
||
19 | * For more information, contact:
|
||
20 | *
|
||
21 | * Generalitat Valenciana
|
||
22 | * Conselleria d'Infraestructures i Transport
|
||
23 | * Av. Blasco Ib??ez, 50
|
||
24 | * 46010 VALENCIA
|
||
25 | * SPAIN
|
||
26 | *
|
||
27 | * +34 963862235
|
||
28 | * gvsig@gva.es
|
||
29 | * www.gvsig.gva.es
|
||
30 | *
|
||
31 | * or
|
||
32 | *
|
||
33 | * IVER T.I. S.A
|
||
34 | * Salamanca 50
|
||
35 | * 46005 Valencia
|
||
36 | * Spain
|
||
37 | *
|
||
38 | * +34 963163400
|
||
39 | * dac@iver.es
|
||
40 | */
|
||
41 | 598 | fernando | package com.iver.andami.plugins; |
42 | |||
43 | import java.io.DataInputStream; |
||
44 | import java.io.File; |
||
45 | 2291 | fernando | import java.io.FileInputStream; |
46 | 598 | fernando | import java.io.IOException; |
47 | import java.io.InputStream; |
||
48 | import java.net.MalformedURLException; |
||
49 | import java.net.URL; |
||
50 | import java.net.URLClassLoader; |
||
51 | import java.security.AllPermission; |
||
52 | import java.security.CodeSource; |
||
53 | import java.security.PermissionCollection; |
||
54 | import java.util.ArrayList; |
||
55 | import java.util.Enumeration; |
||
56 | import java.util.Hashtable; |
||
57 | import java.util.List; |
||
58 | import java.util.StringTokenizer; |
||
59 | import java.util.jar.JarException; |
||
60 | import java.util.zip.ZipEntry; |
||
61 | import java.util.zip.ZipException; |
||
62 | import java.util.zip.ZipFile; |
||
63 | |||
64 | 5275 | jaume | import org.apache.log4j.Logger; |
65 | 598 | fernando | |
66 | 5275 | jaume | import com.iver.andami.messages.Messages; |
67 | |||
68 | |||
69 | 598 | fernando | /**
|
70 | * Class loader que carga las clases pedidas por los plugins de manera que
|
||
71 | * primero busca en el classpath, luego busca en el directorio del propio
|
||
72 | * plugin en los jars especificados por el xml y en caso de no encontrar la
|
||
73 | * clase pide al PluginClassLoaderManager la lista de plugins que pueden
|
||
74 | * satisfacer la clase e intenta cargarlo con cada un de ellos hasta que lo
|
||
75 | * consigue con uno.
|
||
76 | *
|
||
77 | * @author Fernando Gonz?lez Cort?s
|
||
78 | */
|
||
79 | public class PluginClassLoader extends URLClassLoader { |
||
80 | /** DOCUMENT ME! */
|
||
81 | private static Logger logger = Logger.getLogger(PluginClassLoader.class.getName()); |
||
82 | |||
83 | /** DOCUMENT ME! */
|
||
84 | private Hashtable clasesJar = new Hashtable(); |
||
85 | |||
86 | /** DOCUMENT ME! */
|
||
87 | 736 | vcaballero | private File baseDir; |
88 | 598 | fernando | private PluginClassLoader[] pluginLoaders; |
89 | |||
90 | /**
|
||
91 | * Creates a new PluginClassLoader object.
|
||
92 | *
|
||
93 | * @param jars Array con la ruta de los jars en los que buscar? las clases
|
||
94 | * el plugin
|
||
95 | * @param baseDir Directorio base del plugin que se carga. Es en directorio
|
||
96 | * donde se buscan los resources en el m?todo getResources
|
||
97 | * @param cl ClassLoader padre del classLoader, al que se le pedir?
|
||
98 | * resolver las clases antes de utilizar el algoritmo propio
|
||
99 | * @param pluginLoaders DOCUMENT ME!
|
||
100 | 2291 | fernando | *
|
101 | 598 | fernando | * @throws IOException
|
102 | */
|
||
103 | public PluginClassLoader(URL[] jars, String baseDir, ClassLoader cl, |
||
104 | PluginClassLoader[] pluginLoaders) throws IOException { |
||
105 | super(jars, cl);
|
||
106 | 736 | vcaballero | this.baseDir = new File(new File(baseDir).getAbsolutePath()); |
107 | 598 | fernando | this.pluginLoaders = pluginLoaders;
|
108 | |||
109 | ZipFile[] jarFiles = new ZipFile[jars.length]; |
||
110 | |||
111 | for (int i = 0; i < jars.length; i++) { |
||
112 | try {
|
||
113 | jarFiles[i] = new ZipFile(jars[i].getPath()); |
||
114 | |||
115 | Enumeration entradas = jarFiles[i].entries();
|
||
116 | |||
117 | while (entradas.hasMoreElements()) {
|
||
118 | ZipEntry file = (ZipEntry) entradas.nextElement(); |
||
119 | String fileName = file.getName();
|
||
120 | |||
121 | if (!fileName.toLowerCase().endsWith(".class")) { //$NON-NLS-1$ |
||
122 | |||
123 | continue;
|
||
124 | } |
||
125 | |||
126 | fileName = fileName.substring(0, fileName.length() - 6) |
||
127 | .replace('/', '.'); |
||
128 | |||
129 | if (clasesJar.get(fileName) != null) { |
||
130 | throw new JarException(Messages.getString( |
||
131 | 7038 | cesar | "PluginClassLoader.Dos_clases_con_el_mismo_nombre_en_el_plugin") +
|
132 | ": " + fileName + " "+ Messages.getString("en") + " " + |
||
133 | jarFiles[i].getName() + Messages.getString("y_en") + " " + |
||
134 | ((ZipFile) clasesJar.get(fileName)).getName());
|
||
135 | 598 | fernando | } |
136 | |||
137 | clasesJar.put(fileName, jarFiles[i]); |
||
138 | } |
||
139 | } catch (ZipException e) { |
||
140 | 2291 | fernando | throw new IOException(e.getMessage() + " Jar: " + |
141 | jars[i].getPath() + ": " + jarFiles[i]);
|
||
142 | 598 | fernando | } catch (IOException e) { |
143 | throw e;
|
||
144 | 2291 | fernando | } |
145 | 598 | fernando | } |
146 | } |
||
147 | |||
148 | /**
|
||
149 | * DOCUMENT ME!
|
||
150 | *
|
||
151 | * @param name DOCUMENT ME!
|
||
152 | *
|
||
153 | * @return DOCUMENT ME!
|
||
154 | *
|
||
155 | * @throws ClassNotFoundException DOCUMENT ME!
|
||
156 | */
|
||
157 | 2291 | fernando | protected Class singleLoadClass(String name) throws ClassNotFoundException { |
158 | 598 | fernando | // Buscamos en las clases de las librer?as del plugin
|
159 | Class c = findLoadedClass(name);
|
||
160 | |||
161 | 2291 | fernando | if (c != null) { |
162 | return c;
|
||
163 | } |
||
164 | |||
165 | 598 | fernando | try {
|
166 | ZipFile jar = (ZipFile) clasesJar.get(name); |
||
167 | |||
168 | 2291 | fernando | //No est? en ning?n jar
|
169 | 598 | fernando | if (jar == null) { |
170 | 2291 | fernando | //Buscamos en el directorio de clases
|
171 | String classFileName = baseDir + "/classes/" + |
||
172 | name.replace('.', '/') + ".class"; |
||
173 | File f = new File(classFileName); |
||
174 | if (f.exists()){
|
||
175 | byte[] data = loadClassData(f); |
||
176 | c = defineClass(name, data, 0, data.length);
|
||
177 | }else{
|
||
178 | //Buscamos en los otros plugins
|
||
179 | for (int i = 0; i < pluginLoaders.length; i++) { |
||
180 | 3896 | fjp | try
|
181 | { |
||
182 | 5013 | jorpiell | if (pluginLoaders[i] != null){ |
183 | c = pluginLoaders[i].singleLoadClass(name); |
||
184 | }else{
|
||
185 | //TODO El pluginLoaders[i] puede ser nulo?
|
||
186 | logger.warn("PluginLoaders[i] es nulo");
|
||
187 | } |
||
188 | 3896 | fjp | } |
189 | catch (ClassNotFoundException e) |
||
190 | { |
||
191 | // Si no la encontramos en el primer plugin, capturamos la exceptci?n
|
||
192 | // porque es probable que la encontremos en el resto de plugins.
|
||
193 | 5013 | jorpiell | } |
194 | 598 | fernando | |
195 | 2291 | fernando | if (c != null) { |
196 | break;
|
||
197 | } |
||
198 | 598 | fernando | } |
199 | } |
||
200 | } else {
|
||
201 | String fileName = name.replace('.', '/') + ".class"; |
||
202 | ZipEntry classFile = jar.getEntry(fileName);
|
||
203 | byte[] data = loadClassData(classFile, |
||
204 | jar.getInputStream(classFile)); |
||
205 | |||
206 | c = defineClass(name, data, 0, data.length);
|
||
207 | } |
||
208 | |||
209 | if (c == null) { |
||
210 | throw new ClassNotFoundException(name); |
||
211 | } |
||
212 | |||
213 | return c;
|
||
214 | } catch (IOException e) { |
||
215 | throw new ClassNotFoundException(Messages.getString( |
||
216 | "PluginClassLoader.Error_reading_file") + name);
|
||
217 | } |
||
218 | } |
||
219 | |||
220 | /**
|
||
221 | * Carga la clase
|
||
222 | *
|
||
223 | * @param name Nombre de la clase
|
||
224 | * @param resolve Si se ha de resolver la clase o no
|
||
225 | *
|
||
226 | * @return Clase cargada
|
||
227 | *
|
||
228 | * @throws ClassNotFoundException Si no se pudo encontrar la clase
|
||
229 | */
|
||
230 | protected Class loadClass(String name, boolean resolve) |
||
231 | throws ClassNotFoundException { |
||
232 | Class c = null; |
||
233 | |||
234 | // Intentamos cargar con el system classloader
|
||
235 | try {
|
||
236 | 8765 | jjdelcerro | c = super.loadClass(name, resolve);
|
237 | 2927 | fernando | } catch (ClassNotFoundException e1) { |
238 | 7226 | cesar | c = singleLoadClass(name); |
239 | 598 | fernando | } |
240 | |||
241 | if (resolve) {
|
||
242 | resolveClass(c); |
||
243 | } |
||
244 | |||
245 | return c;
|
||
246 | } |
||
247 | 3896 | fjp | |
248 | 598 | fernando | |
249 | /**
|
||
250 | * obtiene el array de bytes de la clase
|
||
251 | *
|
||
252 | * @param classFile Entrada dentro del jar contiene los bytecodes de la
|
||
253 | * clase (el .class)
|
||
254 | * @param is InputStream para leer la entrada del jar
|
||
255 | *
|
||
256 | * @return Bytes de la clase
|
||
257 | *
|
||
258 | * @throws IOException Si no se puede obtener el .class del jar
|
||
259 | */
|
||
260 | private byte[] loadClassData(ZipEntry classFile, InputStream is) |
||
261 | throws IOException { |
||
262 | // Get size of class file
|
||
263 | int size = (int) classFile.getSize(); |
||
264 | |||
265 | // Reserve space to read
|
||
266 | byte[] buff = new byte[size]; |
||
267 | |||
268 | // Get stream to read from
|
||
269 | DataInputStream dis = new DataInputStream(is); |
||
270 | |||
271 | // Read in data
|
||
272 | dis.readFully(buff); |
||
273 | |||
274 | // close stream
|
||
275 | dis.close(); |
||
276 | |||
277 | // return data
|
||
278 | return buff;
|
||
279 | } |
||
280 | |||
281 | /**
|
||
282 | 2291 | fernando | * Gets the bytes of a File
|
283 | *
|
||
284 | * @param file File
|
||
285 | *
|
||
286 | * @return bytes of file
|
||
287 | *
|
||
288 | * @throws IOException If the operation fails
|
||
289 | */
|
||
290 | private byte[] loadClassData(File file) throws IOException { |
||
291 | InputStream is = new FileInputStream(file); |
||
292 | |||
293 | // Get the size of the file
|
||
294 | long length = file.length();
|
||
295 | |||
296 | // You cannot create an array using a long type.
|
||
297 | // It needs to be an int type.
|
||
298 | // Before converting to an int type, check
|
||
299 | // to ensure that file is not larger than Integer.MAX_VALUE.
|
||
300 | if (length > Integer.MAX_VALUE) { |
||
301 | // File is too large
|
||
302 | } |
||
303 | |||
304 | // Create the byte array to hold the data
|
||
305 | byte[] bytes = new byte[(int) length]; |
||
306 | |||
307 | // Read in the bytes
|
||
308 | int offset = 0; |
||
309 | int numRead = 0; |
||
310 | |||
311 | while ((offset < bytes.length) &&
|
||
312 | ((numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)) {
|
||
313 | offset += numRead; |
||
314 | } |
||
315 | |||
316 | // Ensure all the bytes have been read in
|
||
317 | if (offset < bytes.length) {
|
||
318 | throw new IOException("Could not completely read file " + |
||
319 | file.getName()); |
||
320 | } |
||
321 | |||
322 | // Close the input stream and return bytes
|
||
323 | is.close(); |
||
324 | |||
325 | return bytes;
|
||
326 | } |
||
327 | |||
328 | /**
|
||
329 | 598 | fernando | * Obtiene los recursos tomando como la raiz el directorio base del plugin.
|
330 | * Si no se encuentra el recurso ah? se invoca a getResource del
|
||
331 | * classloader padre, que buscar? en el jar de la aplicaci?n. Si ah?
|
||
332 | * tampoco se encuentra nada se devolver? null.
|
||
333 | *
|
||
334 | * @param res Nombre del recurso
|
||
335 | *
|
||
336 | * @return URL del recurso o null si no se pudo encontrar
|
||
337 | */
|
||
338 | public URL getResource(String res) { |
||
339 | try {
|
||
340 | ArrayList resource = new ArrayList(); |
||
341 | StringTokenizer st = new StringTokenizer(res, "\\/"); |
||
342 | |||
343 | while (st.hasMoreTokens()) {
|
||
344 | String token = st.nextToken();
|
||
345 | resource.add(token); |
||
346 | } |
||
347 | |||
348 | 736 | vcaballero | URL ret = getResource(baseDir, resource);
|
349 | 598 | fernando | |
350 | if (ret != null) { |
||
351 | return ret;
|
||
352 | } |
||
353 | } catch (Exception e) { |
||
354 | e.printStackTrace(); |
||
355 | } |
||
356 | |||
357 | return super.getResource(res); |
||
358 | } |
||
359 | |||
360 | /**
|
||
361 | * Busca recursivamente el recurso res en el directorio base. res es una
|
||
362 | * lista de String's con los directorios del path y base es el directorio
|
||
363 | * a partir del cual se busca dicho recurso. En cada ejecuci?n del m?todo
|
||
364 | * se toma el primer elemento de res y se busca dicho directorio en el
|
||
365 | * directorio base. Si se encuentra, ser? el directorio base para una
|
||
366 | * nueva llamada.
|
||
367 | *
|
||
368 | * @param base Directorio desde donde parte la b?squeda del recurso.
|
||
369 | * @param res Lista de strings con el path del recurso que se quiere
|
||
370 | * encontrar
|
||
371 | *
|
||
372 | * @return URL con el recurso
|
||
373 | */
|
||
374 | private URL getResource(File base, List res) { |
||
375 | File[] files = base.listFiles(); |
||
376 | |||
377 | String parte = (String) res.get(0); |
||
378 | |||
379 | for (int i = 0; i < files.length; i++) { |
||
380 | if (files[i].getName().compareTo(parte) == 0) { |
||
381 | if (res.size() == 1) { |
||
382 | try {
|
||
383 | return new URL("file:" + files[i].toString()); |
||
384 | } catch (MalformedURLException e) { |
||
385 | return null; |
||
386 | } |
||
387 | } else {
|
||
388 | return getResource(files[i], res.subList(1, res.size())); |
||
389 | } |
||
390 | } |
||
391 | } |
||
392 | |||
393 | return null; |
||
394 | } |
||
395 | |||
396 | /**
|
||
397 | * Devuelve el nombre del directorio del plugin
|
||
398 | *
|
||
399 | * @return
|
||
400 | */
|
||
401 | public String getPluginName() { |
||
402 | 2291 | fernando | String ret = baseDir.getAbsolutePath().substring(baseDir.getAbsolutePath()
|
403 | .lastIndexOf(File.separatorChar) +
|
||
404 | 598 | fernando | 1);
|
405 | |||
406 | return ret;
|
||
407 | } |
||
408 | |||
409 | /**
|
||
410 | * @see java.security.SecureClassLoader#getPermissions(java.security.CodeSource)
|
||
411 | */
|
||
412 | protected PermissionCollection getPermissions(CodeSource codesource) { |
||
413 | PermissionCollection perms = super.getPermissions(codesource); |
||
414 | perms.add(new AllPermission()); |
||
415 | |||
416 | return perms;
|
||
417 | } |
||
418 | |||
419 | /**
|
||
420 | * DOCUMENT ME!
|
||
421 | *
|
||
422 | * @return Returns the baseDir.
|
||
423 | */
|
||
424 | public String getBaseDir() { |
||
425 | 736 | vcaballero | return baseDir.getAbsolutePath();
|
426 | 598 | fernando | } |
427 | } |