Statistics
| Revision:

root / branches / v2_0_0_prep / frameworks / _fwAndami / src / org / gvsig / andami / plugins / PluginClassLoader.java @ 38564

History | View | Annotate | Download (16 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 29593 jpiera
package org.gvsig.andami.plugins;
42 598 fernando
43 34317 cordinyana
import java.io.DataInputStream;
44
import java.io.File;
45
import java.io.FileInputStream;
46
import java.io.IOException;
47
import java.io.InputStream;
48 598 fernando
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 34317 cordinyana
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 598 fernando
import java.util.zip.ZipEntry;
60
import java.util.zip.ZipException;
61
import java.util.zip.ZipFile;
62
63 27139 csanchez
import org.slf4j.Logger;
64
import org.slf4j.LoggerFactory;
65 598 fernando
66 34317 cordinyana
import org.gvsig.andami.messages.Messages;
67 5275 jaume
68
69 34317 cordinyana
70 598 fernando
/**
71 11997 cesar
 * <p>Class loader which loads the classes requested by the
72
 * plugins. It first tries to search in the classpath, then it requests
73
 * the class to the parent classloader, then it searches in the owns
74
 * plugins' library dir, and if all these methods fail, it tries to load
75
 * the class from any of the depended plugins. Finally, if this also
76
 * fails, the other classloaders provided in the <code>addLoaders</code> method
77
 * are requested to load the class.</p>
78
 *
79
 * <p>The class loader can also be used to load resources from the
80
 * plugin's directory by using the <code>getResource()</code> method.</p>
81 598 fernando
 *
82
 * @author Fernando Gonz?lez Cort?s
83
 */
84
public class PluginClassLoader extends URLClassLoader {
85
    /** DOCUMENT ME! */
86 27139 csanchez
    private static Logger logger = LoggerFactory.getLogger(PluginClassLoader.class.getName());
87 598 fernando
88
    /** DOCUMENT ME! */
89
    private Hashtable clasesJar = new Hashtable();
90
91
    /** DOCUMENT ME! */
92 736 vcaballero
    private File baseDir;
93 598 fernando
    private PluginClassLoader[] pluginLoaders;
94 9634 caballero
    private static ArrayList otherLoaders=new ArrayList();
95 9702 caballero
    private boolean isOtherLoader=false;
96 11997 cesar
97 598 fernando
    /**
98
     * Creates a new PluginClassLoader object.
99
     *
100 11997 cesar
     * @param jars Array with the search paths where classes will be searched
101
     * @param baseDir Base directory for this plugin. This is the directory
102
     * which will be used as basedir in the <code>getResources</code> method.
103
     * @param cl The parent classloader of this classloader. It will be used to
104
     * search classes before trying to search in the plugin's directory
105
     * @param pluginLoaders The classloaders of the depended plugins.
106 2291 fernando
     *
107 598 fernando
     * @throws IOException
108
     */
109
    public PluginClassLoader(URL[] jars, String baseDir, ClassLoader cl,
110
        PluginClassLoader[] pluginLoaders) throws IOException {
111
        super(jars, cl);
112 736 vcaballero
        this.baseDir = new File(new File(baseDir).getAbsolutePath());
113 598 fernando
        this.pluginLoaders = pluginLoaders;
114
115
        ZipFile[] jarFiles = new ZipFile[jars.length];
116
117
        for (int i = 0; i < jars.length; i++) {
118
            try {
119
                jarFiles[i] = new ZipFile(jars[i].getPath());
120
121
                Enumeration entradas = jarFiles[i].entries();
122
123
                while (entradas.hasMoreElements()) {
124
                    ZipEntry file = (ZipEntry) entradas.nextElement();
125
                    String fileName = file.getName();
126
127
                    if (!fileName.toLowerCase().endsWith(".class")) { //$NON-NLS-1$
128
129
                        continue;
130
                    }
131
132
                    fileName = fileName.substring(0, fileName.length() - 6)
133
                                       .replace('/', '.');
134
135
                    if (clasesJar.get(fileName) != null) {
136 34317 cordinyana
                                                logger.warn(Messages
137
                                                                .getString(
138 7038 cesar
                                "PluginClassLoader.Dos_clases_con_el_mismo_nombre_en_el_plugin") +
139
                                ": " + fileName + " "+ Messages.getString("en") + " " +
140 34317 cordinyana
                                jarFiles[i].getName() + " " + Messages.getString("y_en") + " " +
141 7038 cesar
                                ((ZipFile) clasesJar.get(fileName)).getName());
142 598 fernando
                    }
143 34317 cordinyana
                    else {
144
                            clasesJar.put(fileName, jarFiles[i]);
145
                    }
146 598 fernando
                }
147
            } catch (ZipException e) {
148 2291 fernando
                throw new IOException(e.getMessage() + " Jar: " +
149
                    jars[i].getPath() + ": " + jarFiles[i]);
150 598 fernando
            } catch (IOException e) {
151
                throw e;
152 2291 fernando
            }
153 598 fernando
        }
154
    }
155
156
    /**
157
     * DOCUMENT ME!
158
     *
159
     * @param name DOCUMENT ME!
160
     *
161
     * @return DOCUMENT ME!
162
     *
163
     * @throws ClassNotFoundException DOCUMENT ME!
164
     */
165 2291 fernando
    protected Class singleLoadClass(String name) throws ClassNotFoundException {
166 598 fernando
        // Buscamos en las clases de las librer?as del plugin
167
        Class c = findLoadedClass(name);
168
169 2291 fernando
        if (c != null) {
170
            return c;
171
        }
172
173 598 fernando
        try {
174
            ZipFile jar = (ZipFile) clasesJar.get(name);
175
176 2291 fernando
            //No est? en ning?n jar
177 598 fernando
            if (jar == null) {
178 2291 fernando
                //Buscamos en el directorio de clases
179
                String classFileName = baseDir + "/classes/" +
180
                    name.replace('.', '/') + ".class";
181
                File f = new File(classFileName);
182
                if (f.exists()){
183
                    byte[] data = loadClassData(f);
184
                    c = defineClass(name, data, 0, data.length);
185 27999 cordinyana
186
                    if (logger.isDebugEnabled()) {
187
                        logger.debug("Class {} found in the classes folder by the "
188
                                + "classloader for the plugin {}",
189
                                    name, baseDir);
190
                    }
191
192 2291 fernando
                }else{
193
                    //Buscamos en los otros plugins
194
                    for (int i = 0; i < pluginLoaders.length; i++) {
195 9634 caballero
                            try
196 3896 fjp
                            {
197 5013 jorpiell
                                    if (pluginLoaders[i] != null){
198
                                            c = pluginLoaders[i].singleLoadClass(name);
199
                                    }else{
200
                                            //TODO El pluginLoaders[i] puede ser nulo?
201
                                            logger.warn("PluginLoaders[i] es nulo");
202
                                    }
203 3896 fjp
                            }
204
                            catch (ClassNotFoundException e)
205
                            {
206
                                    // Si no la encontramos en el primer plugin, capturamos la exceptci?n
207
                                    // porque es probable que la encontremos en el resto de plugins.
208 9634 caballero
                            }
209 598 fernando
210 2291 fernando
                        if (c != null) {
211
                            break;
212
                        }
213 598 fernando
                    }
214
                }
215
            } else {
216
                String fileName = name.replace('.', '/') + ".class";
217
                ZipEntry classFile = jar.getEntry(fileName);
218
                byte[] data = loadClassData(classFile,
219
                        jar.getInputStream(classFile));
220
221
                c = defineClass(name, data, 0, data.length);
222 27999 cordinyana
223
                if (logger.isDebugEnabled()) {
224
                    logger.debug("Class {} found in the jar file {} by the "
225
                            + "classloader for the plugin {}",
226
                                new Object[] { name, jar.getName(), baseDir });
227
                }
228 598 fernando
            }
229
230
            if (c == null) {
231
                throw new ClassNotFoundException(name);
232
            }
233
234
            return c;
235
        } catch (IOException e) {
236
            throw new ClassNotFoundException(Messages.getString(
237
                    "PluginClassLoader.Error_reading_file") + name);
238
        }
239
    }
240
241
    /**
242
     * Carga la clase
243
     *
244
     * @param name Nombre de la clase
245
     * @param resolve Si se ha de resolver la clase o no
246
     *
247
     * @return Clase cargada
248
     *
249
     * @throws ClassNotFoundException Si no se pudo encontrar la clase
250
     */
251
    protected Class loadClass(String name, boolean resolve)
252
        throws ClassNotFoundException {
253
        Class c = null;
254
255
        // Intentamos cargar con el system classloader
256
        try {
257 9702 caballero
            if (!isOtherLoader)
258
                    c = super.loadClass(name, resolve);
259 27999 cordinyana
            logger.debug("Class {} found by the system classloader", name);
260 2927 fernando
        } catch (ClassNotFoundException e1) {
261 9634 caballero
                try {
262 9687 caballero
                        c = singleLoadClass(name);
263
                } catch (ClassNotFoundException e2) {
264 9699 caballero
                        try {
265 9702 caballero
                                isOtherLoader=true;
266
                                c = loadOtherClass(name);
267 9699 caballero
                        }catch (ClassNotFoundException e3) {
268 27999 cordinyana
                    // throw new ClassNotFoundException(Messages.getString(
269
                    // "PluginClassLoader.Error_reading_file")
270
                    // + name, e3);
271
                    throw new ClassNotFoundException("Class " + name
272
                            + " not found through the plugin " + baseDir, e3);
273 9702 caballero
                        }finally {
274
                                isOtherLoader=false;
275 9699 caballero
                        }
276 9634 caballero
                }
277 598 fernando
        }
278 9703 caballero
        if (c==null)
279
                 throw new ClassNotFoundException(Messages.getString(
280
             "PluginClassLoader.Error_reading_file") + name);
281 598 fernando
        if (resolve) {
282
            resolveClass(c);
283
        }
284
        return c;
285
    }
286 9702 caballero
    private Class loadOtherClass(String name)
287 9634 caballero
                throws ClassNotFoundException
288
    {
289
        ClassLoader[] ocl=(ClassLoader[])otherLoaders.toArray(new ClassLoader[0]);
290
    Class c=null;
291
        for (int i=0;i<ocl.length;i++) {
292 9702 caballero
                c=ocl[i].loadClass(name);
293 27999 cordinyana
                if (c != null) {
294
            logger
295
                        .debug(
296
                        "Class {} found by the alternative classloaders of the plugin {}",
297
                                name, baseDir);
298
299 9634 caballero
                    return c;
300 27999 cordinyana
                }
301 9634 caballero
    }
302 27999 cordinyana
        throw new ClassNotFoundException(name);
303 9634 caballero
    }
304 598 fernando
305
    /**
306
     * obtiene el array de bytes de la clase
307
     *
308
     * @param classFile Entrada dentro del jar contiene los bytecodes de la
309
     *        clase (el .class)
310
     * @param is InputStream para leer la entrada del jar
311
     *
312
     * @return Bytes de la clase
313
     *
314
     * @throws IOException Si no se puede obtener el .class del jar
315
     */
316
    private byte[] loadClassData(ZipEntry classFile, InputStream is)
317
        throws IOException {
318
        // Get size of class file
319
        int size = (int) classFile.getSize();
320
321
        // Reserve space to read
322
        byte[] buff = new byte[size];
323
324
        // Get stream to read from
325
        DataInputStream dis = new DataInputStream(is);
326
327
        // Read in data
328
        dis.readFully(buff);
329
330
        // close stream
331
        dis.close();
332
333
        // return data
334
        return buff;
335
    }
336
337
    /**
338 2291 fernando
     * Gets the bytes of a File
339
     *
340
     * @param file File
341
     *
342
     * @return bytes of file
343
     *
344
     * @throws IOException If the operation fails
345
     */
346
    private byte[] loadClassData(File file) throws IOException {
347
        InputStream is = new FileInputStream(file);
348
349
        // Get the size of the file
350
        long length = file.length();
351
352
        // You cannot create an array using a long type.
353
        // It needs to be an int type.
354
        // Before converting to an int type, check
355
        // to ensure that file is not larger than Integer.MAX_VALUE.
356
        if (length > Integer.MAX_VALUE) {
357
            // File is too large
358
        }
359
360
        // Create the byte array to hold the data
361
        byte[] bytes = new byte[(int) length];
362
363
        // Read in the bytes
364
        int offset = 0;
365
        int numRead = 0;
366
367
        while ((offset < bytes.length) &&
368
                ((numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)) {
369
            offset += numRead;
370
        }
371
372
        // Ensure all the bytes have been read in
373
        if (offset < bytes.length) {
374
            throw new IOException("Could not completely read file " +
375
                file.getName());
376
        }
377
378
        // Close the input stream and return bytes
379
        is.close();
380
381
        return bytes;
382
    }
383
384
    /**
385 11997 cesar
     * Gets the requested resource. If the path is relative, its base directory
386
     * will be the one provided in the PluginClassLoader's constructor.
387
     * If the resource is not found, the parent classloader will be invoked
388
     * to try to get it. If it is not found, it will return null.
389 598 fernando
     *
390 11997 cesar
     * @param res An absolute or relative path to the requested resource.
391 598 fernando
     *
392 11997 cesar
     * @return Resource's URL if it was found, nul otherwise.
393 598 fernando
     */
394
    public URL getResource(String res) {
395
        try {
396
            ArrayList resource = new ArrayList();
397
            StringTokenizer st = new StringTokenizer(res, "\\/");
398
399
            while (st.hasMoreTokens()) {
400
                String token = st.nextToken();
401
                resource.add(token);
402
            }
403
404 736 vcaballero
            URL ret = getResource(baseDir, resource);
405 598 fernando
406
            if (ret != null) {
407
                return ret;
408
            }
409
        } catch (Exception e) {
410
            e.printStackTrace();
411
        }
412
413
        return super.getResource(res);
414
    }
415
416
    /**
417 11997 cesar
     * Gets the requested resource. If the path is relative, its base directory
418
     * will be the one provided in the PluginClassLoader's constructor.
419
     * If the resource is not found, the parent classloader will be invoked
420
     * to try to get it. If it is not found, it will return null.
421 598 fernando
     *
422 11997 cesar
     * @param res An absolute or relative path to the requested resource.
423 598 fernando
     *
424 11997 cesar
     * @return Resource's URL if it was found, nul otherwise.
425 598 fernando
     */
426
    private URL getResource(File base, List res) {
427
        File[] files = base.listFiles();
428
429
        String parte = (String) res.get(0);
430
431
        for (int i = 0; i < files.length; i++) {
432
            if (files[i].getName().compareTo(parte) == 0) {
433
                if (res.size() == 1) {
434
                    try {
435
                        return new URL("file:" + files[i].toString());
436
                    } catch (MalformedURLException e) {
437
                        return null;
438
                    }
439
                } else {
440
                    return getResource(files[i], res.subList(1, res.size()));
441
                }
442
            }
443
        }
444
445
        return null;
446
    }
447
448
    /**
449 11997 cesar
     * Returns the name of the plugin (the name of the directory containing
450
     * the plugin).
451 598 fernando
     *
452 11997 cesar
     * @return An String containing the plugin's name.
453 598 fernando
     */
454
    public String getPluginName() {
455 2291 fernando
        String ret = baseDir.getAbsolutePath().substring(baseDir.getAbsolutePath()
456
                                                                .lastIndexOf(File.separatorChar) +
457 598 fernando
                1);
458
459
        return ret;
460
    }
461
462 11997 cesar
    /*
463 598 fernando
     * @see java.security.SecureClassLoader#getPermissions(java.security.CodeSource)
464
     */
465
    protected PermissionCollection getPermissions(CodeSource codesource) {
466
        PermissionCollection perms = super.getPermissions(codesource);
467
        perms.add(new AllPermission());
468
469
        return perms;
470
    }
471
472
    /**
473 11997 cesar
     * Gets the plugin's base dir, the directory which will be used to
474
     * search resources.
475 598 fernando
     *
476
     * @return Returns the baseDir.
477
     */
478
    public String getBaseDir() {
479 736 vcaballero
        return baseDir.getAbsolutePath();
480 598 fernando
    }
481 9634 caballero
482 11997 cesar
    /**
483
     * Adds other classloader to use when all the normal methods fail.
484
     *
485
     * @param classLoaders An ArrayList of ClassLoaders which will
486
     * be used to load classes when all the normal methods fail.
487
     */
488 9634 caballero
        public static void addLoaders(ArrayList classLoaders) {
489
                otherLoaders.addAll(classLoaders);
490
        }
491 36687 cordinyana
492
    @Override
493
    public String toString() {
494
        return super.toString() + " (" + getPluginName() + ")";
495
    }
496 598 fernando
}