Revision 42437

View differences:

trunk/org.gvsig.desktop/org.gvsig.desktop.framework/org.gvsig.andami/src/main/java/org/gvsig/andami/plugins/PluginClassLoader.java
3 3
 *
4 4
 * Copyright (C) 2007-2013 gvSIG Association.
5 5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
6
 * This program is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10 10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15 15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 19
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
23 22
 */
24 23
package org.gvsig.andami.plugins;
25 24

  
......
53 52
import org.slf4j.Logger;
54 53
import org.slf4j.LoggerFactory;
55 54

  
56

  
57

  
58 55
/**
59
 * <p>Class loader which loads the classes requested by the
60
 * plugins. It first tries to search in the classpath, then it requests
61
 * the class to the parent classloader, then it searches in the owns
62
 * plugins' library dir, and if all these methods fail, it tries to load
63
 * the class from any of the depended plugins. Finally, if this also
64
 * fails, the other classloaders provided in the <code>addLoaders</code> method
65
 * are requested to load the class.</p>
66
 * 
67
 * <p>The class loader can also be used to load resources from the
68
 * plugin's directory by using the <code>getResource()</code> method.</p>
56
 * <p>
57
 * Class loader which loads the classes requested by the plugins. It first tries
58
 * to search in the classpath, then it requests the class to the parent
59
 * classloader, then it searches in the owns plugins' library dir, and if all
60
 * these methods fail, it tries to load the class from any of the depended
61
 * plugins. Finally, if this also fails, the other classloaders provided in the
62
 * <code>addLoaders</code> method are requested to load the class.</p>
69 63
 *
64
 * <p>
65
 * The class loader can also be used to load resources from the plugin's
66
 * directory by using the <code>getResource()</code> method.</p>
67
 *
70 68
 * @author Fernando Gonz�lez Cort�s
71 69
 */
72 70
public class PluginClassLoader extends URLClassLoader {
73
    /** DOCUMENT ME! */
71

  
72
    /**
73
     * DOCUMENT ME!
74
     */
74 75
    private static Logger logger = LoggerFactory.getLogger(PluginClassLoader.class.getName());
75 76

  
76
    /** DOCUMENT ME! */
77
    /**
78
     * DOCUMENT ME!
79
     */
77 80
    private Hashtable<String, ZipFile> clasesJar = new Hashtable<String, ZipFile>();
78 81

  
79
    /** DOCUMENT ME! */
82
    /**
83
     * DOCUMENT ME!
84
     */
80 85
    private File baseDir;
81 86
    private List<PluginClassLoader> pluginLoaders;
82
    private static List<ClassLoader> otherLoaders=new ArrayList<ClassLoader>();
83
    private boolean isOtherLoader=false;
84
 
87
    private static List<ClassLoader> otherLoaders = new ArrayList<ClassLoader>();
88
    private boolean isOtherLoader = false;
89

  
85 90
    /**
86 91
     * Creates a new PluginClassLoader object.
87 92
     *
......
95 100
     * @throws IOException
96 101
     */
97 102
    public PluginClassLoader(URL[] jars, String baseDir, ClassLoader cl,
98
        PluginClassLoader[] pluginLoaders) throws IOException {
103
            PluginClassLoader[] pluginLoaders) throws IOException {
99 104
        this(jars, baseDir, cl, Arrays.asList(pluginLoaders));
100 105
    }
101
    
106

  
102 107
    public PluginClassLoader(URL[] jars, String baseDir, ClassLoader cl,
103 108
            List<PluginClassLoader> pluginLoaders) throws IOException {
104 109
        super(jars, cl);
......
107 112
        this.pluginLoaders = new ArrayList();
108 113
        this.pluginLoaders.addAll(pluginLoaders);
109 114

  
110
        if( jars==null || jars.length<1 ) { 
111
            debugDump(); 
112
        	return;
115
        if (jars == null || jars.length < 1) {
116
            debugDump();
117
            return;
113 118
        }
114 119
        ZipFile[] jarFiles = new ZipFile[jars.length];
115 120

  
......
129 134
                    }
130 135

  
131 136
                    fileName = fileName.substring(0, fileName.length() - 6)
132
                                       .replace('/', '.');
137
                            .replace('/', '.');
133 138

  
134 139
                    if (clasesJar.get(fileName) != null) {
135
						logger.warn("Duplicated class {} in {} and {}",
136
							new Object[] {
137
								fileName, 
138
								jarFiles[i].getName(),
139
								clasesJar.get(fileName).getName()
140
							}
141
						);
140
                        logger.warn("Duplicated class {} in {} and {}",
141
                                new Object[]{
142
                                    fileName,
143
                                    jarFiles[i].getName(),
144
                                    clasesJar.get(fileName).getName()
145
                                }
146
                        );
147
                    } else {
148
                        clasesJar.put(fileName, jarFiles[i]);
142 149
                    }
143
                    else {
144
                    	clasesJar.put(fileName, jarFiles[i]);
145
                    }
146 150
                }
147 151
            } catch (ZipException e) {
148
                throw new IOException(e.getMessage() + " Jar: " +
149
                    jars[i].getPath() + ": " + jarFiles[i]);
152
                throw new IOException(e.getMessage() + " Jar: "
153
                        + jars[i].getPath() + ": " + jarFiles[i]);
150 154
            } catch (IOException e) {
151 155
                throw e;
152 156
            } finally {
153
                debugDump(); 
157
                debugDump();
154 158
            }
