Statistics
| Revision:

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

History | View | Annotate | Download (9.41 KB)

1
package com.iver.andami.plugins;
2

    
3
import java.io.DataInputStream;
4
import java.io.File;
5
import java.io.IOException;
6
import java.io.InputStream;
7
import java.net.MalformedURLException;
8
import java.net.URL;
9
import java.net.URLClassLoader;
10
import java.security.AllPermission;
11
import java.security.CodeSource;
12
import java.security.PermissionCollection;
13
import java.util.ArrayList;
14
import java.util.Enumeration;
15
import java.util.Hashtable;
16
import java.util.List;
17
import java.util.StringTokenizer;
18
import java.util.jar.JarException;
19
import java.util.zip.ZipEntry;
20
import java.util.zip.ZipException;
21
import java.util.zip.ZipFile;
22

    
23
import org.apache.log4j.Logger;
24

    
25
import com.iver.andami.messages.Messages;
26

    
27

    
28
/**
29
 * Class loader que carga las clases pedidas por los plugins de manera que
30
 * primero busca en el classpath, luego busca en el directorio del propio
31
 * plugin en los jars especificados por el xml y en caso de no encontrar la
32
 * clase pide al PluginClassLoaderManager la lista de plugins que pueden
33
 * satisfacer la clase e intenta cargarlo con cada un de ellos hasta que lo
34
 * consigue con uno.
35
 *
36
 * @author Fernando Gonz?lez Cort?s
37
 */
38
public class PluginClassLoader extends URLClassLoader {
39
    /** DOCUMENT ME! */
40
    private static Logger logger = Logger.getLogger(PluginClassLoader.class.getName());
41

    
42
    /** DOCUMENT ME! */
43
    private Hashtable clasesJar = new Hashtable();
44

    
45
    /** DOCUMENT ME! */
46
    private File baseDir;
47
    private PluginClassLoader[] pluginLoaders;
48

    
49
    /**
50
     * Creates a new PluginClassLoader object.
51
     *
52
     * @param jars Array con la ruta de los jars en los que buscar? las clases
53
     *        el plugin
54
     * @param baseDir Directorio base del plugin que se carga. Es en directorio
55
     *        donde se buscan los resources en el m?todo getResources
56
     * @param cl ClassLoader padre del classLoader, al que se le pedir?
57
     *        resolver las clases antes de utilizar el algoritmo propio
58
     * @param pluginLoaders DOCUMENT ME!
59
     * @throws IOException
60
     * @throws IOException
61
     */
62
    public PluginClassLoader(URL[] jars, String baseDir, ClassLoader cl,
63
        PluginClassLoader[] pluginLoaders) throws IOException {
64
        super(jars, cl);
65
        this.baseDir = new File(new File(baseDir).getAbsolutePath());
66
        this.pluginLoaders = pluginLoaders;
67

    
68
        ZipFile[] jarFiles = new ZipFile[jars.length];
69

    
70
        for (int i = 0; i < jars.length; i++) {
71
            try {
72
                jarFiles[i] = new ZipFile(jars[i].getPath());
73

    
74
                Enumeration entradas = jarFiles[i].entries();
75

    
76
                while (entradas.hasMoreElements()) {
77
                    ZipEntry file = (ZipEntry) entradas.nextElement();
78
                    String fileName = file.getName();
79

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

    
82
                        continue;
83
                    }
84

    
85
                    fileName = fileName.substring(0, fileName.length() - 6)
86
                                       .replace('/', '.');
87

    
88
                    if (clasesJar.get(fileName) != null) {
89
                        throw new JarException(Messages.getString(
90
                                "PluginClassLoader.Dos_clases_con_el_mismo_nombre_en_el_plugin" +
91
                                ": " + fileName));
92
                    }
93

    
94
                    clasesJar.put(fileName, jarFiles[i]);
95
                }
96
            } catch (ZipException e) {
97
                    throw new IOException(e.getMessage() + jars[i].getPath()+": (" +i+")"+ jarFiles[i]);                    
98
            } catch (IOException e) {
99
                throw e;
100
                        }
101
        }
102
    }
103

    
104
    /**
105
     * DOCUMENT ME!
106
     *
107
     * @param name DOCUMENT ME!
108
     * @param resolve DOCUMENT ME!
109
     *
110
     * @return DOCUMENT ME!
111
     *
112
     * @throws ClassNotFoundException DOCUMENT ME!
113
     */
114
    protected Class singleLoadClass(String name)
115
        throws ClassNotFoundException {
116
        // Buscamos en las clases de las librer?as del plugin
117
        Class c = findLoadedClass(name);
118
            if (c != null){
119
                    return c;
120
            }
121

    
122
        try {
123
            ZipFile jar = (ZipFile) clasesJar.get(name);
124

    
125
            if (jar == null) {
126
                for (int i = 0; i < pluginLoaders.length; i++) {
127
                    c = pluginLoaders[i].singleLoadClass(name);
128

    
129
                    if (c != null) {
130
                        break;
131
                    }
132
                }
133
            } else {
134
                String fileName = name.replace('.', '/') + ".class";
135
                ZipEntry classFile = jar.getEntry(fileName);
136
                byte[] data = loadClassData(classFile,
137
                        jar.getInputStream(classFile));
138

    
139
                c = defineClass(name, data, 0, data.length);
140
            }
141

    
142
            if (c == null) {
143
                throw new ClassNotFoundException(name);
144
            }
145

    
146
            return c;
147
        } catch (IOException e) {
148
            throw new ClassNotFoundException(Messages.getString(
149
                    "PluginClassLoader.Error_reading_file") + name);
150
        }
151
    }
