Statistics
| Revision:

svn-gvsig-desktop / tags / v1_0_RELEASE / frameworks / _fwAndami / src / com / iver / andami / plugins / PluginClassLoader.java @ 9167

History | View | Annotate | Download (13.3 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

    
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
     *
101
     * @throws IOException
102
     */
103
    public PluginClassLoader(URL[] jars, String baseDir, ClassLoader cl,
104
        PluginClassLoader[] pluginLoaders) throws IOException {
105
        super(jars, cl);
106
        this.baseDir = new File(new File(baseDir).getAbsolutePath());
107
        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
                                "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
                    }
136

    
137
                    clasesJar.put(fileName, jarFiles[i]);
138
                }
139
            } catch (ZipException e) {
140
                throw new IOException(e.getMessage() + " Jar: " +
141
                    jars[i].getPath() + ": " + jarFiles[i]);
142
            } catch (IOException e) {
143
                throw e;
144
            }
145
        }
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
    protected Class singleLoadClass(String name) throws ClassNotFoundException {
158
        // Buscamos en las clases de las librer?as del plugin
159
        Class c = findLoadedClass(name);
160

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

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

    
168
            //No est? en ning?n jar
169
            if (jar == null) {
170
                //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
                            try 
181
                            {
182
                                    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
                            }
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
                            }             
194

    
195
                        if (c != null) {
196
                            break;
197
                        }
198
                    }
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
            c = super.loadClass(name, resolve);
237
        } catch (ClassNotFoundException e1) {
238
                c = singleLoadClass(name);
239
        }
240

    
241
        if (resolve) {
242
            resolveClass(c);
243
        }
244

    
245
        return c;
246
    }
247
   
248

    
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
     * 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
     * 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
            URL ret = getResource(baseDir, resource);
349

    
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
        String ret = baseDir.getAbsolutePath().substring(baseDir.getAbsolutePath()
403
                                                                .lastIndexOf(File.separatorChar) +
404
                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
        return baseDir.getAbsolutePath();
426
    }
427
}