155 159
        }
156 160
    }
157 161

  
158 162
    private void debugDump() {
159
    	if( !logger.isDebugEnabled() ) {
160
    		return;
161
    	}
162
    	logger.debug(this.toString());
163
    	logger.debug("  baseDir: " + this.baseDir);
164
    	logger.debug("  parent: " + this.getParent());
165
    	logger.debug("  depends:");
166
    	for( int n=0; n<this.pluginLoaders.size(); n++ ) {
167
    		logger.debug("    {}", this.pluginLoaders.get(n).toString() );
168
    	}
169
    	logger.debug("  urls:");
170
    	URL[] urls = this.getURLs();
171
    	for( int n=0 ; n<urls.length; n++ ) {
172
    		logger.debug("    " + urls[n].toString());
173
    	}
174
    	logger.debug("  classes:");
175
    	Iterator<Map.Entry<String, ZipFile>>it = this.clasesJar.entrySet().iterator();
176
    	while( it.hasNext() ) {
177
    		Entry<String, ZipFile> entry = it.next();
178
    		logger.debug("    "+ entry.getKey() + "("+entry.getValue().getName()+")");
179
    	}
163
        if (!logger.isDebugEnabled()) {
164
            return;
165
        }
166
        logger.debug(this.toString());
167
        logger.debug("  baseDir: " + this.baseDir);
168
        logger.debug("  parent: " + this.getParent());
169
        logger.debug("  depends:");
170
        for (int n = 0; n < this.pluginLoaders.size(); n++) {
171
            logger.debug("    {}", this.pluginLoaders.get(n).toString());
172
        }
173
        logger.debug("  urls:");
174
        URL[] urls = this.getURLs();
175
        for (int n = 0; n < urls.length; n++) {
176
            logger.debug("    " + urls[n].toString());
177
        }
178
        logger.debug("  classes:");
179
        Iterator<Map.Entry<String, ZipFile>> it = this.clasesJar.entrySet().iterator();
180
        while (it.hasNext()) {
181
            Entry<String, ZipFile> entry = it.next();
182
            logger.debug("    " + entry.getKey() + "(" + entry.getValue().getName() + ")");
183
        }
180 184
    }
181
    
185

  
182 186
    protected Class singleLoadClass(String name) throws ClassNotFoundException {
183 187
        Class<?> c;
184
        Set<String>pluginsVisiteds = new HashSet<String>();
188
        Set<String> pluginsVisiteds = new HashSet<String>();
185 189
        c = this.singleLoadClass(pluginsVisiteds, name);
186 190
        return c;
187 191
    }
......
197 201
     * @throws ClassNotFoundException Si no se pudo encontrar la clase
198 202
     */
199 203
    protected Class loadClass(String name, boolean resolve)
