Statistics
| Revision:

root / trunk / frameworks / _fwAndami / src / com / iver / andami / plugins / PluginClassLoader.java @ 9634

History | View | Annotate | Download (13.8 KB)

1
/* 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
package com.iver.andami.plugins;
42

    
43
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
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
import org.apache.log4j.Logger;
65

    
66
import com.iver.andami.messages.Messages;
67

    
68

    
69
/**
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
    private File baseDir;
88
    private PluginClassLoader[] pluginLoaders;
89
    private static ArrayList otherLoaders=new ArrayList();
90

    
91
    /**
92
     * Creates a new PluginClassLoader object.
93
     *
94
     * @param jars Array con la ruta de los jars en los que buscar? las clases
95
     *        el plugin
96
     * @param baseDir Directorio base del plugin que se carga. Es en directorio
97
     *        donde se buscan los resources en el m?todo getResources
98
     * @param cl ClassLoader padre del classLoader, al que se le pedir?
99
     *        resolver las clases antes de utilizar el algoritmo propio
100
     * @param pluginLoaders DOCUMENT ME!
101
     *
102
     * @throws IOException
103
     */
104
    public PluginClassLoader(URL[] jars, String baseDir, ClassLoader cl,
105
        PluginClassLoader[] pluginLoaders) throws IOException {
106
        super(jars, cl);
107
        this.baseDir = new File(new File(baseDir).getAbsolutePath());
108
        this.pluginLoaders = pluginLoaders;
109

    
110
        ZipFile[] jarFiles = new ZipFile[jars.length];
111

    
112
        for (int i = 0; i < jars.length; i++) {
113
            try {
114
                jarFiles[i] = new ZipFile(jars[i].getPath());
115

    
116
                Enumeration entradas = jarFiles[i].entries();
117

    
118
                while (entradas.hasMoreElements()) {
119
                    ZipEntry file = (ZipEntry) entradas.nextElement();
120
                    String fileName = file.getName();
121

    
122
                    if (!fileName.toLowerCase().endsWith(".class")) { //$NON-NLS-1$
123

    
124
                        continue;
125
                    }
126

    
127
                    fileName = fileName.substring(0, fileName.length() - 6)
128
                                       .replace('/', '.');
129

    
130
                    if (clasesJar.get(fileName) != null) {
131
                        throw new JarException(Messages.getString(
132
                                "PluginClassLoader.Dos_clases_con_el_mismo_nombre_en_el_plugin") +
133
                                ": " + fileName + " "+ Messages.getString("en") + " " +
134
                                jarFiles[i].getName() + Messages.getString("y_en") + " " +
135
                                ((ZipFile) clasesJar.get(fileName)).getName());
136
                    }
137

    
138
                    clasesJar.put(fileName, jarFiles[i]);
139
                }
140
            } catch (ZipException e) {
141
                throw new IOException(e.getMessage() + " Jar: " +
142
                    jars[i].getPath() + ": " + jarFiles[i]);
143
            } catch (IOException e) {
144
                throw e;
145
            }
146
        }
147
    }
148

    
149
    /**
150
     * DOCUMENT ME!
151
     *
152
     * @param name DOCUMENT ME!
153
     *
154
     * @return DOCUMENT ME!
155
     *
156
     * @throws ClassNotFoundException DOCUMENT ME!
157
     */
