001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019 020package icy.plugin.classloader; 021 022import java.io.IOException; 023import java.io.InputStream; 024import java.net.URL; 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.Enumeration; 028import java.util.HashSet; 029import java.util.List; 030import java.util.Set; 031import java.util.logging.Level; 032import java.util.logging.Logger; 033 034/** 035 * Abstract class loader that can load classes from different resources 036 * 037 * @author Kamran Zafar 038 * @author Stephane Dallongeville 039 */ 040@SuppressWarnings("unchecked") 041public abstract class AbstractClassLoader extends ClassLoader 042{ 043 protected final List<ProxyClassLoader> loaders = new ArrayList<ProxyClassLoader>(); 044 045 private final ProxyClassLoader systemLoader = new SystemLoader(); 046 private final ProxyClassLoader parentLoader = new ParentLoader(); 047 private final ProxyClassLoader currentLoader = new CurrentLoader(); 048 private final ProxyClassLoader threadLoader = new ThreadContextLoader(); 049 050 /** 051 * Build a new instance of AbstractClassLoader.java. 052 * 053 * @param parent 054 * parent class loader 055 */ 056 public AbstractClassLoader(ClassLoader parent) 057 { 058 super(parent); 059 060 addDefaultLoaders(); 061 } 062 063 public void addLoader(ProxyClassLoader loader) 064 { 065 loaders.add(loader); 066 067 Collections.sort(loaders); 068 } 069 070 protected void addDefaultLoaders() 071 { 072 // always add this one 073 loaders.add(systemLoader); 074 loaders.add(parentLoader); 075 loaders.add(currentLoader); 076 loaders.add(threadLoader); 077 078 Collections.sort(loaders); 079 } 080 081 @Override 082 public Class loadClass(String className) throws ClassNotFoundException 083 { 084 return loadClass(className, true); 085 } 086 087 /** 088 * Overrides the loadClass method to load classes from other resources, 089 * JarClassLoader is the only subclass in this project that loads classes 090 * from jar files 091 * 092 * @see java.lang.ClassLoader#loadClass(String, boolean) 093 */ 094 @Override 095 public Class loadClass(String className, boolean resolveIt) throws ClassNotFoundException 096 { 097 if (className == null || className.trim().equals("")) 098 return null; 099 100 final List<ProxyClassLoader> loadersDone = new ArrayList<ProxyClassLoader>(); 101 102 for (ProxyClassLoader l : loaders) 103 { 104 // don't search in same loader 105 if (l.isEnabled() && !loadersDone.contains(l)) 106 { 107 final Class clazz = l.loadClass(className, resolveIt); 108 if (clazz != null) 109 return clazz; 110 111 // loader done 112 loadersDone.add(l); 113 } 114 } 115 116 throw new ClassNotFoundException(className); 117 } 118 119 /** 120 * Overrides the getResourceAsStream method to load non-class resources from 121 * other sources, JarClassLoader is the only subclass in this project that 122 * loads non-class resources from jar files 123 * 124 * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String) 125 */ 126 @Override 127 public InputStream getResourceAsStream(String name) 128 { 129 if (name == null || name.trim().equals("")) 130 return null; 131 132 final List<ProxyClassLoader> loadersDone = new ArrayList<ProxyClassLoader>(); 133 134 for (ProxyClassLoader l : loaders) 135 { 136 // don't search in same loader 137 if (l.isEnabled() && !loadersDone.contains(l)) 138 { 139 final InputStream is = l.getResourceAsStream(name); 140 if (is != null) 141 return is; 142 143 // loader done 144 loadersDone.add(l); 145 } 146 } 147 148 return null; 149 } 150 151 @Override 152 public URL getResource(String name) 153 { 154 if (name == null || name.trim().equals("")) 155 return null; 156 157 final List<ProxyClassLoader> loadersDone = new ArrayList<ProxyClassLoader>(); 158 159 for (ProxyClassLoader l : loaders) 160 { 161 // don't search in same loader 162 if (l.isEnabled() && !loadersDone.contains(l)) 163 { 164 final URL url = l.getResource(name); 165 if (url != null) 166 return url; 167 168 // loader done 169 loadersDone.add(l); 170 } 171 } 172 173 return null; 174 } 175 176 @Override 177 public Enumeration<URL> getResources(String name) throws IOException 178 { 179 if (name == null || name.trim().equals("")) 180 return null; 181 182 final Set<URL> result = new HashSet<URL>(); 183 final List<ProxyClassLoader> loadersDone = new ArrayList<ProxyClassLoader>(); 184 185 for (ProxyClassLoader l : loaders) 186 { 187 // don't search in same loader 188 if (l.isEnabled() && !loadersDone.contains(l)) 189 { 190 final Enumeration<URL> urls = l.getResources(name); 191 192 if (urls != null) 193 { 194 // avoid duplicate using Set here 195 while (urls.hasMoreElements()) 196 result.add(urls.nextElement()); 197 } 198 199 // loader done 200 loadersDone.add(l); 201 } 202 } 203 204 return Collections.enumeration(result); 205 } 206 207 /** 208 * System class loader 209 */ 210 class SystemLoader extends ProxyClassLoader 211 { 212 private final Logger logger = Logger.getLogger(SystemLoader.class.getName()); 213 214 public SystemLoader() 215 { 216 super(10); 217 218 enabled = Configuration.isSystemLoaderEnabled(); 219 } 220 221 @Override 222 public ClassLoader getLoader() 223 { 224 return getSystemClassLoader(); 225 } 226 227 @Override 228 public Class loadClass(String className, boolean resolveIt) 229 { 230 Class result; 231 232 try 233 { 234 result = findSystemClass(className); 235 } 236 catch (ClassNotFoundException e) 237 { 238 return null; 239 } 240 241 if (logger.isLoggable(Level.FINEST)) 242 logger.finest("Returning system class " + className); 243 244 return result; 245 } 246 247 @Override 248 public InputStream getResourceAsStream(String name) 249 { 250 InputStream is = getSystemResourceAsStream(name); 251 252 if (is != null) 253 { 254 if (logger.isLoggable(Level.FINEST)) 255 logger.finest("Returning system resource " + name); 256 257 return is; 258 } 259 260 return null; 261 } 262 263 @Override 264 public URL getResource(String name) 265 { 266 URL url = getSystemResource(name); 267 268 if (url != null) 269 { 270 if (logger.isLoggable(Level.FINEST)) 271 logger.finest("Returning system resource " + name); 272 273 return url; 274 } 275 276 return null; 277 } 278 279 @Override 280 public Enumeration<URL> getResources(String name) throws IOException 281 { 282 Enumeration<URL> urls = getSystemResources(name); 283 284 if ((urls != null) && urls.hasMoreElements()) 285 { 286 if (logger.isLoggable(Level.FINEST)) 287 logger.finest("Returning system resources " + name); 288 289 return urls; 290 } 291 292 return null; 293 } 294 } 295 296 /** 297 * Parent class loader 298 */ 299 class ParentLoader extends ProxyClassLoader 300 { 301 private final Logger logger = Logger.getLogger(ParentLoader.class.getName()); 302 303 public ParentLoader() 304 { 305 super(30); 306 307 enabled = Configuration.isParentLoaderEnabled(); 308 } 309 310 @Override 311 public ClassLoader getLoader() 312 { 313 return getParent(); 314 } 315 316 @Override 317 public Class loadClass(String className, boolean resolveIt) 318 { 319 Class result; 320 321 try 322 { 323 result = getParent().loadClass(className); 324 } 325 catch (ClassNotFoundException e) 326 { 327 return null; 328 } 329 330 if (logger.isLoggable(Level.FINEST)) 331 logger.finest("Returning class " + className + " loaded with parent classloader"); 332 333 return result; 334 } 335 336 @Override 337 public InputStream getResourceAsStream(String name) 338 { 339 InputStream is = getParent().getResourceAsStream(name); 340 341 if (is != null) 342 { 343 if (logger.isLoggable(Level.FINEST)) 344 logger.finest("Returning resource " + name + " loaded with parent classloader"); 345 346 return is; 347 } 348 return null; 349 } 350 351 @Override 352 public URL getResource(String name) 353 { 354 URL url = getParent().getResource(name); 355 356 if (url != null) 357 { 358 if (logger.isLoggable(Level.FINEST)) 359 logger.finest("Returning resource " + name + " loaded with parent classloader"); 360 361 return url; 362 } 363 364 return null; 365 } 366 367 @Override 368 public Enumeration<URL> getResources(String name) throws IOException 369 { 370 Enumeration<URL> urls = getParent().getResources(name); 371 372 if ((urls != null) && urls.hasMoreElements()) 373 { 374 if (logger.isLoggable(Level.FINEST)) 375 logger.finest("Returning resource " + name + " loaded with parent classloader"); 376 377 return urls; 378 } 379 380 return null; 381 } 382 } 383 384 /** 385 * Current class loader 386 */ 387 class CurrentLoader extends ProxyClassLoader 388 { 389 private final Logger logger = Logger.getLogger(CurrentLoader.class.getName()); 390 391 public CurrentLoader() 392 { 393 super(20); 394 395 enabled = Configuration.isCurrentLoaderEnabled(); 396 } 397 398 @Override 399 public ClassLoader getLoader() 400 { 401 return getClass().getClassLoader(); 402 } 403 404 @Override 405 public Class loadClass(String className, boolean resolveIt) 406 { 407 Class result; 408 409 try 410 { 411 result = getClass().getClassLoader().loadClass(className); 412 } 413 catch (ClassNotFoundException e) 414 { 415 return null; 416 } 417 418 if (logger.isLoggable(Level.FINEST)) 419 logger.finest("Returning class " + className + " loaded with current classloader"); 420 421 return result; 422 } 423 424 @Override 425 public InputStream getResourceAsStream(String name) 426 { 427 InputStream is = getClass().getClassLoader().getResourceAsStream(name); 428 429 if (is != null) 430 { 431 if (logger.isLoggable(Level.FINEST)) 432 logger.finest("Returning resource " + name + " loaded with current classloader"); 433 434 return is; 435 } 436 437 return null; 438 } 439 440 @Override 441 public URL getResource(String name) 442 { 443 URL url = getClass().getClassLoader().getResource(name); 444 445 if (url != null) 446 { 447 if (logger.isLoggable(Level.FINEST)) 448 logger.finest("Returning resource " + name + " loaded with current classloader"); 449 450 return url; 451 } 452 453 return null; 454 } 455 456 @Override 457 public Enumeration<URL> getResources(String name) throws IOException 458 { 459 Enumeration<URL> urls = getClass().getClassLoader().getResources(name); 460 461 if ((urls != null) && (urls.hasMoreElements())) 462 { 463 if (logger.isLoggable(Level.FINEST)) 464 logger.finest("Returning resources " + name + " loaded with current classloader"); 465 466 return urls; 467 } 468 469 return null; 470 } 471 472 } 473 474 /** 475 * Current class loader 476 */ 477 class ThreadContextLoader extends ProxyClassLoader 478 { 479 private final Logger logger = Logger.getLogger(ThreadContextLoader.class.getName()); 480 481 public ThreadContextLoader() 482 { 483 super(40); 484 485 enabled = Configuration.isThreadContextLoaderEnabled(); 486 } 487 488 @Override 489 public ClassLoader getLoader() 490 { 491 return Thread.currentThread().getContextClassLoader(); 492 } 493 494 @Override 495 public Class loadClass(String className, boolean resolveIt) 496 { 497 Class result; 498 try 499 { 500 result = Thread.currentThread().getContextClassLoader().loadClass(className); 501 } 502 catch (ClassNotFoundException e) 503 { 504 return null; 505 } 506 507 if (logger.isLoggable(Level.FINEST)) 508 logger.finest("Returning class " + className + " loaded with thread context classloader"); 509 510 return result; 511 } 512 513 @Override 514 public InputStream getResourceAsStream(String name) 515 { 516 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(name); 517 518 if (is != null) 519 { 520 if (logger.isLoggable(Level.FINEST)) 521 logger.finest("Returning resource " + name + " loaded with thread context classloader"); 522 523 return is; 524 } 525 526 return null; 527 } 528 529 @Override 530 public URL getResource(String name) 531 { 532 URL url = Thread.currentThread().getContextClassLoader().getResource(name); 533 534 if (url != null) 535 { 536 if (logger.isLoggable(Level.FINEST)) 537 logger.finest("Returning resource " + name + " loaded with thread context classloader"); 538 539 return url; 540 } 541 542 return null; 543 } 544 545 @Override 546 public Enumeration<URL> getResources(String name) throws IOException 547 { 548 Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(name); 549 550 if ((urls != null) && (urls.hasMoreElements())) 551 { 552 if (logger.isLoggable(Level.FINEST)) 553 logger.finest("Returning resources " + name + " loaded with thread context classloader"); 554 555 return urls; 556 } 557 558 return null; 559 } 560 } 561 562 public ProxyClassLoader getSystemLoader() 563 { 564 return systemLoader; 565 } 566 567 public ProxyClassLoader getParentLoader() 568 { 569 return parentLoader; 570 } 571 572 public ProxyClassLoader getCurrentLoader() 573 { 574 return currentLoader; 575 } 576 577 public ProxyClassLoader getThreadLoader() 578 { 579 return threadLoader; 580 } 581}