200
        throws ClassNotFoundException {
204
            throws ClassNotFoundException {
201 205
        Class<?> c = null;
202 206

  
203 207
        // Intentamos cargar con el system classloader
204 208
        try {
205
            if (!isOtherLoader)
206
            	c = super.loadClass(name, resolve);
207
            logger.debug("Found class {} in system-classloader", name);
209
            if (!isOtherLoader) {
210
                c = super.loadClass(name, resolve);
211
                logger.debug("Found class {} in system-classloader", name);
212
            }
208 213
        } catch (ClassNotFoundException e1) {
209
        	try {
210
        		c = singleLoadClass(name);
211
        	} catch (ClassNotFoundException e2) {
212
        		try {
213
        			isOtherLoader=true;
214
        			c = loadOtherClass(name);
215
        		}catch (ClassNotFoundException e3) {
216
                    // throw new ClassNotFoundException(Messages.getString(
217
                    // "PluginClassLoader.Error_reading_file")
218
                    // + name, e3);
214
            try {
215
                c = singleLoadClass(name);
216
            } catch (ClassNotFoundException e2) {
217
                try {
218
                    isOtherLoader = true;
219
                    c = loadOtherClass(name);
220
                } catch (ClassNotFoundException e3) {
219 221
                    throw new ClassNotFoundException("Class " + name
220
                            + " not found through the plugin " + baseDir, e3);        			 
221
        		}finally {
222
        			isOtherLoader=false;
223
        		}
224
        	}
222
                            + " not found through the plugin " + baseDir, e3);
223
                } finally {
224
                    isOtherLoader = false;
225
                }
226
            }
225 227
        }
226
        if (c==null)
227
        	 throw new ClassNotFoundException(Messages.getString(
228
             "PluginClassLoader.Error_reading_file") + name);
228
        if (c == null) {
229
            throw new ClassNotFoundException(Messages.getString(
230
                    "PluginClassLoader.Error_reading_file") + name);
231
        }
229 232
        if (resolve) {
230 233
            resolveClass(c);
231 234
        }
232 235
        return c;
233 236
    }
234
    
237

  
235 238
    private Class<?> loadOtherClass(String name)