158
    protected Class singleLoadClass(String name) throws ClassNotFoundException {
159
        // Buscamos en las clases de las librer?as del plugin
160
        Class c = findLoadedClass(name);
161

    
162
        if (c != null) {
163
            return c;
164
        }
165

    
166
        try {
167
            ZipFile jar = (ZipFile) clasesJar.get(name);
168

    
169
            //No est? en ning?n jar
170
            if (jar == null) {
171
                //Buscamos en el directorio de clases
172
                String classFileName = baseDir + "/classes/" +
173
                    name.replace('.', '/') + ".class";
174
                File f = new File(classFileName);
175
                if (f.exists()){
176
                    byte[] data = loadClassData(f);
177
                    c = defineClass(name, data, 0, data.length);
178
                }else{
179
                    //Buscamos en los otros plugins
180
                    for (int i = 0; i < pluginLoaders.length; i++) {
181
                            try
182
                            {
183
                                    if (pluginLoaders[i] != null){
184
                                            c = pluginLoaders[i].singleLoadClass(name);
185
                                    }else{
186
                                            //TODO El pluginLoaders[i] puede ser nulo?
187
                                            logger.warn("PluginLoaders[i] es nulo");
188
                                    }
189
                            }
190
                            catch (ClassNotFoundException e)
191
                            {
192
                                    // Si no la encontramos en el primer plugin, capturamos la exceptci?n
193
                                    // porque es probable que la encontremos en el resto de plugins.
194
                            }
195

    
196
                        if (c != null) {
197
                            break;
198
                        }
199
                    }
200
                }
201
            } else {
202
                String fileName = name.replace('.', '/') + ".class";
203
                ZipEntry classFile = jar.getEntry(fileName);
204
                byte[] data = loadClassData(classFile,
205
                        jar.getInputStream(classFile));
206

    
207
                c = defineClass(name, data, 0, data.length);
208
            }
209

    
210
            if (c == null) {
211
                throw new ClassNotFoundException(name);
212
            }
213

    
214
            return c;
215
        } catch (IOException e) {
216
            throw new ClassNotFoundException(Messages.getString(
217
                    "PluginClassLoader.Error_reading_file") + name);
218
        }
219
    }
220

    
221
    /**
222
     * Carga la clase
223
     *
224
     * @param name Nombre de la clase
225
     * @param resolve Si se ha de resolver la clase o no
226
     *
227
     * @return Clase cargada
228
     *
229
     * @throws ClassNotFoundException Si no se pudo encontrar la clase
230
     */
231
    protected Class loadClass(String name, boolean resolve)
232
        throws ClassNotFoundException {
233
        Class c = null;
234

    
235
        // Intentamos cargar con el system classloader
236
        try {
237
            c = super.loadClass(name, resolve);
238
        } catch (ClassNotFoundException e1) {
239
                try {
240
                        c = loadOtherClass(name,resolve);
241
                } catch (ClassNotFoundException e2) {
242
                         c = singleLoadClass(name);
243
                }
244
        }
245

    
246
        if (resolve) {
247
            resolveClass(c);
248
        }
249

    
250
        return c;
251
    }
252
    private Class loadOtherClass(String name, boolean resolve)
253
                throws ClassNotFoundException
254
    {
255
        ClassLoader[] ocl=(ClassLoader[])otherLoaders.toArray(new ClassLoader[0]);
256
    Class c=null;
257
        for (int i=0;i<ocl.length;i++) {
258
            c=ocl[i].loadClass(name);
259
            if (c!=null)
260
                    return c;
261
    }
262
        throw new ClassNotFoundException();
263
    }
264

    
265
    /**
266
     * obtiene el array de bytes de la clase
267
     *
268
     * @param classFile Entrada dentro del jar contiene los bytecodes de la
269
     *        clase (el .class)
270
     * @param is InputStream para leer la entrada del jar
271
     *
272
     * @return Bytes de la clase
273
     *
274
     * @throws IOException Si no se puede obtener el .class del jar
275
     */
276
    private byte[] loadClassData(ZipEntry classFile, InputStream is)
277
        throws IOException {
278
        // Get size of class file
279
        int size = (int) classFile.getSize();
280

    
281
        // Reserve space to read
282
        byte[] buff = new byte[size];
283

    
284
        // Get stream to read from
285
        DataInputStream dis = new DataInputStream(is);
286

    
287
        // Read in data
288
        dis.readFully(buff);
289

    
290
        // close stream
291
        dis.close();
292

    
293
        // return data
294
        return buff;
295
    }
296

    
297
    /**
298
     * Gets the bytes of a File
299
     *
300
     * @param file File
301
     *
302
     * @return bytes of file
303
     *
304
     * @throws IOException If the operation fails
305
     */
