svn-gvsig-desktop / branches / org.gvsig.desktop-2018a / org.gvsig.desktop.framework / org.gvsig.andami / src / main / java / org / gvsig / andami / plugins / PluginClassLoader.java @ 43888
History | View | Annotate | Download (23.3 KB)
1 | 40559 | jjdelcerro | /**
|
---|---|---|---|
2 | * gvSIG. Desktop Geographic Information System.
|
||
3 | 40435 | jjdelcerro | *
|
4 | 40559 | jjdelcerro | * Copyright (C) 2007-2013 gvSIG Association.
|
5 | 40435 | jjdelcerro | *
|
6 | 42437 | jjdelcerro | * This program is free software; you can redistribute it and/or modify it under
|
7 | * the terms of the GNU General Public License as published by the Free Software
|
||
8 | * Foundation; either version 3 of the License, or (at your option) any later
|
||
9 | * version.
|
||
10 | 40435 | jjdelcerro | *
|
11 | 42437 | jjdelcerro | * This program is distributed in the hope that it will be useful, but WITHOUT
|
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||
13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||
14 | * details.
|
||
15 | 40435 | jjdelcerro | *
|
16 | 42437 | jjdelcerro | * You should have received a copy of the GNU General Public License along with
|
17 | * this program; if not, write to the Free Software Foundation, Inc., 51
|
||
18 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
19 | 40435 | jjdelcerro | *
|
20 | 42437 | jjdelcerro | * For any additional information, do not hesitate to contact us at info AT
|
21 | * gvsig.com, or visit our website www.gvsig.com.
|
||
22 | 40435 | jjdelcerro | */
|
23 | package org.gvsig.andami.plugins; |
||
24 | |||
25 | import java.io.DataInputStream; |
||
26 | import java.io.File; |
||
27 | import java.io.FileInputStream; |
||
28 | import java.io.IOException; |
||
29 | import java.io.InputStream; |
||
30 | import java.net.MalformedURLException; |
||
31 | import java.net.URL; |
||
32 | import java.net.URLClassLoader; |
||
33 | import java.security.AllPermission; |
||
34 | import java.security.CodeSource; |
||
35 | import java.security.PermissionCollection; |
||
36 | import java.util.ArrayList; |
||
37 | 41231 | jjdelcerro | import java.util.Arrays; |
38 | 42774 | jbadia | import java.util.Collections; |
39 | 40435 | jjdelcerro | import java.util.Enumeration; |
40 | 42559 | jjdelcerro | import java.util.HashMap; |
41 | 41770 | fdiaz | import java.util.HashSet; |
42 | 40435 | jjdelcerro | import java.util.Hashtable; |
43 | 40566 | jjdelcerro | import java.util.Iterator; |
44 | 40435 | jjdelcerro | import java.util.List; |
45 | 40566 | jjdelcerro | import java.util.Map; |
46 | import java.util.Map.Entry; |
||
47 | 41770 | fdiaz | import java.util.Set; |
48 | 40435 | jjdelcerro | import java.util.StringTokenizer; |
49 | import java.util.zip.ZipEntry; |
||
50 | import java.util.zip.ZipException; |
||
51 | import java.util.zip.ZipFile; |
||
52 | 43888 | jjdelcerro | import org.apache.commons.lang3.StringUtils; |
53 | 40435 | jjdelcerro | |
54 | 40566 | jjdelcerro | import org.gvsig.andami.messages.Messages; |
55 | 40435 | jjdelcerro | import org.slf4j.Logger; |
56 | import org.slf4j.LoggerFactory; |
||
57 | |||
58 | /**
|
||
59 | 42437 | jjdelcerro | * <p>
|
60 | * Class loader which loads the classes requested by the plugins. It first tries
|
||
61 | * to search in the classpath, then it requests the class to the parent
|
||
62 | * classloader, then it searches in the owns plugins' library dir, and if all
|
||
63 | * these methods fail, it tries to load the class from any of the depended
|
||
64 | * plugins. Finally, if this also fails, the other classloaders provided in the
|
||
65 | * <code>addLoaders</code> method are requested to load the class.</p>
|
||
66 | 40435 | jjdelcerro | *
|
67 | 42437 | jjdelcerro | * <p>
|
68 | * The class loader can also be used to load resources from the plugin's
|
||
69 | * directory by using the <code>getResource()</code> method.</p>
|
||
70 | *
|
||
71 | 40435 | jjdelcerro | * @author Fernando Gonz�lez Cort�s
|
72 | */
|
||
73 | public class PluginClassLoader extends URLClassLoader { |
||
74 | 42437 | jjdelcerro | |
75 | /**
|
||
76 | * DOCUMENT ME!
|
||
77 | */
|
||
78 | 40599 | jjdelcerro | private static Logger logger = LoggerFactory.getLogger(PluginClassLoader.class.getName()); |
79 | 40435 | jjdelcerro | |
80 | |||
81 | 42559 | jjdelcerro | private Map<String, ZipFile> clasesJar = new HashMap<>(); |
82 | |||
83 | |||
84 | 40435 | jjdelcerro | private File baseDir; |
85 | 41231 | jjdelcerro | private List<PluginClassLoader> pluginLoaders; |
86 | 42559 | jjdelcerro | private static List<ClassLoader> otherLoaders = new ArrayList<>(); |
87 | 42437 | jjdelcerro | private boolean isOtherLoader = false; |
88 | |||
89 | 40435 | jjdelcerro | /**
|
90 | * Creates a new PluginClassLoader object.
|
||
91 | *
|
||
92 | * @param jars Array with the search paths where classes will be searched
|
||
93 | * @param baseDir Base directory for this plugin. This is the directory
|
||
94 | * which will be used as basedir in the <code>getResources</code> method.
|
||
95 | * @param cl The parent classloader of this classloader. It will be used to
|
||
96 | * search classes before trying to search in the plugin's directory
|
||
97 | * @param pluginLoaders The classloaders of the depended plugins.
|
||
98 | *
|
||
99 | * @throws IOException
|
||
100 | */
|
||
101 | public PluginClassLoader(URL[] jars, String baseDir, ClassLoader cl, |
||
102 | 42437 | jjdelcerro | PluginClassLoader[] pluginLoaders) throws IOException { |
103 | 41231 | jjdelcerro | this(jars, baseDir, cl, Arrays.asList(pluginLoaders)); |
104 | 41025 | jjdelcerro | } |
105 | 42437 | jjdelcerro | |
106 | 41025 | jjdelcerro | public PluginClassLoader(URL[] jars, String baseDir, ClassLoader cl, |
107 | 41231 | jjdelcerro | List<PluginClassLoader> pluginLoaders) throws IOException { |
108 | 40435 | jjdelcerro | super(jars, cl);
|
109 | this.baseDir = new File(new File(baseDir).getAbsolutePath()); |
||
110 | 42559 | jjdelcerro | try {
|
111 | logger.debug("Creating PluginClassLoader for {}.", this.getPluginName()); |
||
112 | this.pluginLoaders = new ArrayList(); |
||
113 | this.pluginLoaders.addAll(pluginLoaders);
|
||
114 | 40435 | jjdelcerro | |
115 | 42559 | jjdelcerro | if (jars == null || jars.length < 1) { |
116 | debugDump(); |
||
117 | return;
|
||
118 | } |
||
119 | ZipFile[] jarFiles = new ZipFile[jars.length]; |
||
120 | 40435 | jjdelcerro | |
121 | 42559 | jjdelcerro | for (int i = 0; i < jars.length; i++) { |
122 | try {
|
||
123 | logger.debug("Classloader {}, loading jar {}", this.getPluginName(), jars[i].getPath()); |
||
124 | jarFiles[i] = new ZipFile(jars[i].getPath()); |
||
125 | 40435 | jjdelcerro | |
126 | 42559 | jjdelcerro | Enumeration<? extends ZipEntry> entradas = jarFiles[i].entries(); |
127 | 40435 | jjdelcerro | |
128 | 42559 | jjdelcerro | while (entradas.hasMoreElements()) {
|
129 | ZipEntry file = (ZipEntry) entradas.nextElement(); |
||
130 | String fileName = file.getName();
|
||
131 | 40435 | jjdelcerro | |
132 | 42559 | jjdelcerro | if (!fileName.toLowerCase().endsWith(".class")) { //$NON-NLS-1$ |
133 | 40435 | jjdelcerro | |
134 | 42559 | jjdelcerro | continue;
|
135 | } |
||
136 | 40435 | jjdelcerro | |
137 | 42559 | jjdelcerro | fileName = fileName.substring(0, fileName.length() - 6) |
138 | .replace('/', '.'); |
||
139 | 40435 | jjdelcerro | |
140 | 42559 | jjdelcerro | if (clasesJar.get(fileName) != null) { |
141 | logger.warn("Classloader {}, duplicated class {} in {} and {}",
|
||
142 | new Object[]{ |
||
143 | this.getPluginName(),
|
||
144 | fileName, |
||
145 | jarFiles[i].getName(), |
||
146 | clasesJar.get(fileName).getName() |
||
147 | } |
||
148 | ); |
||
149 | } else {
|
||
150 | clasesJar.put(fileName, jarFiles[i]); |
||
151 | } |
||
152 | 40435 | jjdelcerro | } |
153 | 42559 | jjdelcerro | } catch (ZipException e) { |
154 | throw new IOException(e.getMessage() + " Jar: " |
||
155 | + jars[i].getPath() + ": " + jarFiles[i]);
|
||
156 | } catch (IOException e) { |
||
157 | throw e;
|
||
158 | 40435 | jjdelcerro | } |
159 | } |
||
160 | 42559 | jjdelcerro | } finally {
|
161 | debugDump(); |
||
162 | 40435 | jjdelcerro | } |
163 | } |
||
164 | |||
165 | 40566 | jjdelcerro | private void debugDump() { |
166 | 42437 | jjdelcerro | if (!logger.isDebugEnabled()) {
|
167 | return;
|
||
168 | } |
||
169 | logger.debug(this.toString());
|
||
170 | logger.debug(" baseDir: " + this.baseDir); |
||
171 | logger.debug(" parent: " + this.getParent()); |
||
172 | logger.debug(" depends:");
|
||
173 | 42559 | jjdelcerro | for (PluginClassLoader pluginLoader : this.pluginLoaders) { |
174 | logger.debug(" {}", pluginLoader.toString());
|
||
175 | 42437 | jjdelcerro | } |
176 | logger.debug(" urls:");
|
||
177 | URL[] urls = this.getURLs(); |
||
178 | 42559 | jjdelcerro | for (URL url : urls) { |
179 | logger.debug(" " + url.toString());
|
||
180 | 42437 | jjdelcerro | } |
181 | 42559 | jjdelcerro | // logger.debug(" classes:");
|
182 | // Iterator<Map.Entry<String, ZipFile>> it = this.clasesJar.entrySet().iterator();
|
||
183 | // while (it.hasNext()) {
|
||
184 | // Entry<String, ZipFile> entry = it.next();
|
||
185 | // logger.debug(" " + entry.getKey() + "(" + entry.getValue().getName() + ")");
|
||
186 | // }
|
||
187 | 40566 | jjdelcerro | } |
188 | 42437 | jjdelcerro | |
189 | 40435 | jjdelcerro | protected Class singleLoadClass(String name) throws ClassNotFoundException { |
190 | 41788 | jjdelcerro | Class<?> c;
|
191 | 42559 | jjdelcerro | Set<String> pluginsVisiteds = new HashSet<>(); |
192 | 41788 | jjdelcerro | c = this.singleLoadClass(pluginsVisiteds, name);
|
193 | return c;
|
||
194 | 40435 | jjdelcerro | } |
195 | |||
196 | /**
|
||
197 | * Carga la clase
|
||
198 | *
|
||
199 | * @param name Nombre de la clase
|
||
200 | * @param resolve Si se ha de resolver la clase o no
|
||
201 | *
|
||
202 | * @return Clase cargada
|
||
203 | *
|
||
204 | * @throws ClassNotFoundException Si no se pudo encontrar la clase
|
||
205 | */
|
||
206 | 42559 | jjdelcerro | @Override
|
207 | 40435 | jjdelcerro | protected Class loadClass(String name, boolean resolve) |
208 | 42437 | jjdelcerro | throws ClassNotFoundException { |
209 | 40566 | jjdelcerro | Class<?> c = null; |
210 | 40435 | jjdelcerro | |
211 | 42559 | jjdelcerro | // Intentamos cargar con el URLClassLoader
|
212 | 43888 | jjdelcerro | for( int retry=0; c==null && retry<3; retry++) { |
213 | try {
|
||
214 | if (!isOtherLoader) {
|
||
215 | c = super.loadClass(name, resolve);
|
||
216 | logger.debug("Classloader {}, found class {}.", this.getPluginName(), name); |
||
217 | } |
||
218 | } catch (ClassNotFoundException e1) { |
||
219 | 42437 | jjdelcerro | } |
220 | 43888 | jjdelcerro | if (c == null) { |
221 | 42437 | jjdelcerro | try {
|
222 | 43888 | jjdelcerro | c = singleLoadClass(name); |
223 | } catch (ClassNotFoundException e2) { |
||
224 | } |
||
225 | } |
||
226 | if (c == null) { |
||
227 | try {
|
||
228 | 42437 | jjdelcerro | isOtherLoader = true;
|
229 | c = loadOtherClass(name); |
||
230 | } catch (ClassNotFoundException e3) { |
||
231 | 40435 | jjdelcerro | throw new ClassNotFoundException("Class " + name |
232 | 42437 | jjdelcerro | + " not found through the plugin " + baseDir, e3);
|
233 | } finally {
|
||
234 | isOtherLoader = false;
|
||
235 | } |
||
236 | } |
||
237 | 43888 | jjdelcerro | if( c==null ) { |
238 | logger.info("class "+name+", retry "+retry); |
||
239 | } |
||
240 | 40435 | jjdelcerro | } |
241 | 42437 | jjdelcerro | if (c == null) { |
242 | 43888 | jjdelcerro | if( StringUtils.startsWith(name,"java.") || StringUtils.startsWith(name,"javax.") ) { |
243 | logger.warn("Can't locate class '"+name+"'. It can be a serious problem. It is advisable that you close the application."); |
||
244 | } |
||
245 | 42437 | jjdelcerro | throw new ClassNotFoundException(Messages.getString( |
246 | "PluginClassLoader.Error_reading_file") + name);
|
||
247 | } |
||
248 | 40435 | jjdelcerro | if (resolve) {
|
249 | resolveClass(c); |
||
250 | } |
||
251 | return c;
|
||
252 | } |
||
253 | 42437 | jjdelcerro | |
254 | 40566 | jjdelcerro | private Class<?> loadOtherClass(String name) |
255 | 41231 | jjdelcerro | throws ClassNotFoundException { |
256 | 42559 | jjdelcerro | ClassLoader[] ocls = (ClassLoader[]) otherLoaders.toArray(new ClassLoader[0]); |
257 | Class<?> c;
|
||
258 | for (ClassLoader ocl : ocls) { |
||
259 | c = ocl.loadClass(name); |
||
260 | 41231 | jjdelcerro | if (c != null) { |
261 | 42559 | jjdelcerro | logger.debug("Classloader {}, found class {} in classloader {}",
|
262 | new Object[]{this, getPluginName(), name, ocl.toString()} |
||
263 | ); |
||
264 | 41231 | jjdelcerro | return c;
|
265 | } |
||
266 | } |
||
267 | throw new ClassNotFoundException(name); |
||
268 | 40435 | jjdelcerro | } |
269 | |||
270 | /**
|
||
271 | * obtiene el array de bytes de la clase
|
||
272 | *
|
||
273 | * @param classFile Entrada dentro del jar contiene los bytecodes de la
|
||
274 | 42437 | jjdelcerro | * clase (el .class)
|
275 | 40435 | jjdelcerro | * @param is InputStream para leer la entrada del jar
|
276 | *
|
||
277 | * @return Bytes de la clase
|
||
278 | *
|
||
279 | * @throws IOException Si no se puede obtener el .class del jar
|
||
280 | */
|
||
281 | private byte[] loadClassData(ZipEntry classFile, InputStream is) |
||
282 | 42437 | jjdelcerro | throws IOException { |
283 | 40435 | jjdelcerro | // Get size of class file
|
284 | int size = (int) classFile.getSize(); |
||
285 | |||
286 | // Reserve space to read
|
||
287 | byte[] buff = new byte[size]; |
||
288 | |||
289 | // Get stream to read from
|
||
290 | DataInputStream dis = new DataInputStream(is); |
||
291 | |||
292 | // Read in data
|
||
293 | dis.readFully(buff); |
||
294 | |||
295 | // close stream
|
||
296 | dis.close(); |
||
297 | |||
298 | // return data
|
||
299 | return buff;
|
||
300 | } |
||
301 | |||
302 | /**
|
||
303 | * Gets the bytes of a File
|
||
304 | *
|
||
305 | * @param file File
|
||
306 | *
|
||
307 | * @return bytes of file
|
||
308 | *
|
||
309 | * @throws IOException If the operation fails
|
||
310 | */
|
||
311 | private byte[] loadClassData(File file) throws IOException { |
||
312 | InputStream is = new FileInputStream(file); |
||
313 | |||
314 | // Get the size of the file
|
||
315 | long length = file.length();
|
||
316 | |||
317 | // You cannot create an array using a long type.
|
||
318 | // It needs to be an int type.
|
||
319 | // Before converting to an int type, check
|
||
320 | // to ensure that file is not larger than Integer.MAX_VALUE.
|
||
321 | if (length > Integer.MAX_VALUE) { |
||
322 | // File is too large
|
||
323 | } |
||
324 | |||
325 | // Create the byte array to hold the data
|
||
326 | byte[] bytes = new byte[(int) length]; |
||
327 | |||
328 | // Read in the bytes
|
||
329 | int offset = 0; |
||
330 | int numRead = 0; |
||
331 | |||
332 | 42437 | jjdelcerro | while ((offset < bytes.length)
|
333 | && ((numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)) {
|
||
334 | 40435 | jjdelcerro | offset += numRead; |
335 | } |
||
336 | |||
337 | 40566 | jjdelcerro | // Close the input stream and return bytes
|
338 | is.close(); |
||
339 | |||
340 | 40435 | jjdelcerro | // Ensure all the bytes have been read in
|
341 | if (offset < bytes.length) {
|
||
342 | 42437 | jjdelcerro | throw new IOException("Could not completely read file " |
343 | + file.getName()); |
||
344 | 40435 | jjdelcerro | } |
345 | |||
346 | return bytes;
|
||
347 | } |
||
348 | |||
349 | /**
|
||
350 | * Gets the requested resource. If the path is relative, its base directory
|
||
351 | 42437 | jjdelcerro | * will be the one provided in the PluginClassLoader's constructor. If the
|
352 | * resource is not found, search in dependents plugins, otherwise the parent
|
||
353 | * classloader will be invoked to try to get it. If it is not found, it will
|
||
354 | * return null.
|
||
355 | 40435 | jjdelcerro | *
|
356 | * @param res An absolute or relative path to the requested resource.
|
||
357 | *
|
||
358 | * @return Resource's URL if it was found, nul otherwise.
|
||
359 | */
|
||
360 | 42559 | jjdelcerro | @Override
|
361 | 40435 | jjdelcerro | public URL getResource(String res) { |
362 | 42559 | jjdelcerro | URL ret ;
|
363 | 41770 | fdiaz | |
364 | 42559 | jjdelcerro | Set<String> pluginsVisiteds = new HashSet<>(); |
365 | 41788 | jjdelcerro | ret = this.getResource(pluginsVisiteds, res);
|
366 | 40566 | jjdelcerro | return ret;
|
367 | 40435 | jjdelcerro | } |
368 | |||
369 | /**
|
||
370 | * Gets the requested resource. If the path is relative, its base directory
|
||
371 | 42437 | jjdelcerro | * will be the one provided in the PluginClassLoader's constructor. If the
|
372 | * resource is not found, the parent classloader will be invoked to try to
|
||
373 | * get it. If it is not found, it will return null.
|
||
374 | 40435 | jjdelcerro | *
|
375 | * @param res An absolute or relative path to the requested resource.
|
||
376 | *
|
||
377 | * @return Resource's URL if it was found, nul otherwise.
|
||
378 | */
|
||
379 | 40566 | jjdelcerro | private URL getResource(File base, List<String> res) { |
380 | 40435 | jjdelcerro | File[] files = base.listFiles(); |
381 | |||
382 | 40566 | jjdelcerro | String parte = res.get(0); |
383 | 40435 | jjdelcerro | |
384 | 42559 | jjdelcerro | for (File file : files) { |
385 | if (file.getName().compareTo(parte) == 0) { |
||
386 | 40435 | jjdelcerro | if (res.size() == 1) { |
387 | try {
|
||
388 | 42559 | jjdelcerro | return new URL("file:" + file.toString()); |
389 | }catch (MalformedURLException e) { |
||
390 | 40435 | jjdelcerro | return null; |
391 | } |
||
392 | } else {
|
||
393 | 42559 | jjdelcerro | return getResource(file, res.subList(1, res.size())); |
394 | 40435 | jjdelcerro | } |
395 | } |
||
396 | } |
||
397 | |||
398 | return null; |
||
399 | } |
||
400 | 42774 | jbadia | |
401 | @Override
|
||
402 | public Enumeration<URL> getResources(String name) throws IOException { |
||
403 | 42929 | jjdelcerro | HashSet visitedPlugins = new HashSet(); |
404 | return getResources(name, visitedPlugins);
|
||
405 | } |
||
406 | |||
407 | protected Enumeration<URL> getResources(String name, HashSet<PluginClassLoader> visitedPlugins) throws IOException { |
||
408 | 42774 | jbadia | List<URL> resources = new ArrayList<>(); |
409 | Enumeration<URL> aux = super.getResources(name); |
||
410 | while(aux.hasMoreElements()){
|
||
411 | URL url = aux.nextElement();
|
||
412 | resources.add(url); |
||
413 | } |
||
414 | 42929 | jjdelcerro | visitedPlugins.add(this);
|
415 | 42774 | jbadia | for(PluginClassLoader loader: this.pluginLoaders){ |
416 | 42929 | jjdelcerro | if (!visitedPlugins.contains(loader)) {
|
417 | aux = loader.getResources(name, visitedPlugins); |
||
418 | while(aux.hasMoreElements()){
|
||
419 | URL url = aux.nextElement();
|
||
420 | resources.add(url); |
||
421 | } |
||
422 | } |
||
423 | 42774 | jbadia | } |
424 | return Collections.enumeration(resources); |
||
425 | } |
||
426 | 40435 | jjdelcerro | |
427 | /**
|
||
428 | 42437 | jjdelcerro | * Returns the name of the plugin (the name of the directory containing the
|
429 | * plugin).
|
||
430 | 40435 | jjdelcerro | *
|
431 | * @return An String containing the plugin's name.
|
||
432 | */
|
||
433 | public String getPluginName() { |
||
434 | 42437 | jjdelcerro | return baseDir.getName();
|
435 | 40435 | jjdelcerro | } |
436 | |||
437 | /*
|
||
438 | * @see java.security.SecureClassLoader#getPermissions(java.security.CodeSource)
|
||
439 | */
|
||
440 | 42559 | jjdelcerro | @Override
|
441 | 40435 | jjdelcerro | protected PermissionCollection getPermissions(CodeSource codesource) { |
442 | PermissionCollection perms = super.getPermissions(codesource); |
||
443 | perms.add(new AllPermission()); |
||
444 | |||
445 | return perms;
|
||
446 | } |
||
447 | |||
448 | /**
|
||
449 | 42437 | jjdelcerro | * Gets the plugin's base Iterator<Map.Entry<Integer, Integer>>dir, the
|
450 | * directory which will be used to search resources.
|
||
451 | 40435 | jjdelcerro | *
|
452 | * @return Returns the baseDir.
|
||
453 | */
|
||
454 | public String getBaseDir() { |
||
455 | return baseDir.getAbsolutePath();
|
||
456 | } |
||
457 | |||
458 | /**
|
||
459 | * Adds other classloader to use when all the normal methods fail.
|
||
460 | 42437 | jjdelcerro | *
|
461 | * @param classLoaders An ArrayList of ClassLoaders which will be used to
|
||
462 | * load classes when all the normal methods fail.
|
||
463 | 40435 | jjdelcerro | */
|
464 | 42437 | jjdelcerro | public static void addLoaders(ArrayList classLoaders) { |
465 | otherLoaders.addAll(classLoaders); |
||
466 | } |
||
467 | 40435 | jjdelcerro | |
468 | @Override
|
||
469 | public String toString() { |
||
470 | return super.toString() + " (" + getPluginName() + ")"; |
||
471 | } |
||
472 | 42437 | jjdelcerro | |
473 | 41231 | jjdelcerro | public void addPluginClassLoader(PluginClassLoader pluginClassLoader) { |
474 | 42437 | jjdelcerro | if (!this.pluginLoaders.contains(pluginClassLoader)) { |
475 | 41344 | jjdelcerro | this.pluginLoaders.add(pluginClassLoader);
|
476 | } |
||
477 | 41231 | jjdelcerro | } |
478 | 42437 | jjdelcerro | |
479 | 41788 | jjdelcerro | private Class singleLoadClass(Set<String> pluginsVisiteds, String name) throws ClassNotFoundException { |
480 | 42437 | jjdelcerro | |
481 | if (pluginsVisiteds.contains(this.getPluginName())) { |
||
482 | 41770 | fdiaz | return null; |
483 | } |
||
484 | pluginsVisiteds.add(this.getPluginName());
|
||
485 | |||
486 | 41788 | jjdelcerro | // Buscamos en las clases de las librer�as del plugin
|
487 | Class<?> c = findLoadedClass(name);
|
||
488 | |||
489 | if (c != null) { |
||
490 | return c;
|
||
491 | } |
||
492 | |||
493 | 42559 | jjdelcerro | logger.debug("Classloader {}, searching class '{}'",
|
494 | new Object[]{this.getPluginName(), name} |
||
495 | ); |
||
496 | 41788 | jjdelcerro | try {
|
497 | ZipFile jar = (ZipFile) clasesJar.get(name); |
||
498 | |||
499 | //No esta en ningun jar
|
||
500 | if (jar == null) { |
||
501 | //Buscamos en el directorio de clases
|
||
502 | 42437 | jjdelcerro | String classFileName = baseDir + "/classes/" |
503 | + name.replace('.', '/') + ".class"; |
||
504 | 41788 | jjdelcerro | File f = new File(classFileName); |
505 | 42437 | jjdelcerro | if (f.exists()) {
|
506 | 41788 | jjdelcerro | byte[] data = loadClassData(f); |
507 | c = defineClass(name, data, 0, data.length);
|
||
508 | 42559 | jjdelcerro | logger.debug("Classloader {}/classes-folder, found class {}",
|
509 | new Object[]{this.getPluginName(), name} |
||
510 | ); |
||
511 | 41788 | jjdelcerro | |
512 | } else {
|
||
513 | 42559 | jjdelcerro | for (PluginClassLoader pluginLoader1 : pluginLoaders) {
|
514 | 41788 | jjdelcerro | c = null;
|
515 | 42559 | jjdelcerro | PluginClassLoader pluginLoader = pluginLoader1; |
516 | if (pluginLoader != null) { |
||
517 | 41788 | jjdelcerro | try {
|
518 | 42559 | jjdelcerro | c = pluginLoader.singleLoadClass(pluginsVisiteds, name); |
519 | 41788 | jjdelcerro | } catch (ClassNotFoundException e) { |
520 | // Si no la encontramos en el primer plugin, capturamos la exceptcion
|
||
521 | // porque es probable que la encontremos en el resto de plugins.
|
||
522 | } |
||
523 | 42559 | jjdelcerro | if (c != null) { |
524 | logger.debug("Classloader {}, found class {} in plugin {}",
|
||
525 | new Object[]{ |
||
526 | this.getPluginName(),
|
||
527 | name, |
||
528 | pluginLoader.getPluginName() |
||
529 | } |
||
530 | ); |
||
531 | return c;
|
||
532 | } |
||
533 | 41788 | jjdelcerro | } |
534 | } |
||
535 | 42559 | jjdelcerro | |
536 | |||
537 | 41770 | fdiaz | } |
538 | 41788 | jjdelcerro | } else {
|
539 | 42559 | jjdelcerro | logger.debug("Classloader {}, found class {} in jar {}",
|
540 | new Object[]{ |
||
541 | this.getPluginName(),
|
||
542 | name, |
||
543 | jar.getName() |
||
544 | } |
||
545 | ); |
||
546 | 41788 | jjdelcerro | String fileName = name.replace('.', '/') + ".class"; |
547 | ZipEntry classFile = jar.getEntry(fileName);
|
||
548 | byte[] data = loadClassData(classFile, |
||
549 | jar.getInputStream(classFile)); |
||
550 | |||
551 | c = defineClass(name, data, 0, data.length);
|
||
552 | 42559 | jjdelcerro | if (c == null) { |
553 | logger.debug("Classloader {}, can't load class {} from jar {}",
|
||
554 | new Object[]{ |
||
555 | this.getPluginName(),
|
||
556 | name, |
||
557 | jar.getName() |
||
558 | } |
||
559 | ); |
||
560 | } |
||
561 | 42437 | jjdelcerro | |
562 | 41770 | fdiaz | } |
563 | 41788 | jjdelcerro | |
564 | if (c == null) { |
||
565 | 42559 | jjdelcerro | logger.debug("Classloader {}, class not found {}",
|
566 | new Object[]{ |
||
567 | this.getPluginName(),
|
||
568 | name |
||
569 | } |
||
570 | ); |
||
571 | debugDump(); |
||
572 | 41788 | jjdelcerro | throw new ClassNotFoundException(name); |
573 | 41770 | fdiaz | } |
574 | |||
575 | 41788 | jjdelcerro | return c;
|
576 | } catch (IOException e) { |
||
577 | 42559 | jjdelcerro | logger.debug("Classloader {}, error loading class {}",
|
578 | new Object[]{ |
||
579 | this.getPluginName(),
|
||
580 | name |
||
581 | }, |
||
582 | e |
||
583 | ); |
||
584 | 41788 | jjdelcerro | throw new ClassNotFoundException(Messages.getString( |
585 | "PluginClassLoader.Error_reading_file") + name);
|
||
586 | 42437 | jjdelcerro | } |
587 | 41770 | fdiaz | } |
588 | |||
589 | 41788 | jjdelcerro | /**
|
590 | 42437 | jjdelcerro | * Este metodo busca en este class loader y en el de los plugins de los que
|
591 | * depende. Se cerciora de que no se queda bucleado cuando hay una relacion
|
||
592 | * recursiva entre los plugins.
|
||
593 | *
|
||
594 | * @param pluginsVisiteds, set con los plugins que va visitando para evitar
|
||
595 | * que se quede bucleado cuando hay referencia ciclicas entre plugins.
|
||
596 | 41788 | jjdelcerro | * @param res
|
597 | 42437 | jjdelcerro | *
|
598 | * @return
|
||
599 | 41788 | jjdelcerro | */
|
600 | 42437 | jjdelcerro | private URL getResource(Set<String> pluginsVisiteds, String res) { |
601 | 41770 | fdiaz | URL ret = null; |
602 | |||
603 | 42437 | jjdelcerro | if (pluginsVisiteds.contains(this.getPluginName())) { |
604 | 41770 | fdiaz | return null; |
605 | } |
||
606 | pluginsVisiteds.add(this.getPluginName());
|
||
607 | |||
608 | 41788 | jjdelcerro | //
|
609 | // Primero buscamos en el directorio del plugin.
|
||
610 | try {
|
||
611 | 42559 | jjdelcerro | logger.debug("Classloader {}, searching resource '{}'", this.getPluginName(), res); |
612 | List<String> resource = new ArrayList<>(); |
||
613 | 41788 | jjdelcerro | StringTokenizer st = new StringTokenizer(res, "\\/"); |
614 | 42437 | jjdelcerro | while (st.hasMoreTokens()) {
|
615 | 41788 | jjdelcerro | String token = st.nextToken();
|
616 | resource.add(token); |
||
617 | } |
||
618 | ret = getResource(baseDir, resource); |
||
619 | 42437 | jjdelcerro | if (ret != null) { |
620 | 41788 | jjdelcerro | return ret;
|
621 | } |
||
622 | } catch (Exception e) { |
||
623 | 42559 | jjdelcerro | logger.warn("Classloader {}, Error getting resource '{}'.",
|
624 | new Object[]{ |
||
625 | this.getPluginName(), res
|
||
626 | }, |
||
627 | e |
||
628 | ); |
||
629 | 41788 | jjdelcerro | } |
630 | |||
631 | 42559 | jjdelcerro | logger.debug("Classloader {}, searching in depends pluginLoaders", this.getPluginName()); |
632 | for (PluginClassLoader pluginClassLoader : this.pluginLoaders) { |
||
633 | 42437 | jjdelcerro | if (pluginClassLoader != null) { |
634 | 41770 | fdiaz | try {
|
635 | pluginsVisiteds.add(pluginClassLoader.getPluginName()); |
||
636 | 41788 | jjdelcerro | ret = pluginClassLoader.getResource(pluginsVisiteds, res); |
637 | 42437 | jjdelcerro | if (ret != null) { |
638 | 42559 | jjdelcerro | logger.debug("Classloader {}, Found resource '{}' in plugin '{}'.",
|
639 | 41770 | fdiaz | new Object[]{ |
640 | 42559 | jjdelcerro | this.getPluginName(), res, pluginClassLoader.getPluginName(),});
|
641 | 41770 | fdiaz | return ret;
|
642 | } |
||
643 | } catch (Exception e) { |
||
644 | // Ignore, try in next classloader
|
||
645 | } |
||
646 | } |
||
647 | } |
||
648 | 41788 | jjdelcerro | |
649 | 42437 | jjdelcerro | if (ret == null) { |
650 | 41788 | jjdelcerro | //
|
651 | // Por ultimo en el class loader padre, se supone que es el del sistema.
|
||
652 | try {
|
||
653 | ret = super.getResource(res);
|
||
654 | } catch (Exception e) { |
||
655 | 42559 | jjdelcerro | logger.warn("Classloader {}, error getting resource '{}' in parent classloader'",
|
656 | new Object[]{this.getPluginName(), res}, |
||
657 | e |
||
658 | ); |
||
659 | 41788 | jjdelcerro | } |
660 | 42437 | jjdelcerro | if (ret == null) { |
661 | 42559 | jjdelcerro | logger.debug("Classloader {}, Resource '{}' not found.",
|
662 | new Object[]{this.getPluginName(), res} |
||
663 | ); |
||
664 | 41788 | jjdelcerro | } |
665 | } |
||
666 | return ret;
|
||
667 | 41770 | fdiaz | } |
668 | 41788 | jjdelcerro | |
669 | 40435 | jjdelcerro | } |