236 239
            throws ClassNotFoundException {
237 240
        ClassLoader[] ocl = (ClassLoader[]) otherLoaders.toArray(new ClassLoader[0]);
......
252 255
     * obtiene el array de bytes de la clase
253 256
     *
254 257
     * @param classFile Entrada dentro del jar contiene los bytecodes de la
255
     *        clase (el .class)
258
     * clase (el .class)
256 259
     * @param is InputStream para leer la entrada del jar
257 260
     *
258 261
     * @return Bytes de la clase
......
260 263
     * @throws IOException Si no se puede obtener el .class del jar
261 264
     */
262 265
    private byte[] loadClassData(ZipEntry classFile, InputStream is)
263
        throws IOException {
266
            throws IOException {
264 267
        // Get size of class file
265 268
        int size = (int) classFile.getSize();
266 269

  
......
310 313
        int offset = 0;
311 314
        int numRead = 0;
312 315

  
313
        while ((offset < bytes.length) &&
314
                ((numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)) {
316
        while ((offset < bytes.length)
317
                && ((numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)) {
315 318
            offset += numRead;
316 319
        }
317 320

  
......
320 323

  
321 324
        // Ensure all the bytes have been read in
322 325
        if (offset < bytes.length) {
323
            throw new IOException("Could not completely read file " +
324
                file.getName());
326
            throw new IOException("Could not completely read file "
327
                    + file.getName());
325 328
        }
326 329

  
327 330
        return bytes;
......
329 332

  
330 333
    /**
331 334
     * Gets the requested resource. If the path is relative, its base directory
332
     * will be the one provided in the PluginClassLoader's constructor.
333
     * If the resource is not found, search in dependents plugins, otherwise 
334
     * the parent classloader will be invoked to try to get it. 
335
     * If it is not found, it will return null.
335
     * will be the one provided in the PluginClassLoader's constructor. If the
336
     * resource is not found, search in dependents plugins, otherwise the parent
337
     * classloader will be invoked to try to get it. If it is not found, it will
338
     * return null.
336 339
     *
337 340
     * @param res An absolute or relative path to the requested resource.
338 341
     *
......
341 344
    public URL getResource(String res) {
342 345
        URL ret = null;
343 346

  
344
        Set<String>pluginsVisiteds = new HashSet<String>();
347
        Set<String> pluginsVisiteds = new HashSet<String>();
345 348
        ret = this.getResource(pluginsVisiteds, res);
346 349
        return ret;
347 350
    }
348 351

  
349 352
    /**
350 353
     * Gets the requested resource. If the path is relative, its base directory
351
     * will be the one provided in the PluginClassLoader's constructor.
352
     * If the resource is not found, the parent classloader will be invoked
353
     * to try to get it. If it is not found, it will return null.
354
     * will be the one provided in the PluginClassLoader's constructor. If the
355
     * resource is not found, the parent classloader will be invoked to try to
356
     * get it. If it is not found, it will return null.
354 357
     *
355 358
     * @param res An absolute or relative path to the requested resource.
356 359
     *
......
379 382
    }
380 383

  
381 384
    /**
382
     * Returns the name of the plugin (the name of the directory containing
383
     * the plugin).
385
     * Returns the name of the plugin (the name of the directory containing the
386
     * plugin).
384 387
     *
385 388
     * @return An String containing the plugin's name.
386 389
     */
387 390
    public String getPluginName() {
388
    	return baseDir.getName();
391
        return baseDir.getName();
389 392
    }
390 393

  
391 394
    /*
......
399 402
    }
400 403

  
401 404
    /**
402
     * Gets the plugin's base Iterator<Map.Entry<Integer, Integer>>dir, the directory which will be used to
403
     * search resources.
405
     * Gets the plugin's base Iterator<Map.Entry<Integer, Integer>>dir, the
406
     * directory which will be used to search resources.
404 407
     *
405 408
     * @return Returns the baseDir.
406 409
     */
......
410 413

  
411 414
    /**
412 415
     * Adds other classloader to use when all the normal methods fail.
413
     * 
414
     * @param classLoaders An ArrayList of ClassLoaders which will
415
     * be used to load classes when all the normal methods fail.
416
     *
417
     * @param classLoaders An ArrayList of ClassLoaders which will be used to
418
     * load classes when all the normal methods fail.
416 419
     */
417
	public static void addLoaders(ArrayList classLoaders) {
418
		otherLoaders.addAll(classLoaders);
419
	}
420
    public static void addLoaders(ArrayList classLoaders) {
421
        otherLoaders.addAll(classLoaders);
422
    }
420 423

  
421 424
    @Override
422 425
    public String toString() {
423 426
        return super.toString() + " (" + getPluginName() + ")";
424 427
    }
425
    
428

  
426 429
    public void addPluginClassLoader(PluginClassLoader pluginClassLoader) {
427
        if( !this.pluginLoaders.contains(pluginClassLoader) ) {
430
        if (!this.pluginLoaders.contains(pluginClassLoader)) {
428 431
            this.pluginLoaders.add(pluginClassLoader);
429 432
        }
430 433
    }
431
    
434

  
432 435
    private Class singleLoadClass(Set<String> pluginsVisiteds, String name) throws ClassNotFoundException {
433
        
434
        if ( pluginsVisiteds.contains(this.getPluginName()) ) {
436

  
437
        if (pluginsVisiteds.contains(this.getPluginName())) {
435 438
            return null;
436 439
        }
437 440
        pluginsVisiteds.add(this.getPluginName());
......
443 446
            return c;
444 447
        }
445 448

  
446
        logger.debug("Searching class '{}' in {}", new Object[] {name, this.toString()});
449
        logger.debug("Searching class '{}' in {}", new Object[]{name, this.toString()});
447 450
        try {
448 451
            ZipFile jar = (ZipFile) clasesJar.get(name);
449 452

  
450 453
            //No esta en ningun jar
451 454
            if (jar == null) {
452 455
                //Buscamos en el directorio de clases
453
                String classFileName = baseDir + "/classes/" +
454
                    name.replace('.', '/') + ".class";
456
                String classFileName = baseDir + "/classes/"
457
                        + name.replace('.', '/') + ".class";
455 458
                File f = new File(classFileName);
456
                if (f.exists()){
459
                if (f.exists()) {
457 460
                    byte[] data = loadClassData(f);
458 461
                    c = defineClass(name, data, 0, data.length);
459 462
                    logger.debug("Found class {} in classes-folder of plugin {}",
460
                    		new Object[] {name, baseDir});
463
                            new Object[]{name, baseDir});
461 464

  
462 465
                } else {
463 466
                    //Buscamos en los otros plugins
464
                    for ( int i = 0; i < pluginLoaders.size(); i++ ) {
467
                    for (int i = 0; i < pluginLoaders.size(); i++) {
465 468
                        c = null;
466
                        if ( pluginLoaders.get(i) != null ) {
469
                        if (pluginLoaders.get(i) != null) {
467 470
                            try {
468 471
                                c = pluginLoaders.get(i).singleLoadClass(pluginsVisiteds, name);
469 472
                            } catch (ClassNotFoundException e) {
......
471 474
                                // porque es probable que la encontremos en el resto de plugins.
472 475
                            }
473 476
                        }
474
                        if ( c != null ) {
477
                        if (c != null) {
475 478
                            return c;
476 479
                        }
477 480
                    }
......
483 486
                        jar.getInputStream(classFile));
484 487

  
485 488
                c = defineClass(name, data, 0, data.length);
486
                
489

  
487 490
                logger.debug("Found class {} in jar {} of plugin {}",
488
                            new Object[] { name, jar.getName(), baseDir} );
491
                        new Object[]{name, jar.getName(), baseDir});
489 492
            }
490 493

  
491 494
            if (c == null) {
......
496 499
        } catch (IOException e) {
497 500
            throw new ClassNotFoundException(Messages.getString(
498 501
                    "PluginClassLoader.Error_reading_file") + name);
499
        }        
502
        }
500 503
    }
501 504

  
502 505
    /**
503
     * Este metodo busca en este class loader y en el de los plugins
504
     * de los que depende. Se cerciora de que no se queda
505
     * bucleado cuando hay una relacion recursiva entre los plugins.
506
     * 
507
     * @param pluginsVisiteds, set con los plugins que va visitando para evitar 
508
     *      que se quede bucleado cuando hay referencia ciclicas entre plugins.
506
     * Este metodo busca en este class loader y en el de los plugins de los que
507
     * depende. Se cerciora de que no se queda bucleado cuando hay una relacion
508
     * recursiva entre los plugins.
509
     *
510
     * @param pluginsVisiteds, set con los plugins que va visitando para evitar
511
     * que se quede bucleado cuando hay referencia ciclicas entre plugins.
509 512
     * @param res
510
     * 
511
     * @return 
513
     *
514
     * @return
512 515
     */
513
     private URL getResource(Set<String> pluginsVisiteds, String res) {
516
    private URL getResource(Set<String> pluginsVisiteds, String res) {
514 517
        URL ret = null;
515 518

  
516
        if ( pluginsVisiteds.contains(this.getPluginName()) ) {
519
        if (pluginsVisiteds.contains(this.getPluginName())) {
517 520
            return null;
518 521
        }
519 522
        pluginsVisiteds.add(this.getPluginName());
......
524 527
            logger.debug("Plugin {}. Searching resource '{}'", res, this.getPluginName());
525 528
            List<String> resource = new ArrayList<String>();
526 529
            StringTokenizer st = new StringTokenizer(res, "\\/");
527
            while ( st.hasMoreTokens() ) {
530
            while (st.hasMoreTokens()) {
528 531
                String token = st.nextToken();
529 532
                resource.add(token);
530 533
            }
531 534
            ret = getResource(baseDir, resource);
532
            if ( ret != null ) {
535
            if (ret != null) {
533 536
                return ret;
534 537
            }
535 538
        } catch (Exception e) {
......
537 540
        }
538 541

  
539 542
        logger.debug("Plugin {}. Searching in depends pluginLoaders", this.getPluginName());
540
        for ( int i = 0; i < this.pluginLoaders.size(); i++ ) {
543
        for (int i = 0; i < this.pluginLoaders.size(); i++) {
541 544
            PluginClassLoader pluginClassLoader = pluginLoaders.get(i);
542
            if ( pluginClassLoader != null ) {
545
            if (pluginClassLoader != null) {
543 546
                try {
544 547
                    pluginsVisiteds.add(pluginClassLoader.getPluginName());
545 548
                    ret = pluginClassLoader.getResource(pluginsVisiteds, res);
546
                    if ( ret != null ) {
549
                    if (ret != null) {
547 550
                        logger.trace("Plugin {}. Found resource '{}' in plugin '{}'.",
548 551
                                new Object[]{
549 552
                                    this.getPluginName(), pluginClassLoader.getPluginName(), res
......
556 559
            }
557 560
        }
558 561

  
559
        if ( ret == null ) {
562
        if (ret == null) {
560 563
            //
561 564
            // Por ultimo en el class loader padre, se supone que es el del sistema.
562 565
            try {
......
564 567
            } catch (Exception e) {
565 568
                logger.warn("Plugin {}. Error getting resource '{}' in parent classloader'", new Object[]{this.getPluginName(), res}, e);
566 569
            }
567
            if ( ret == null ) {
570
            if (ret == null) {
568 571
                logger.debug("Plugin {}. Resource '{}' not found.", new Object[]{this.getPluginName(), res});
569 572
            }
570 573
        }

Also available in: Unified diff