306
    private byte[] loadClassData(File file) throws IOException {
307
        InputStream is = new FileInputStream(file);
308

    
309
        // Get the size of the file
310
        long length = file.length();
311

    
312
        // You cannot create an array using a long type.
313
        // It needs to be an int type.
314
        // Before converting to an int type, check
315
        // to ensure that file is not larger than Integer.MAX_VALUE.
316
        if (length > Integer.MAX_VALUE) {
317
            // File is too large
318
        }
319

    
320
        // Create the byte array to hold the data
321
        byte[] bytes = new byte[(int) length];
322

    
323
        // Read in the bytes
324
        int offset = 0;
325
        int numRead = 0;
326

    
327
        while ((offset < bytes.length) &&
328
                ((numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)) {
329
            offset += numRead;
330
        }
331

    
332
        // Ensure all the bytes have been read in
333
        if (offset < bytes.length) {
334
            throw new IOException("Could not completely read file " +
335
                file.getName());
336
        }
337

    
338
        // Close the input stream and return bytes
339
        is.close();
340

    
341
        return bytes;
342
    }
343

    
344
    /**
345
     * Obtiene los recursos tomando como la raiz el directorio base del plugin.
346
     * Si no se encuentra el recurso ah? se invoca a getResource del
347
     * classloader padre, que buscar? en el jar de la aplicaci?n. Si ah?
348
     * tampoco se encuentra nada se devolver? null.
349
     *
350
     * @param res Nombre del recurso
351
     *
352
     * @return URL del recurso o null si no se pudo encontrar
353
     */
354
    public URL getResource(String res) {
355
        try {
356
            ArrayList resource = new ArrayList();
357
            StringTokenizer st = new StringTokenizer(res, "\\/");
358

    
359
            while (st.hasMoreTokens()) {
360
                String token = st.nextToken();
361
                resource.add(token);
362
            }
363

    
364
            URL ret = getResource(baseDir, resource);
365

    
366
            if (ret != null) {
367
                return ret;
368
            }
369
        } catch (Exception e) {
370
            e.printStackTrace();
371
        }
372

    
373
        return super.getResource(res);
374
    }
375

    
376
    /**
377
     * Busca recursivamente el recurso res en el directorio base. res es una
378
     * lista de String's con los directorios del path y base es el directorio
379
     * a partir del cual se busca dicho recurso. En cada ejecuci?n del m?todo
380
     * se toma el primer elemento de res y se busca dicho directorio en el
381
     * directorio base. Si se encuentra, ser? el directorio base para una
382
     * nueva llamada.
383
     *
384
     * @param base Directorio desde donde parte la b?squeda del recurso.
385
     * @param res Lista de strings con el path del recurso que se quiere
386
     *        encontrar
387
     *
388
     * @return URL con el recurso
389
     */
390
    private URL getResource(File base, List res) {
391
        File[] files = base.listFiles();
392

    
393
        String parte = (String) res.get(0);
394

    
395
        for (int i = 0; i < files.length; i++) {
396
            if (files[i].getName().compareTo(parte) == 0) {
397
                if (res.size() == 1) {
398
                    try {
399
                        return new URL("file:" + files[i].toString());
400
                    } catch (MalformedURLException e) {
401
                        return null;
402
                    }
403
                } else {
404
                    return getResource(files[i], res.subList(1, res.size()));
405
                }
406
            }
407
        }
408

    
409
        return null;
410
    }
411

    
412
    /**
413
     * Devuelve el nombre del directorio del plugin
414
     *
415
     * @return
416
     */
417
    public String getPluginName() {
418
        String ret = baseDir.getAbsolutePath().substring(baseDir.getAbsolutePath()
419
                                                                .lastIndexOf(File.separatorChar) +
420
                1);
421

    
422
        return ret;
423
    }
424

    
425
    /**
426
     * @see java.security.SecureClassLoader#getPermissions(java.security.CodeSource)
427
     */
428
    protected PermissionCollection getPermissions(CodeSource codesource) {
429
        PermissionCollection perms = super.getPermissions(codesource);
430
        perms.add(new AllPermission());
431

    
432
        return perms;
433
    }
434

    
435
    /**
436
     * DOCUMENT ME!
437
     *
438
     * @return Returns the baseDir.
439
     */
440
    public String getBaseDir() {
441
        return baseDir.getAbsolutePath();
442
    }
443

    
444
        public static void addLoaders(ArrayList classLoaders) {
445
                otherLoaders.addAll(classLoaders);
446
        }
447
}