152

    
153
    /**
154
     * Carga la clase
155
     *
156
     * @param name Nombre de la clase
157
     * @param resolve Si se ha de resolver la clase o no
158
     *
159
     * @return Clase cargada
160
     *
161
     * @throws ClassNotFoundException Si no se pudo encontrar la clase
162
     */
163
    protected Class loadClass(String name, boolean resolve)
164
        throws ClassNotFoundException {
165
        Class c = null;
166

    
167
        // Intentamos cargar con el system classloader
168
        try {
169
            c = super.loadClass(name, resolve);
170
        } catch (ClassNotFoundException e1) {
171
            c = singleLoadClass(name);
172
        }
173

    
174
        if (resolve) {
175
            resolveClass(c);
176
        }
177

    
178
        return c;
179
    }
180

    
181
    /**
182
     * obtiene el array de bytes de la clase
183
     *
184
     * @param classFile Entrada dentro del jar contiene los bytecodes de la
185
     *        clase (el .class)
186
     * @param is InputStream para leer la entrada del jar
187
     *
188
     * @return Bytes de la clase
189
     *
190
     * @throws IOException Si no se puede obtener el .class del jar
191
     */
192
    private byte[] loadClassData(ZipEntry classFile, InputStream is)
193
        throws IOException {
194
        // Get size of class file
195
        int size = (int) classFile.getSize();
196

    
197
        // Reserve space to read
198
        byte[] buff = new byte[size];
199

    
200
        // Get stream to read from
201
        DataInputStream dis = new DataInputStream(is);
202

    
203
        // Read in data
204
        dis.readFully(buff);
205

    
206
        // close stream
207
        dis.close();
208

    
209
        // return data
210
        return buff;
211
    }
212

    
213
    /**
214
     * Obtiene los recursos tomando como la raiz el directorio base del plugin.
215
     * Si no se encuentra el recurso ah? se invoca a getResource del
216
     * classloader padre, que buscar? en el jar de la aplicaci?n. Si ah?
217
     * tampoco se encuentra nada se devolver? null.
218
     *
219
     * @param res Nombre del recurso
220
     *
221
     * @return URL del recurso o null si no se pudo encontrar
222
     */
223
    public URL getResource(String res) {
224
        try {
225
            ArrayList resource = new ArrayList();
226
            StringTokenizer st = new StringTokenizer(res, "\\/");
227

    
228
            while (st.hasMoreTokens()) {
229
                String token = st.nextToken();
230
                resource.add(token);
231
            }
232

    
233
            URL ret = getResource(baseDir, resource);
234

    
235
            if (ret != null) {
236
                return ret;
237
            }
238
        } catch (Exception e) {
239
            e.printStackTrace();
240
        }
241

    
242
        return super.getResource(res);
243
    }
244

    
245
    /**
246
     * Busca recursivamente el recurso res en el directorio base. res es una
247
     * lista de String's con los directorios del path y base es el directorio
248
     * a partir del cual se busca dicho recurso. En cada ejecuci?n del m?todo
249
     * se toma el primer elemento de res y se busca dicho directorio en el
250
     * directorio base. Si se encuentra, ser? el directorio base para una
251
     * nueva llamada.
252
     *
253
     * @param base Directorio desde donde parte la b?squeda del recurso.
254
     * @param res Lista de strings con el path del recurso que se quiere
255
     *        encontrar
256
     *
257
     * @return URL con el recurso
258
     */
259
    private URL getResource(File base, List res) {
260
        File[] files = base.listFiles();
261

    
262
        String parte = (String) res.get(0);
263

    
264
        for (int i = 0; i < files.length; i++) {
265
            if (files[i].getName().compareTo(parte) == 0) {
266
                if (res.size() == 1) {
267
                    try {
268
                        return new URL("file:" + files[i].toString());
269
                    } catch (MalformedURLException e) {
270
                        return null;
271
                    }
272
                } else {
273
                    return getResource(files[i], res.subList(1, res.size()));
274
                }
275
            }
276
        }
277

    
278
        return null;
279
    }
280

    
281
    /**
282
     * Devuelve el nombre del directorio del plugin
283
     *
284
     * @return
285
     */
286
    public String getPluginName() {
287
        String ret = baseDir.getAbsolutePath().substring(baseDir.getAbsolutePath().lastIndexOf(File.separatorChar) +
288
                1);
289

    
290
        return ret;
291
    }
292

    
293
    /**
294
     * @see java.security.SecureClassLoader#getPermissions(java.security.CodeSource)
295
     */
296
    protected PermissionCollection getPermissions(CodeSource codesource) {
297
        PermissionCollection perms = super.getPermissions(codesource);
298
        perms.add(new AllPermission());
299

    
300
        return perms;
301
    }
302

    
303
    /**
304
     * DOCUMENT ME!
305
     *
306
     * @return Returns the baseDir.
307
     */
308
    public String getBaseDir() {
309
        return baseDir.getAbsolutePath();
310
    }
311
}