Statistics
| Revision:

svn-gvsig-desktop / trunk / frameworks / _fwAndami / src / com / iver / andami / plugins / PluginClassLoader.java @ 2291

History | View | Annotate | Download (12.7 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 com.iver.andami.messages.Messages;
44

    
45
import org.apache.log4j.Logger;
46

    
47
import java.io.DataInputStream;
48
import java.io.File;
49
import java.io.FileInputStream;
50
import java.io.FileNotFoundException;
51
import java.io.IOException;
52
import java.io.InputStream;
53

    
54
import java.net.MalformedURLException;
55
import java.net.URL;
56
import java.net.URLClassLoader;
57

    
58
import java.security.AllPermission;
59
import java.security.CodeSource;
60
import java.security.PermissionCollection;
61

    
62
import java.util.ArrayList;
63
import java.util.Enumeration;
64
import java.util.Hashtable;
65
import java.util.List;
66
import java.util.StringTokenizer;
67
import java.util.jar.JarException;
68
import java.util.zip.ZipEntry;
69
import java.util.zip.ZipException;
70
import java.util.zip.ZipFile;
71

    
72

    
73
/**
74
 * Class loader que carga las clases pedidas por los plugins de manera que
75
 * primero busca en el classpath, luego busca en el directorio del propio
76
 * plugin en los jars especificados por el xml y en caso de no encontrar la
77
 * clase pide al PluginClassLoaderManager la lista de plugins que pueden
78
 * satisfacer la clase e intenta cargarlo con cada un de ellos hasta que lo
79
 * consigue con uno.
80
 *
81
 * @author Fernando Gonz?lez Cort?s
82
 */
83
public class PluginClassLoader extends URLClassLoader {
84
    /** DOCUMENT ME! */
85
    private static Logger logger = Logger.getLogger(PluginClassLoader.class.getName());
86

    
87
    /** DOCUMENT ME! */
88
    private Hashtable clasesJar = new Hashtable();
89

    
90
    /** DOCUMENT ME! */
91
    private File baseDir;
92
    private PluginClassLoader[] pluginLoaders;
93

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

    
113
        ZipFile[] jarFiles = new ZipFile[jars.length];
114

    
115
        for (int i = 0; i < jars.length; i++) {
116
            try {
117
                jarFiles[i] = new ZipFile(jars[i].getPath());
118

    
119
                Enumeration entradas = jarFiles[i].entries();
120

    
121
                while (entradas.hasMoreElements()) {
122
                    ZipEntry file = (ZipEntry) entradas.nextElement();
123
                    String fileName = file.getName();
124

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

    
127
                        continue;
128
                    }
129

    
130
                    fileName = fileName.substring(0, fileName.length() - 6)
131
                                       .replace('/', '.');
132

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

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

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

    
165
        if (c != null) {
166
            return c;
167
        }
168

    
169
        try {
170
            ZipFile jar = (ZipFile) clasesJar.get(name);
171

    
172
            //No est? en ning?n jar
173
            if (jar == null) {
174
                //Buscamos en el directorio de clases
175
                String classFileName = baseDir + "/classes/" +
176
                    name.replace('.', '/') + ".class";
177
                File f = new File(classFileName);
178
                if (f.exists()){
179
                    byte[] data = loadClassData(f);
180
                    c = defineClass(name, data, 0, data.length);
181
                }else{
182
                    //Buscamos en los otros plugins
183
                    for (int i = 0; i < pluginLoaders.length; i++) {
184
                        c = pluginLoaders[i].singleLoadClass(name);
185

    
186
                        if (c != null) {
187
                            break;
188
                        }
189
                    }
190
                }
191
            } else {
192
                String fileName = name.replace('.', '/') + ".class";
193
                ZipEntry classFile = jar.getEntry(fileName);
194
                byte[] data = loadClassData(classFile,
195
                        jar.getInputStream(classFile));
196

    
197
                c = defineClass(name, data, 0, data.length);
198
            }
199

    
200
            if (c == null) {
201
                throw new ClassNotFoundException(name);
202
            }
203

    
204
            return c;
205
        } catch (IOException e) {
206
            throw new ClassNotFoundException(Messages.getString(
207
                    "PluginClassLoader.Error_reading_file") + name);
208
        }
209
    }
210

    
211
    /**
212
     * Carga la clase
213
     *
214
     * @param name Nombre de la clase
215
     * @param resolve Si se ha de resolver la clase o no
216
     *
217
     * @return Clase cargada
218
     *
219
     * @throws ClassNotFoundException Si no se pudo encontrar la clase
220
     */
221
    protected Class loadClass(String name, boolean resolve)
222
        throws ClassNotFoundException {
223
        Class c = null;
224

    
225
        // Intentamos cargar con el system classloader
226
        try {
227
            c = super.loadClass(name, resolve);
228
        } catch (ClassNotFoundException e1) {
229
            c = singleLoadClass(name);
230
        }
231

    
232
        if (resolve) {
233
            resolveClass(c);
234
        }
235

    
236
        return c;
237
    }
238

    
239
    /**
240
     * obtiene el array de bytes de la clase
241
     *
242
     * @param classFile Entrada dentro del jar contiene los bytecodes de la
243
     *        clase (el .class)
244
     * @param is InputStream para leer la entrada del jar
245
     *
246
     * @return Bytes de la clase
247
     *
248
     * @throws IOException Si no se puede obtener el .class del jar
249
     */
250
    private byte[] loadClassData(ZipEntry classFile, InputStream is)
251
        throws IOException {
252
        // Get size of class file
253
        int size = (int) classFile.getSize();
254

    
255
        // Reserve space to read
256
        byte[] buff = new byte[size];
257

    
258
        // Get stream to read from
259
        DataInputStream dis = new DataInputStream(is);
260

    
261
        // Read in data
262
        dis.readFully(buff);
263

    
264
        // close stream
265
        dis.close();
266

    
267
        // return data
268
        return buff;
269
    }
270

    
271
    /**
272
     * Gets the bytes of a File
273
     *
274
     * @param file File
275
     *
276
     * @return bytes of file
277
     *
278
     * @throws IOException If the operation fails
279
     */
280
    private byte[] loadClassData(File file) throws IOException {
281
        InputStream is = new FileInputStream(file);
282

    
283
        // Get the size of the file
284
        long length = file.length();
285

    
286
        // You cannot create an array using a long type.
287
        // It needs to be an int type.
288
        // Before converting to an int type, check
289
        // to ensure that file is not larger than Integer.MAX_VALUE.
290
        if (length > Integer.MAX_VALUE) {
291
            // File is too large
292
        }
293

    
294
        // Create the byte array to hold the data
295
        byte[] bytes = new byte[(int) length];
296

    
297
        // Read in the bytes
298
        int offset = 0;
299
        int numRead = 0;
300

    
301
        while ((offset < bytes.length) &&
302
                ((numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)) {
303
            offset += numRead;
304
        }
305

    
306
        // Ensure all the bytes have been read in
307
        if (offset < bytes.length) {
308
            throw new IOException("Could not completely read file " +
309
                file.getName());
310
        }
311

    
312
        // Close the input stream and return bytes
313
        is.close();
314

    
315
        return bytes;
316
    }
317

    
318
    /**
319
     * Obtiene los recursos tomando como la raiz el directorio base del plugin.
320
     * Si no se encuentra el recurso ah? se invoca a getResource del
321
     * classloader padre, que buscar? en el jar de la aplicaci?n. Si ah?
322
     * tampoco se encuentra nada se devolver? null.
323
     *
324
     * @param res Nombre del recurso
325
     *
326
     * @return URL del recurso o null si no se pudo encontrar
327
     */
328
    public URL getResource(String res) {
329
        try {
330
            ArrayList resource = new ArrayList();
331
            StringTokenizer st = new StringTokenizer(res, "\\/");
332

    
333
            while (st.hasMoreTokens()) {
334
                String token = st.nextToken();
335
                resource.add(token);
336
            }
337

    
338
            URL ret = getResource(baseDir, resource);
339

    
340
            if (ret != null) {
341
                return ret;
342
            }
343
        } catch (Exception e) {
344
            e.printStackTrace();
345
        }
346

    
347
        return super.getResource(res);
348
    }
349

    
350
    /**
351
     * Busca recursivamente el recurso res en el directorio base. res es una
352
     * lista de String's con los directorios del path y base es el directorio
353
     * a partir del cual se busca dicho recurso. En cada ejecuci?n del m?todo
354
     * se toma el primer elemento de res y se busca dicho directorio en el
355
     * directorio base. Si se encuentra, ser? el directorio base para una
356
     * nueva llamada.
357
     *
358
     * @param base Directorio desde donde parte la b?squeda del recurso.
359
     * @param res Lista de strings con el path del recurso que se quiere
360
     *        encontrar
361
     *
362
     * @return URL con el recurso
363
     */
364
    private URL getResource(File base, List res) {
365
        File[] files = base.listFiles();
366

    
367
        String parte = (String) res.get(0);
368

    
369
        for (int i = 0; i < files.length; i++) {
370
            if (files[i].getName().compareTo(parte) == 0) {
371
                if (res.size() == 1) {
372
                    try {
373
                        return new URL("file:" + files[i].toString());
374
                    } catch (MalformedURLException e) {
375
                        return null;
376
                    }
377
                } else {
378
                    return getResource(files[i], res.subList(1, res.size()));
379
                }
380
            }
381
        }
382

    
383
        return null;
384
    }
385

    
386
    /**
387
     * Devuelve el nombre del directorio del plugin
388
     *
389
     * @return
390
     */
391
    public String getPluginName() {
392
        String ret = baseDir.getAbsolutePath().substring(baseDir.getAbsolutePath()
393
                                                                .lastIndexOf(File.separatorChar) +
394
                1);
395

    
396
        return ret;
397
    }
398

    
399
    /**
400
     * @see java.security.SecureClassLoader#getPermissions(java.security.CodeSource)
401
     */
402
    protected PermissionCollection getPermissions(CodeSource codesource) {
403
        PermissionCollection perms = super.getPermissions(codesource);
404
        perms.add(new AllPermission());
405

    
406
        return perms;
407
    }
408

    
409
    /**
410
     * DOCUMENT ME!
411
     *
412
     * @return Returns the baseDir.
413
     */
414
    public String getBaseDir() {
415
        return baseDir.getAbsolutePath();
416
    }
417
}