src/core/net/sf/basedb/util/JarClassLoader.java

Code
Comments
Other
Rev Date Author Line
1343 15 Sep 05 nicklas 1 /*
1343 15 Sep 05 nicklas 2   $Id$
1343 15 Sep 05 nicklas 3
3675 16 Aug 07 jari 4   Copyright (C) 2005 Nicklas Nordborg
4889 06 Apr 09 nicklas 5   Copyright (C) 2006 Jari Häkkinen, Nicklas Nordborg
3675 16 Aug 07 jari 6   Copyright (C) 2007 Nicklas Nordborg, Martin Svensson
1343 15 Sep 05 nicklas 7
2304 22 May 06 jari 8   This file is part of BASE - BioArray Software Environment.
2304 22 May 06 jari 9   Available at http://base.thep.lu.se/
1343 15 Sep 05 nicklas 10
1343 15 Sep 05 nicklas 11   BASE is free software; you can redistribute it and/or
1343 15 Sep 05 nicklas 12   modify it under the terms of the GNU General Public License
4479 05 Sep 08 jari 13   as published by the Free Software Foundation; either version 3
1343 15 Sep 05 nicklas 14   of the License, or (at your option) any later version.
1343 15 Sep 05 nicklas 15
1343 15 Sep 05 nicklas 16   BASE is distributed in the hope that it will be useful,
1343 15 Sep 05 nicklas 17   but WITHOUT ANY WARRANTY; without even the implied warranty of
1343 15 Sep 05 nicklas 18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1343 15 Sep 05 nicklas 19   GNU General Public License for more details.
1343 15 Sep 05 nicklas 20
1343 15 Sep 05 nicklas 21   You should have received a copy of the GNU General Public License
4515 11 Sep 08 jari 22   along with BASE. If not, see <http://www.gnu.org/licenses/>.
1343 15 Sep 05 nicklas 23 */
1343 15 Sep 05 nicklas 24 package net.sf.basedb.util;
1343 15 Sep 05 nicklas 25
4323 30 May 08 nicklas 26 import java.util.ArrayList;
4323 30 May 08 nicklas 27 import java.util.List;
1343 15 Sep 05 nicklas 28 import java.util.Map;
1343 15 Sep 05 nicklas 29 import java.util.HashMap;
7192 29 Aug 16 nicklas 30 import java.util.Iterator;
1345 19 Sep 05 nicklas 31 import java.util.Enumeration;
4393 13 Aug 08 nicklas 32 import java.util.Vector;
1343 15 Sep 05 nicklas 33 import java.util.jar.JarFile;
1343 15 Sep 05 nicklas 34 import java.util.jar.JarEntry;
1345 19 Sep 05 nicklas 35 import java.util.jar.Manifest;
7718 22 May 19 nicklas 36
1345 19 Sep 05 nicklas 37 import java.util.jar.Attributes;
5394 25 Aug 10 nicklas 38 import java.util.jar.Attributes.Name;
1345 19 Sep 05 nicklas 39 import java.io.File;
1343 15 Sep 05 nicklas 40 import java.io.InputStream;
1343 15 Sep 05 nicklas 41 import java.io.IOException;
5622 03 May 11 nicklas 42 import java.io.OutputStream;
1345 19 Sep 05 nicklas 43 import java.net.URL;
1345 19 Sep 05 nicklas 44 import java.net.MalformedURLException;
1343 15 Sep 05 nicklas 45
5622 03 May 11 nicklas 46 import net.sf.basedb.core.Application;
3078 22 Jan 07 nicklas 47 import net.sf.basedb.core.InvalidDataException;
3078 22 Jan 07 nicklas 48
1343 15 Sep 05 nicklas 49 /**
1343 15 Sep 05 nicklas 50   A class loader implementation that loads classes from JAR files.
1345 19 Sep 05 nicklas 51   Each JAR file requires a separate instance of this class. Use
1635 18 Nov 05 nicklas 52   the {@link #getInstance(String)} method to get an existing or create
1345 19 Sep 05 nicklas 53   a new instance for a specific JAR file. If the classes in the JAR file
1345 19 Sep 05 nicklas 54   requires other classes in another JAR file to work, the paths to those
1345 19 Sep 05 nicklas 55   JAR files must be listed in the <code>Class-Path</code> attribute in the
1345 19 Sep 05 nicklas 56   <code>META-INF/MANIFEST.MF</code> file. For example:
1345 19 Sep 05 nicklas 57   <pre class="code">
1345 19 Sep 05 nicklas 58 Manifest-Version: 1.0
1345 19 Sep 05 nicklas 59 Class-Path: OtherJarPlugin.jar
1345 19 Sep 05 nicklas 60 </pre> 
1343 15 Sep 05 nicklas 61
1345 19 Sep 05 nicklas 62   If more than one JAR is needed separate them with one or more spaces.
1345 19 Sep 05 nicklas 63   Note! It is only the <code>Class-Path</code> entry for the JAR file passed
1584 10 Nov 05 nicklas 64   to the {@link #getInstance(String)} method that is checked. The manifest
1345 19 Sep 05 nicklas 65   file is not checked for the other JARs.
5013 25 Jun 09 nicklas 66   <p>
5013 25 Jun 09 nicklas 67   This class loader will by default first look in the specified JAR files 
5013 25 Jun 09 nicklas 68   for a class or resource and only if not found delegate to the parent class
5013 25 Jun 09 nicklas 69   loader. This behaviour can be changed by calling {@link #setDelegateFirst(boolean)}
5013 25 Jun 09 nicklas 70   or by setting <code>X-Delegate-First : true</code> in the <code>MANIFEST.MF</code>
5013 25 Jun 09 nicklas 71   file in the main JAR file.
1345 19 Sep 05 nicklas 72
1343 15 Sep 05 nicklas 73   @author Nicklas
1343 15 Sep 05 nicklas 74   @version 2.0
1343 15 Sep 05 nicklas 75   @base.modified $Date$
1343 15 Sep 05 nicklas 76 */
1343 15 Sep 05 nicklas 77 public final class JarClassLoader
1343 15 Sep 05 nicklas 78   extends ClassLoader
1343 15 Sep 05 nicklas 79 {
1343 15 Sep 05 nicklas 80
6444 09 Apr 14 nicklas 81   private static final org.slf4j.Logger log = 
6444 09 Apr 14 nicklas 82     org.slf4j.LoggerFactory.getLogger(JarClassLoader.class);
4321 29 May 08 nicklas 83   
1343 15 Sep 05 nicklas 84   /**
1343 15 Sep 05 nicklas 85     A map of all loaded class loaders.
1343 15 Sep 05 nicklas 86   */
3631 03 Aug 07 nicklas 87   private static final HashMap<String, JarClassLoader> classLoaders = 
3631 03 Aug 07 nicklas 88     new HashMap<String, JarClassLoader>();
1343 15 Sep 05 nicklas 89
1343 15 Sep 05 nicklas 90   /**
1343 15 Sep 05 nicklas 91     Get a class loader for the specified JAR file. If a class loader for
1343 15 Sep 05 nicklas 92     the specified file already exists, that class loader is returned, otherwise
1343 15 Sep 05 nicklas 93     a new class loader is created.
1343 15 Sep 05 nicklas 94     
1343 15 Sep 05 nicklas 95     @param jarPath The path to a JAR file
1343 15 Sep 05 nicklas 96     @return A class loader
1343 15 Sep 05 nicklas 97     @throws IOException If the jar file can't be loaded
1343 15 Sep 05 nicklas 98   */
1343 15 Sep 05 nicklas 99   public static final ClassLoader getInstance(String jarPath)
1343 15 Sep 05 nicklas 100     throws IOException
1343 15 Sep 05 nicklas 101   {
3631 03 Aug 07 nicklas 102     return getInstance(jarPath, false);
3631 03 Aug 07 nicklas 103   }
3631 03 Aug 07 nicklas 104
3631 03 Aug 07 nicklas 105   /**
3631 03 Aug 07 nicklas 106     Get a class loader for the specified JAR file, optionally unloading an the old
3631 03 Aug 07 nicklas 107     one if the JAR file has been modified. A new class loader is created if no
3631 03 Aug 07 nicklas 108     class loader exists or if <code>autoUnload</code> is <code>true</code> and the JAR file 
3631 03 Aug 07 nicklas 109     has changed since the existing class loader was created. 
3631 03 Aug 07 nicklas 110     
3631 03 Aug 07 nicklas 111     @param jarPath The path to a JAR file
4323 30 May 08 nicklas 112     @param autoUnload If TRUE the old class loaded will automatically be unloaded if the
4323 30 May 08 nicklas 113       JAR file or any one it depends on (listed in the Class-Path attribute in the manifest file)
4323 30 May 08 nicklas 114       has been modified (if the timestamp and/or size) is different
3631 03 Aug 07 nicklas 115     @return A class loader
3631 03 Aug 07 nicklas 116     @throws IOException If the jar file can't be loaded
3631 03 Aug 07 nicklas 117     @since 2.4
3631 03 Aug 07 nicklas 118   */
3631 03 Aug 07 nicklas 119   public static final ClassLoader getInstance(String jarPath, boolean autoUnload)
3631 03 Aug 07 nicklas 120     throws IOException
3631 03 Aug 07 nicklas 121   {
3631 03 Aug 07 nicklas 122     JarClassLoader cl = classLoaders.get(jarPath);
3631 03 Aug 07 nicklas 123     if (cl != null && autoUnload)
3631 03 Aug 07 nicklas 124     {
3631 03 Aug 07 nicklas 125       // Check if JAR file exists and has same timestamp and size
4323 30 May 08 nicklas 126       if (cl.hasChanged(true)) cl = null;
3631 03 Aug 07 nicklas 127     }
1343 15 Sep 05 nicklas 128     if (cl == null)
1343 15 Sep 05 nicklas 129     {
1343 15 Sep 05 nicklas 130       cl = new JarClassLoader(jarPath);
1343 15 Sep 05 nicklas 131       synchronized (classLoaders)
1343 15 Sep 05 nicklas 132       {
1343 15 Sep 05 nicklas 133         classLoaders.put(jarPath, cl);
1343 15 Sep 05 nicklas 134       }
1343 15 Sep 05 nicklas 135     }
1343 15 Sep 05 nicklas 136     return cl;
1343 15 Sep 05 nicklas 137   }
3631 03 Aug 07 nicklas 138   
1343 15 Sep 05 nicklas 139   /**
3760 20 Sep 07 martin 140      Get a new class loader for the specified jar file.  
3760 20 Sep 07 martin 141       @param jarPath The path to the jar file
3760 20 Sep 07 martin 142       @return A class loader object
3760 20 Sep 07 martin 143       @throws IOException If the jar file can't be loaded
3760 20 Sep 07 martin 144   */
3760 20 Sep 07 martin 145   public static final ClassLoader newInstance(String jarPath)
3760 20 Sep 07 martin 146     throws IOException
3760 20 Sep 07 martin 147   {
3760 20 Sep 07 martin 148     return new JarClassLoader(jarPath);
3760 20 Sep 07 martin 149   }
3760 20 Sep 07 martin 150   
3760 20 Sep 07 martin 151   /**
1343 15 Sep 05 nicklas 152     Check if a class loader for the given JAR file exists.
1343 15 Sep 05 nicklas 153     @param jarPath The path to the JAR file
1343 15 Sep 05 nicklas 154     @return TRUE if a class loader exists, FALSE otherwise
1343 15 Sep 05 nicklas 155   */
1343 15 Sep 05 nicklas 156   public static final boolean exists(String jarPath)
1343 15 Sep 05 nicklas 157   {
1343 15 Sep 05 nicklas 158     return classLoaders.containsKey(jarPath);
1343 15 Sep 05 nicklas 159   }
1343 15 Sep 05 nicklas 160   
1343 15 Sep 05 nicklas 161   /**
1345 19 Sep 05 nicklas 162     The main JAR file to load classes from.
1343 15 Sep 05 nicklas 163   */
1345 19 Sep 05 nicklas 164   private final File mainJarFile;
1345 19 Sep 05 nicklas 165
1345 19 Sep 05 nicklas 166   /**
1345 19 Sep 05 nicklas 167     Contains mappings from class names to the JAR file in which 
1345 19 Sep 05 nicklas 168     the class implementation can be found.
1345 19 Sep 05 nicklas 169   */
4393 13 Aug 08 nicklas 170   private final Map<String, List<File>> classPath;
5013 25 Jun 09 nicklas 171
5013 25 Jun 09 nicklas 172   /**
5013 25 Jun 09 nicklas 173     If we should delegate to the parent class loader first.
5013 25 Jun 09 nicklas 174   */
5013 25 Jun 09 nicklas 175   private boolean delegateFirst;
1343 15 Sep 05 nicklas 176   
1343 15 Sep 05 nicklas 177   /**
4323 30 May 08 nicklas 178     For keeping track of jar files listed by Class-Path entry
4323 30 May 08 nicklas 179     in the manifest file.
4323 30 May 08 nicklas 180   */
5394 25 Aug 10 nicklas 181   private final Map<File, JarInfo> jarFiles;
7192 29 Aug 16 nicklas 182     
7192 29 Aug 16 nicklas 183   /**
7192 29 Aug 16 nicklas 184     Class loaders for extensions.
7192 29 Aug 16 nicklas 185   */
7192 29 Aug 16 nicklas 186   private final List<JarClassLoaderProxy> proxyLoaders;
4323 30 May 08 nicklas 187   
4323 30 May 08 nicklas 188   /**
3631 03 Aug 07 nicklas 189     The timestamp of the JAR file.
3631 03 Aug 07 nicklas 190   */
3631 03 Aug 07 nicklas 191   private final long jarTimeStamp;
3631 03 Aug 07 nicklas 192   
3631 03 Aug 07 nicklas 193   /**
3631 03 Aug 07 nicklas 194     The size of the JAR file.
3631 03 Aug 07 nicklas 195   */
3631 03 Aug 07 nicklas 196   private final long jarSize;
3631 03 Aug 07 nicklas 197   
3631 03 Aug 07 nicklas 198   
3631 03 Aug 07 nicklas 199   /**
1343 15 Sep 05 nicklas 200     Create a new JAR file class loader.
1343 15 Sep 05 nicklas 201     @param jarPath The path to the JAR file
3078 22 Jan 07 nicklas 202     @throws InvalidDataException If the JAR file can't be loaded
3078 22 Jan 07 nicklas 203     @throws IOException If there is another IO-related error
1343 15 Sep 05 nicklas 204   */
1343 15 Sep 05 nicklas 205   private JarClassLoader(String jarPath)
1343 15 Sep 05 nicklas 206     throws IOException
1343 15 Sep 05 nicklas 207   {
1347 19 Sep 05 nicklas 208     super(Thread.currentThread().getContextClassLoader());
4391 12 Aug 08 nicklas 209     log.debug("Creating JarClassLoader: file=" + jarPath);
1345 19 Sep 05 nicklas 210     mainJarFile = new File(jarPath);
4323 30 May 08 nicklas 211     if (!mainJarFile.exists() || !mainJarFile.isFile()) 
4323 30 May 08 nicklas 212     {
4323 30 May 08 nicklas 213       throw new InvalidDataException("JAR file not found: " + jarPath);
4323 30 May 08 nicklas 214     }
3631 03 Aug 07 nicklas 215     this.jarTimeStamp = mainJarFile.lastModified();
3631 03 Aug 07 nicklas 216     this.jarSize = mainJarFile.length();
5013 25 Jun 09 nicklas 217     this.delegateFirst = false;
4393 13 Aug 08 nicklas 218     classPath = new HashMap<String, List<File>>();
5394 25 Aug 10 nicklas 219     jarFiles = new HashMap<File, JarInfo>();
7192 29 Aug 16 nicklas 220     proxyLoaders = new ArrayList<JarClassLoaderProxy>();
1345 19 Sep 05 nicklas 221     loadJarFile(mainJarFile, true);
1343 15 Sep 05 nicklas 222   }
1345 19 Sep 05 nicklas 223
7194 30 Aug 16 nicklas 224   @Override
7194 30 Aug 16 nicklas 225   public String toString() 
7194 30 Aug 16 nicklas 226   {
7194 30 Aug 16 nicklas 227     return super.toString() + "[" + mainJarFile + "]";
7194 30 Aug 16 nicklas 228   }
7194 30 Aug 16 nicklas 229
1343 15 Sep 05 nicklas 230   /*
1345 19 Sep 05 nicklas 231     From the ClassLoader class
1343 15 Sep 05 nicklas 232     -------------------------------------------
1343 15 Sep 05 nicklas 233   */
4391 12 Aug 08 nicklas 234   @Override
2031 21 Feb 06 nicklas 235   protected Class<?> findClass(String name)
1343 15 Sep 05 nicklas 236     throws ClassNotFoundException
1343 15 Sep 05 nicklas 237   {
4391 12 Aug 08 nicklas 238     log.debug("findClass: " + name);
4393 13 Aug 08 nicklas 239     List<File> files = classPath.get(classNameToPath(name));
4393 13 Aug 08 nicklas 240     if (files == null || files.isEmpty())
1345 19 Sep 05 nicklas 241     {
1345 19 Sep 05 nicklas 242       throw new ClassNotFoundException(name);
1345 19 Sep 05 nicklas 243     }
5394 25 Aug 10 nicklas 244     File file = files.get(0);
5394 25 Aug 10 nicklas 245     byte[] b = loadClassData(file, name);
5394 25 Aug 10 nicklas 246     int i = name.lastIndexOf('.');
5394 25 Aug 10 nicklas 247     if (i > 0)
5394 25 Aug 10 nicklas 248     {
5394 25 Aug 10 nicklas 249       String packageName = name.substring(0, i);
7718 22 May 19 nicklas 250       if (!isPackageDefined(packageName))
5394 25 Aug 10 nicklas 251       {
5394 25 Aug 10 nicklas 252         JarInfo info = jarFiles.get(file);
5394 25 Aug 10 nicklas 253         Manifest mf = info != null ? info.manifest : null;
7718 22 May 19 nicklas 254         definePackage(packageName, mf);
5394 25 Aug 10 nicklas 255       }
5394 25 Aug 10 nicklas 256     }
5394 25 Aug 10 nicklas 257     Class<?> clazz = defineClass(name, b, 0, b.length);
5394 25 Aug 10 nicklas 258     return clazz;
1343 15 Sep 05 nicklas 259   }
1345 19 Sep 05 nicklas 260   
7718 22 May 19 nicklas 261   // Safety limit to avoid endless loops in case parent class loaders are incorrectly linked
7718 22 May 19 nicklas 262   private static final int MAX_PARENTS = 20;
7718 22 May 19 nicklas 263   private boolean isPackageDefined(String name)
7718 22 May 19 nicklas 264   {
7718 22 May 19 nicklas 265     ClassLoader c = this;
7718 22 May 19 nicklas 266     for (int i = 0; c != null && i < MAX_PARENTS; i++)
7718 22 May 19 nicklas 267     {
7718 22 May 19 nicklas 268       if (c.getDefinedPackage(name) != null) return true;
7718 22 May 19 nicklas 269       c = c.getParent();
7718 22 May 19 nicklas 270     }
7718 22 May 19 nicklas 271     return false;
7718 22 May 19 nicklas 272   }
7718 22 May 19 nicklas 273   
4391 12 Aug 08 nicklas 274   @Override
1345 19 Sep 05 nicklas 275   protected URL findResource(String name)
1345 19 Sep 05 nicklas 276   {
4391 12 Aug 08 nicklas 277     log.debug("findResource: " + name);
1345 19 Sep 05 nicklas 278     URL url = null;
1345 19 Sep 05 nicklas 279     try
1345 19 Sep 05 nicklas 280     {
4393 13 Aug 08 nicklas 281       List<File> files = classPath.get(name);
4393 13 Aug 08 nicklas 282       if (files != null && files.size() > 0)
1345 19 Sep 05 nicklas 283       {
4393 13 Aug 08 nicklas 284         if (log.isDebugEnabled()) log.debug("Found: " + files);
4393 13 Aug 08 nicklas 285         String newUrl = "jar:"+files.get(0).toURI().toURL()+"!/"+name;
1345 19 Sep 05 nicklas 286         url = new URL(newUrl);
1345 19 Sep 05 nicklas 287       }
4391 12 Aug 08 nicklas 288       else
4391 12 Aug 08 nicklas 289       {
4391 12 Aug 08 nicklas 290         log.debug("Not found: " + name);
4391 12 Aug 08 nicklas 291       }
1345 19 Sep 05 nicklas 292     }
1345 19 Sep 05 nicklas 293     catch (MalformedURLException ex)
1345 19 Sep 05 nicklas 294     {
1345 19 Sep 05 nicklas 295       ex.printStackTrace();
1345 19 Sep 05 nicklas 296     }
1345 19 Sep 05 nicklas 297     return url;
1345 19 Sep 05 nicklas 298   }
1345 19 Sep 05 nicklas 299   
4391 12 Aug 08 nicklas 300   @Override
4393 13 Aug 08 nicklas 301   protected Enumeration<URL> findResources(String name)
4393 13 Aug 08 nicklas 302   {
4393 13 Aug 08 nicklas 303     log.debug("findResources: " + name);
4393 13 Aug 08 nicklas 304     Enumeration<URL> url = null;
4393 13 Aug 08 nicklas 305     try
4393 13 Aug 08 nicklas 306     {
4393 13 Aug 08 nicklas 307       List<File> files = classPath.get(name);
4393 13 Aug 08 nicklas 308       if (files != null && files.size() > 0)
4393 13 Aug 08 nicklas 309       {
4393 13 Aug 08 nicklas 310         if (log.isDebugEnabled()) log.debug("Found: " + files);
5013 25 Jun 09 nicklas 311         Vector<URL> v = new Vector<URL>(files.size());
4393 13 Aug 08 nicklas 312         for (File f : files)
4393 13 Aug 08 nicklas 313         {
4393 13 Aug 08 nicklas 314           String newUrl = "jar:"+f.toURI().toURL()+"!/"+name;
4393 13 Aug 08 nicklas 315           v.add(new URL(newUrl));
4393 13 Aug 08 nicklas 316         }
4393 13 Aug 08 nicklas 317         url = v.elements();
4393 13 Aug 08 nicklas 318       }
4393 13 Aug 08 nicklas 319       else
4393 13 Aug 08 nicklas 320       {
4393 13 Aug 08 nicklas 321         log.debug("Not found: " + name);
4393 13 Aug 08 nicklas 322       }
4393 13 Aug 08 nicklas 323     }
4393 13 Aug 08 nicklas 324     catch (MalformedURLException ex)
4393 13 Aug 08 nicklas 325     {
4393 13 Aug 08 nicklas 326       ex.printStackTrace();
4393 13 Aug 08 nicklas 327     }
4393 13 Aug 08 nicklas 328     return url;
4393 13 Aug 08 nicklas 329   }
4393 13 Aug 08 nicklas 330   
4393 13 Aug 08 nicklas 331   @Override
4391 12 Aug 08 nicklas 332   protected Class<?> loadClass(String name, boolean resolve)
4391 12 Aug 08 nicklas 333     throws ClassNotFoundException
4391 12 Aug 08 nicklas 334   {
4391 12 Aug 08 nicklas 335     if (log.isDebugEnabled()) log.debug("loadClass: " + name);
5013 25 Jun 09 nicklas 336     
5013 25 Jun 09 nicklas 337     // 1. Check if the class has already been loaded
6875 20 Apr 15 nicklas 338     Class<?> c = findLoadedClass(name);
5013 25 Jun 09 nicklas 339     if (log.isDebugEnabled()) log.debug("loaded class (" + name + "): " + c);
5013 25 Jun 09 nicklas 340     
5013 25 Jun 09 nicklas 341     ClassLoader system = ClassLoader.getSystemClassLoader();
5013 25 Jun 09 nicklas 342     ClassLoader parent = getParent();
5013 25 Jun 09 nicklas 343     
5013 25 Jun 09 nicklas 344     // 2. Check the system class loader
7192 29 Aug 16 nicklas 345     if (c == null && parent != system) 
7192 29 Aug 16 nicklas 346     {
7192 29 Aug 16 nicklas 347       c = loadClassInternal(system, name);
7192 29 Aug 16 nicklas 348       if (log.isDebugEnabled()) log.debug("system class (" + name + "): " + c);
7192 29 Aug 16 nicklas 349     }
5013 25 Jun 09 nicklas 350     
5013 25 Jun 09 nicklas 351     // 3. Delegate to parent class loader
7192 29 Aug 16 nicklas 352     if (c == null && delegateFirst) 
7192 29 Aug 16 nicklas 353     {
7192 29 Aug 16 nicklas 354       c = loadClassInternal(parent, name);
7192 29 Aug 16 nicklas 355       if (log.isDebugEnabled()) log.debug("parent class (" + name + "): " + c);
7192 29 Aug 16 nicklas 356     }
5013 25 Jun 09 nicklas 357
5013 25 Jun 09 nicklas 358     // 4. Look for the class in this class path
5013 25 Jun 09 nicklas 359     if (c == null)
5013 25 Jun 09 nicklas 360     {
5013 25 Jun 09 nicklas 361       try
5013 25 Jun 09 nicklas 362       {
5013 25 Jun 09 nicklas 363         c = findClass(name);
5013 25 Jun 09 nicklas 364       }
5013 25 Jun 09 nicklas 365       catch (ClassNotFoundException ex)
5013 25 Jun 09 nicklas 366       {}
7192 29 Aug 16 nicklas 367       if (log.isDebugEnabled()) log.debug("my class (" + name + "): " + c);
5013 25 Jun 09 nicklas 368     }
5013 25 Jun 09 nicklas 369     
7194 30 Aug 16 nicklas 370     // 5. Side-load from proxied JAR files (eg. other extensions)
7192 29 Aug 16 nicklas 371     Iterator<JarClassLoaderProxy> it = proxyLoaders.iterator();
7192 29 Aug 16 nicklas 372     while (c == null && it.hasNext())
7192 29 Aug 16 nicklas 373     {
7192 29 Aug 16 nicklas 374       JarClassLoaderProxy proxyLoader = it.next();
7192 29 Aug 16 nicklas 375       c = proxyLoader.findClass(name);
7192 29 Aug 16 nicklas 376       if (log.isDebugEnabled()) log.debug("proxy class (" + proxyLoader.jarPath + "; " + name + "): " + c);
7192 29 Aug 16 nicklas 377     }
7192 29 Aug 16 nicklas 378     
7194 30 Aug 16 nicklas 379     // 6. Delegate to parent class loader if it hasn't been done
7194 30 Aug 16 nicklas 380     if (c == null && !delegateFirst) 
7194 30 Aug 16 nicklas 381     {
7194 30 Aug 16 nicklas 382       c = loadClassInternal(parent, name);
7194 30 Aug 16 nicklas 383       if (log.isDebugEnabled()) log.debug("parent class (" + name + "): " + c);
7194 30 Aug 16 nicklas 384     }
7194 30 Aug 16 nicklas 385     
5013 25 Jun 09 nicklas 386     if (c == null)
5013 25 Jun 09 nicklas 387     {
5013 25 Jun 09 nicklas 388       // Class not found
5013 25 Jun 09 nicklas 389       throw new ClassNotFoundException(name);
5013 25 Jun 09 nicklas 390     }
5013 25 Jun 09 nicklas 391     
5013 25 Jun 09 nicklas 392     if (resolve) resolveClass(c);
5013 25 Jun 09 nicklas 393     return c;
4391 12 Aug 08 nicklas 394   }
4391 12 Aug 08 nicklas 395   
4391 12 Aug 08 nicklas 396   @Override
4391 12 Aug 08 nicklas 397   public URL getResource(String name)
4391 12 Aug 08 nicklas 398   {
4391 12 Aug 08 nicklas 399     if (log.isDebugEnabled()) log.debug("getResource: " + name);
5013 25 Jun 09 nicklas 400     URL url = null;
5013 25 Jun 09 nicklas 401     ClassLoader parent = getParent();
5013 25 Jun 09 nicklas 402     
5013 25 Jun 09 nicklas 403     // 1. Check with parent loader if delegating
5013 25 Jun 09 nicklas 404     if (delegateFirst)
5013 25 Jun 09 nicklas 405     {
5013 25 Jun 09 nicklas 406       if (parent != null) url = parent.getResource(name);
5013 25 Jun 09 nicklas 407     }
5013 25 Jun 09 nicklas 408     
5013 25 Jun 09 nicklas 409     // 2. Check with this class loader
5013 25 Jun 09 nicklas 410     if (url == null) url = findResource(name);
5013 25 Jun 09 nicklas 411
7196 30 Aug 16 nicklas 412     // 3. Side-load from proxied JAR files (eg. other extensions)
7196 30 Aug 16 nicklas 413     if (url == null)
7196 30 Aug 16 nicklas 414     {
7196 30 Aug 16 nicklas 415       Iterator<JarClassLoaderProxy> it = proxyLoaders.iterator();
7196 30 Aug 16 nicklas 416       while (url == null && it.hasNext())
7196 30 Aug 16 nicklas 417       {
7196 30 Aug 16 nicklas 418         JarClassLoaderProxy proxyLoader = it.next();
7196 30 Aug 16 nicklas 419         url = proxyLoader.findResource(name);
7196 30 Aug 16 nicklas 420       }
7196 30 Aug 16 nicklas 421     }
7196 30 Aug 16 nicklas 422
5013 25 Jun 09 nicklas 423     // 3. Check with parent loader if not done so already
5013 25 Jun 09 nicklas 424     if (url == null && !delegateFirst)
5013 25 Jun 09 nicklas 425     {
5013 25 Jun 09 nicklas 426       if (parent != null) url = parent.getResource(name);
5013 25 Jun 09 nicklas 427     }
5013 25 Jun 09 nicklas 428     return url;
4391 12 Aug 08 nicklas 429   }
4393 13 Aug 08 nicklas 430   
4393 13 Aug 08 nicklas 431   @Override
4393 13 Aug 08 nicklas 432   public Enumeration<URL> getResources(String name)
4393 13 Aug 08 nicklas 433     throws IOException
4393 13 Aug 08 nicklas 434   {
4393 13 Aug 08 nicklas 435     if (log.isDebugEnabled()) log.debug("getResources: " + name);
5013 25 Jun 09 nicklas 436     ClassLoader parent = getParent();
5013 25 Jun 09 nicklas 437     Vector<URL> resources = new Vector<URL>();
5013 25 Jun 09 nicklas 438     
5013 25 Jun 09 nicklas 439     Enumeration<URL> parentResources = null;
5013 25 Jun 09 nicklas 440     if (parent != null) 
5013 25 Jun 09 nicklas 441     {
5013 25 Jun 09 nicklas 442       parentResources = parent.getResources(name);
5013 25 Jun 09 nicklas 443     }
5013 25 Jun 09 nicklas 444     Enumeration<URL> myResources = findResources(name);
5013 25 Jun 09 nicklas 445
5013 25 Jun 09 nicklas 446     if (delegateFirst) addResources(resources, parentResources);
5013 25 Jun 09 nicklas 447     addResources(resources, myResources);
7196 30 Aug 16 nicklas 448     Iterator<JarClassLoaderProxy> it = proxyLoaders.iterator();
7196 30 Aug 16 nicklas 449     while (it.hasNext())
7196 30 Aug 16 nicklas 450     {
7196 30 Aug 16 nicklas 451       JarClassLoaderProxy proxyLoader = it.next();
7196 30 Aug 16 nicklas 452       addResources(resources, proxyLoader.findResources(name));
7196 30 Aug 16 nicklas 453     }
7196 30 Aug 16 nicklas 454
5013 25 Jun 09 nicklas 455     if (!delegateFirst) addResources(resources, parentResources);
5013 25 Jun 09 nicklas 456     
5013 25 Jun 09 nicklas 457     return resources.elements();
4393 13 Aug 08 nicklas 458   }
1343 15 Sep 05 nicklas 459   // -------------------------------------------
1343 15 Sep 05 nicklas 460
1343 15 Sep 05 nicklas 461   /**
5013 25 Jun 09 nicklas 462     If the class loader should delegate to the parent class loader
5013 25 Jun 09 nicklas 463     before trying to find the class by it's own.
5013 25 Jun 09 nicklas 464     @param delegateFirst TRUE to delegate to parent first, FALSE to
5013 25 Jun 09 nicklas 465       only delegate if the class is not found
5013 25 Jun 09 nicklas 466     @since 2.13
5013 25 Jun 09 nicklas 467   */
5013 25 Jun 09 nicklas 468   public void setDelegateFirst(boolean delegateFirst)
5013 25 Jun 09 nicklas 469   {
5013 25 Jun 09 nicklas 470     this.delegateFirst = delegateFirst;
5013 25 Jun 09 nicklas 471   }
5013 25 Jun 09 nicklas 472   
5013 25 Jun 09 nicklas 473   /**
5013 25 Jun 09 nicklas 474     If this class loader delegates to the parent class loader
5013 25 Jun 09 nicklas 475     before or after trying to find the class by itself.
5013 25 Jun 09 nicklas 476     @since 2.13
5013 25 Jun 09 nicklas 477   */
5013 25 Jun 09 nicklas 478   public boolean getDelegateFirst()
5013 25 Jun 09 nicklas 479   {
5013 25 Jun 09 nicklas 480     return delegateFirst;
5013 25 Jun 09 nicklas 481   }
5013 25 Jun 09 nicklas 482   
5013 25 Jun 09 nicklas 483   /**
4323 30 May 08 nicklas 484     Check if the JAR file this class loader loads is classes
4323 30 May 08 nicklas 485     from has changed since this class loader was created. 
4323 30 May 08 nicklas 486     @param checkSecondary TRUE to also check secondary JAR files
4323 30 May 08 nicklas 487       listed in the Class-Path entry of the manifest file
4323 30 May 08 nicklas 488     @since 2.8
4323 30 May 08 nicklas 489   */
4323 30 May 08 nicklas 490   public boolean hasChanged(boolean checkSecondary)
4323 30 May 08 nicklas 491   {
7226 15 Nov 16 nicklas 492     if (log.isDebugEnabled())
7226 15 Nov 16 nicklas 493     {
7226 15 Nov 16 nicklas 494       log.debug("Checking if JAR has changed: " + mainJarFile);
7226 15 Nov 16 nicklas 495     }
4323 30 May 08 nicklas 496     boolean isSame = mainJarFile.exists() && 
4323 30 May 08 nicklas 497       mainJarFile.lastModified() == jarTimeStamp && mainJarFile.length() == jarSize;
7226 15 Nov 16 nicklas 498     if (!isSame) 
7226 15 Nov 16 nicklas 499     {
7226 15 Nov 16 nicklas 500       log.debug("Main JAR file has changed: " + mainJarFile);
7226 15 Nov 16 nicklas 501       return true;
7226 15 Nov 16 nicklas 502     }
4323 30 May 08 nicklas 503     if (checkSecondary)
4323 30 May 08 nicklas 504     {
5394 25 Aug 10 nicklas 505       for (JarInfo info : jarFiles.values())
4323 30 May 08 nicklas 506       {
4323 30 May 08 nicklas 507         File jarFile = info.jarFile;
7226 15 Nov 16 nicklas 508         if (jarFile.equals(mainJarFile)) continue; // We have already checked this one
7226 15 Nov 16 nicklas 509         
7226 15 Nov 16 nicklas 510         if (log.isDebugEnabled())
7226 15 Nov 16 nicklas 511         {
7226 15 Nov 16 nicklas 512           log.debug("Checking if secondary JAR has changed: " + jarFile);
7226 15 Nov 16 nicklas 513         }
4390 12 Aug 08 nicklas 514         if (jarFile.exists())
4390 12 Aug 08 nicklas 515         {
4390 12 Aug 08 nicklas 516           isSame = info.existed && 
4390 12 Aug 08 nicklas 517             jarFile.lastModified() == info.lastModified && 
4390 12 Aug 08 nicklas 518             jarFile.length() == info.size;
4390 12 Aug 08 nicklas 519         }
4390 12 Aug 08 nicklas 520         else
4390 12 Aug 08 nicklas 521         {
4390 12 Aug 08 nicklas 522           isSame = !info.existed;
4390 12 Aug 08 nicklas 523         }
7226 15 Nov 16 nicklas 524         if (!isSame) 
7226 15 Nov 16 nicklas 525         {
7226 15 Nov 16 nicklas 526           log.debug("Secondary JAR file has changed: " + jarFile);
7226 15 Nov 16 nicklas 527           return true;
7226 15 Nov 16 nicklas 528         }
4323 30 May 08 nicklas 529       }
7196 30 Aug 16 nicklas 530       for (JarClassLoaderProxy proxy : proxyLoaders)
7196 30 Aug 16 nicklas 531       {
7226 15 Nov 16 nicklas 532         if (log.isDebugEnabled())
7226 15 Nov 16 nicklas 533         {
7226 15 Nov 16 nicklas 534           log.debug("Checking if proxy JAR has changed: " + proxy.jarPath);
7226 15 Nov 16 nicklas 535         }
7226 15 Nov 16 nicklas 536         isSame = !proxy.hasChanged();
7226 15 Nov 16 nicklas 537         if (!isSame) 
7226 15 Nov 16 nicklas 538         {
7226 15 Nov 16 nicklas 539           log.debug("Proxy JAR has changed: " + proxy.jarPath);
7226 15 Nov 16 nicklas 540           return true;
7226 15 Nov 16 nicklas 541         }
7196 30 Aug 16 nicklas 542       }
4323 30 May 08 nicklas 543     }
7226 15 Nov 16 nicklas 544     
7226 15 Nov 16 nicklas 545     if (log.isDebugEnabled()) log.debug("JAR has not changed: " + mainJarFile);
4323 30 May 08 nicklas 546     return false;
4323 30 May 08 nicklas 547   }
4323 30 May 08 nicklas 548   
4323 30 May 08 nicklas 549   /**
1345 19 Sep 05 nicklas 550     Convert a class name to a file path. Ie. replace dots with slahses and add .class
6898 12 May 15 nicklas 551     to the end: net.sf.basedb.util.JarClassLoader -&gt; net/sf/basedb/util/JarClassLoader.class
1345 19 Sep 05 nicklas 552   */
1345 19 Sep 05 nicklas 553   private String classNameToPath(String className)
1345 19 Sep 05 nicklas 554   {
1345 19 Sep 05 nicklas 555     return className.replaceAll("\\.", "/")+".class";
1345 19 Sep 05 nicklas 556   }
1345 19 Sep 05 nicklas 557   
1345 19 Sep 05 nicklas 558   /**
1345 19 Sep 05 nicklas 559     Open the specified JAR file, list all entries and put them in the 
1345 19 Sep 05 nicklas 560     {@link #classPath} mapping.
1345 19 Sep 05 nicklas 561     @param file The JAR file to open
1345 19 Sep 05 nicklas 562     @param followClassPath If the MANIFEST file should be checked for
1345 19 Sep 05 nicklas 563       a <code>Class-Path</code> entry that lists other JAR files
1345 19 Sep 05 nicklas 564       that also should be checked.
1345 19 Sep 05 nicklas 565     @throws IOException If there is an error reading the JAR files
1345 19 Sep 05 nicklas 566   */
1345 19 Sep 05 nicklas 567   private void loadJarFile(File file, boolean followClassPath)
1345 19 Sep 05 nicklas 568     throws IOException
1345 19 Sep 05 nicklas 569   {
4391 12 Aug 08 nicklas 570     log.debug("loadJarFile: file=" + file);
5394 25 Aug 10 nicklas 571     JarInfo info = new JarInfo(file);
5394 25 Aug 10 nicklas 572     jarFiles.put(file, info);
4321 29 May 08 nicklas 573     if (!file.exists() || !file.isFile())
4321 29 May 08 nicklas 574     {
4389 12 Aug 08 nicklas 575       log.warn("File not found: " + file);
4389 12 Aug 08 nicklas 576       return;
4321 29 May 08 nicklas 577     }
1345 19 Sep 05 nicklas 578     JarFile jarFile = new JarFile(file);
6880 21 Apr 15 nicklas 579     try
1345 19 Sep 05 nicklas 580     {
6880 21 Apr 15 nicklas 581       Manifest manifest = jarFile.getManifest();
6880 21 Apr 15 nicklas 582       info.manifest = manifest;
6880 21 Apr 15 nicklas 583       Enumeration<JarEntry> entries = jarFile.entries();
6880 21 Apr 15 nicklas 584       while (entries.hasMoreElements())
5014 25 Jun 09 martin 585       {
6880 21 Apr 15 nicklas 586         JarEntry jarEntry = entries.nextElement();
6880 21 Apr 15 nicklas 587         if (jarEntry.isDirectory())
6880 21 Apr 15 nicklas 588         {
6880 21 Apr 15 nicklas 589           /* #### CONTINUE-STATEMENT #### */
6880 21 Apr 15 nicklas 590           continue;
6880 21 Apr 15 nicklas 591         }
6880 21 Apr 15 nicklas 592         String name = jarEntry.getName();
6880 21 Apr 15 nicklas 593         log.trace("Adding: " + name);
6880 21 Apr 15 nicklas 594         List<File> list = classPath.get(name);
6880 21 Apr 15 nicklas 595         if (list == null)
6880 21 Apr 15 nicklas 596         {
6880 21 Apr 15 nicklas 597           list = new ArrayList<File>();
6880 21 Apr 15 nicklas 598           classPath.put(name, list);
6880 21 Apr 15 nicklas 599         }
6880 21 Apr 15 nicklas 600         list.add(file);
5014 25 Jun 09 martin 601       }
6880 21 Apr 15 nicklas 602       if (followClassPath && manifest != null)
1345 19 Sep 05 nicklas 603       {
7192 29 Aug 16 nicklas 604         File directory = file.getParentFile();
6880 21 Apr 15 nicklas 605         Attributes attr = manifest.getMainAttributes();
6880 21 Apr 15 nicklas 606         if (attr != null)
1345 19 Sep 05 nicklas 607         {
6880 21 Apr 15 nicklas 608           String cp = attr.getValue(Attributes.Name.CLASS_PATH);
6880 21 Apr 15 nicklas 609           log.debug("Class path of JAR file '" + file + "': " + cp);
6880 21 Apr 15 nicklas 610           if (cp != null)
1345 19 Sep 05 nicklas 611           {
6880 21 Apr 15 nicklas 612             String[] cps = cp.split("\\s+");
6880 21 Apr 15 nicklas 613             for (int i = 0; i < cps.length; ++i)
1345 19 Sep 05 nicklas 614             {
6880 21 Apr 15 nicklas 615               if (cps[i] != null && !cps[i].trim().equals(""))
5622 03 May 11 nicklas 616               {
7192 29 Aug 16 nicklas 617                 File classPathFile = new File(directory, cps[i]);
6880 21 Apr 15 nicklas 618                 if (!classPathFile.exists())
5622 03 May 11 nicklas 619                 {
6880 21 Apr 15 nicklas 620                   log.debug("Referenced JAR file doesn't exist: '" + cps[i]);
6880 21 Apr 15 nicklas 621                   // See if the requested file exists under META-INF
6880 21 Apr 15 nicklas 622                   JarEntry entry = jarFile.getJarEntry("META-INF/" + cps[i]);
6880 21 Apr 15 nicklas 623                   if (entry != null)
5622 03 May 11 nicklas 624                   {
6880 21 Apr 15 nicklas 625                     log.debug("Extracting JAR from META-INF/" + cps[i] + " to jar.cache");
6880 21 Apr 15 nicklas 626                     // Extract it to userfiles directory (if we have to)
6880 21 Apr 15 nicklas 627                     classPathFile = new File(Application.getUserFilesDirectory(), 
6880 21 Apr 15 nicklas 628                         "jar.cache/" + file.getName() + "/" + classPathFile.getName());
6880 21 Apr 15 nicklas 629                     boolean exists = classPathFile.exists();
6880 21 Apr 15 nicklas 630                     boolean sameTime = exists && classPathFile.lastModified() == entry.getTime();
6880 21 Apr 15 nicklas 631                     boolean sameSize = exists && classPathFile.length() == entry.getSize();
6880 21 Apr 15 nicklas 632                     if (!exists || !sameTime || !sameSize)
5622 03 May 11 nicklas 633                     {
6880 21 Apr 15 nicklas 634                       classPathFile.getParentFile().mkdirs();
6880 21 Apr 15 nicklas 635                       InputStream in = null;
6880 21 Apr 15 nicklas 636                       OutputStream out = null;
6880 21 Apr 15 nicklas 637                       try
6880 21 Apr 15 nicklas 638                       {
6880 21 Apr 15 nicklas 639                         in = jarFile.getInputStream(entry);
6880 21 Apr 15 nicklas 640                         out = FileUtil.getOutputStream(classPathFile);
6880 21 Apr 15 nicklas 641                         FileUtil.copy(in, out);
6880 21 Apr 15 nicklas 642                       }
6880 21 Apr 15 nicklas 643                       finally
6880 21 Apr 15 nicklas 644                       {
6880 21 Apr 15 nicklas 645                         FileUtil.close(in);
6880 21 Apr 15 nicklas 646                         FileUtil.close(out);
6880 21 Apr 15 nicklas 647                       }
5622 03 May 11 nicklas 648                     }
5622 03 May 11 nicklas 649                   }
7192 29 Aug 16 nicklas 650                   loadJarFile(classPathFile, false);
5622 03 May 11 nicklas 651                 }
7192 29 Aug 16 nicklas 652                 else
7192 29 Aug 16 nicklas 653                 {
7192 29 Aug 16 nicklas 654                   proxyLoaders.add(new JarClassLoaderProxy(classPathFile.getAbsolutePath()));
7192 29 Aug 16 nicklas 655                 }
5622 03 May 11 nicklas 656               }
1345 19 Sep 05 nicklas 657             }
1345 19 Sep 05 nicklas 658           }
6880 21 Apr 15 nicklas 659           if (file == mainJarFile)
6880 21 Apr 15 nicklas 660           {
6880 21 Apr 15 nicklas 661             delegateFirst = Values.getBoolean(attr.getValue("X-Delegate-First"), delegateFirst);
6880 21 Apr 15 nicklas 662           }
1345 19 Sep 05 nicklas 663         }
1345 19 Sep 05 nicklas 664       }
1345 19 Sep 05 nicklas 665     }
6880 21 Apr 15 nicklas 666     finally
6880 21 Apr 15 nicklas 667     {
6880 21 Apr 15 nicklas 668       FileUtil.close(jarFile);
6880 21 Apr 15 nicklas 669     }
1345 19 Sep 05 nicklas 670   }
1345 19 Sep 05 nicklas 671   
1345 19 Sep 05 nicklas 672   /**
1343 15 Sep 05 nicklas 673     Load the byte[] of the given class. 
1343 15 Sep 05 nicklas 674     
1345 19 Sep 05 nicklas 675     @param file The JAR file in which the class implmentation is located
1343 15 Sep 05 nicklas 676     @param name The name of the class using regular naming convention,
1343 15 Sep 05 nicklas 677       ie. net.sf.basedb.util.JarClassLoader
1343 15 Sep 05 nicklas 678     @throws ClassNotFoundException If the class can't be loaded
1343 15 Sep 05 nicklas 679   */
1345 19 Sep 05 nicklas 680   private byte[] loadClassData(File file, String name)
1343 15 Sep 05 nicklas 681     throws ClassNotFoundException
1343 15 Sep 05 nicklas 682   {
4391 12 Aug 08 nicklas 683     log.debug("loadClassData: " + name + " from file: " + file);
1343 15 Sep 05 nicklas 684     InputStream in = null;
6880 21 Apr 15 nicklas 685     JarFile jarFile = null;
1343 15 Sep 05 nicklas 686     try
1343 15 Sep 05 nicklas 687     {
6880 21 Apr 15 nicklas 688       jarFile = new JarFile(file);
1343 15 Sep 05 nicklas 689       // Get the jar entry and open an input stream to read the file
1345 19 Sep 05 nicklas 690       JarEntry jarEntry = jarFile.getJarEntry(classNameToPath(name));
1345 19 Sep 05 nicklas 691       if (jarEntry == null)
1345 19 Sep 05 nicklas 692       {
1345 19 Sep 05 nicklas 693         throw new ClassNotFoundException(name+" in file "+file);
1345 19 Sep 05 nicklas 694       }
1345 19 Sep 05 nicklas 695       in = jarFile.getInputStream(jarEntry);
3994 22 Nov 07 enell 696       int classSize = (int)jarEntry.getSize();
3994 22 Nov 07 enell 697       byte[] b = new byte[classSize];
3994 22 Nov 07 enell 698       int totalRead = 0;
3994 22 Nov 07 enell 699       while (totalRead < classSize)
3994 22 Nov 07 enell 700       {
3994 22 Nov 07 enell 701         int numRead = in.read(b, totalRead, b.length - totalRead);
3994 22 Nov 07 enell 702         if (numRead == -1) break;
3994 22 Nov 07 enell 703         totalRead += numRead;
4391 12 Aug 08 nicklas 704         log.debug("loadClassData: size=" + classSize + "; loaded=" + totalRead);
3994 22 Nov 07 enell 705       }
3994 22 Nov 07 enell 706       if (totalRead != classSize) 
3994 22 Nov 07 enell 707       {
3994 22 Nov 07 enell 708         throw new ClassFormatError("Could not read the complete class for " + 
3994 22 Nov 07 enell 709           name + ": " + totalRead + " bytes; expected " + classSize + " bytes");
3994 22 Nov 07 enell 710       }
1343 15 Sep 05 nicklas 711       return b;
1343 15 Sep 05 nicklas 712     }
1343 15 Sep 05 nicklas 713     catch (Throwable ex)
1343 15 Sep 05 nicklas 714     {
1343 15 Sep 05 nicklas 715       throw new ClassNotFoundException(name, ex);
1343 15 Sep 05 nicklas 716     }
1343 15 Sep 05 nicklas 717     finally
1343 15 Sep 05 nicklas 718     {
5384 13 Aug 10 nicklas 719       FileUtil.close(in);
6880 21 Apr 15 nicklas 720       FileUtil.close(jarFile);
1343 15 Sep 05 nicklas 721     }
1343 15 Sep 05 nicklas 722   }
5394 25 Aug 10 nicklas 723   /**
5394 25 Aug 10 nicklas 724     Define the package with the given name using information from a 
5394 25 Aug 10 nicklas 725     manifest for vendor, title and version. The package should not
5394 25 Aug 10 nicklas 726     already exist.
5394 25 Aug 10 nicklas 727     @param name The name of the package
5394 25 Aug 10 nicklas 728     @param mf An optional manifest
5394 25 Aug 10 nicklas 729     @return A package
5394 25 Aug 10 nicklas 730     @since 2.16
5394 25 Aug 10 nicklas 731   */
5394 25 Aug 10 nicklas 732   private Package definePackage(String name, Manifest mf)
5394 25 Aug 10 nicklas 733   {
5394 25 Aug 10 nicklas 734     String specTitle = null;
5394 25 Aug 10 nicklas 735     String specVersion = null;
5394 25 Aug 10 nicklas 736     String specVendor = null;
5394 25 Aug 10 nicklas 737     String implTitle = null;
5394 25 Aug 10 nicklas 738     String implVersion = null;
5394 25 Aug 10 nicklas 739     String implVendor = null;
5394 25 Aug 10 nicklas 740     if (mf != null)
5394 25 Aug 10 nicklas 741     {
5394 25 Aug 10 nicklas 742       // Check if attributes are defined for the given package path
5394 25 Aug 10 nicklas 743       String path = name.replace('.', '/') + "/";
5394 25 Aug 10 nicklas 744       Attributes attr = mf.getAttributes(path);
5394 25 Aug 10 nicklas 745       if (attr != null) 
5394 25 Aug 10 nicklas 746       {
5394 25 Aug 10 nicklas 747         specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
5394 25 Aug 10 nicklas 748         specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
5394 25 Aug 10 nicklas 749         specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
5394 25 Aug 10 nicklas 750         implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
5394 25 Aug 10 nicklas 751         implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
5394 25 Aug 10 nicklas 752         implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
5394 25 Aug 10 nicklas 753       }
5394 25 Aug 10 nicklas 754       // Check global attributes
5394 25 Aug 10 nicklas 755       attr = mf.getMainAttributes();
5394 25 Aug 10 nicklas 756       if (attr != null) 
5394 25 Aug 10 nicklas 757       {
5394 25 Aug 10 nicklas 758           if (specTitle == null) specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
5394 25 Aug 10 nicklas 759           if (specVersion == null) specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
5394 25 Aug 10 nicklas 760           if (specVendor == null) specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
5394 25 Aug 10 nicklas 761           if (implTitle == null) implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
5394 25 Aug 10 nicklas 762           if (implVersion == null) implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
5394 25 Aug 10 nicklas 763           if (implVendor == null) implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
5394 25 Aug 10 nicklas 764       }
5394 25 Aug 10 nicklas 765     }
5394 25 Aug 10 nicklas 766     return definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, null);
5394 25 Aug 10 nicklas 767   }
4323 30 May 08 nicklas 768   
5013 25 Jun 09 nicklas 769   private Class<?> loadClassInternal(ClassLoader loader, String name)
5013 25 Jun 09 nicklas 770   {
5013 25 Jun 09 nicklas 771     Class<?> c = null;
5013 25 Jun 09 nicklas 772     if (loader != null)
5013 25 Jun 09 nicklas 773     {
5013 25 Jun 09 nicklas 774       try
5013 25 Jun 09 nicklas 775       {
5013 25 Jun 09 nicklas 776         c = loader.loadClass(name);
5013 25 Jun 09 nicklas 777       }
5013 25 Jun 09 nicklas 778       catch (ClassNotFoundException ex)
5013 25 Jun 09 nicklas 779       {}
5013 25 Jun 09 nicklas 780     }
5013 25 Jun 09 nicklas 781     return c;
5013 25 Jun 09 nicklas 782   }
5013 25 Jun 09 nicklas 783   
5013 25 Jun 09 nicklas 784   private void addResources(Vector<URL> resources, Enumeration<URL> e)
5013 25 Jun 09 nicklas 785   {
5013 25 Jun 09 nicklas 786     if (e == null) return;
5013 25 Jun 09 nicklas 787     while (e.hasMoreElements())
5013 25 Jun 09 nicklas 788     {
5013 25 Jun 09 nicklas 789       resources.add(e.nextElement());
5013 25 Jun 09 nicklas 790     }
5013 25 Jun 09 nicklas 791   }
5013 25 Jun 09 nicklas 792   
4323 30 May 08 nicklas 793   static class JarInfo
4323 30 May 08 nicklas 794   {
4323 30 May 08 nicklas 795     final File jarFile;
5394 25 Aug 10 nicklas 796     Manifest manifest;
4390 12 Aug 08 nicklas 797     final boolean existed;
4323 30 May 08 nicklas 798     final long lastModified;
4323 30 May 08 nicklas 799     final long size;
4323 30 May 08 nicklas 800     
4323 30 May 08 nicklas 801     JarInfo(File jarFile)
4323 30 May 08 nicklas 802     {
4323 30 May 08 nicklas 803       this.jarFile = jarFile;
4390 12 Aug 08 nicklas 804       this.existed = jarFile.exists();
4323 30 May 08 nicklas 805       this.size = jarFile.length();
4323 30 May 08 nicklas 806       this.lastModified = jarFile.lastModified();
4323 30 May 08 nicklas 807     }
4323 30 May 08 nicklas 808   }
7192 29 Aug 16 nicklas 809   
7192 29 Aug 16 nicklas 810   /**
7192 29 Aug 16 nicklas 811     A proxy class loader is typically needed when one extension
7192 29 Aug 16 nicklas 812     depends on another extension. The proxy is needed since
7192 29 Aug 16 nicklas 813     we want to use lazy initialization of the other class loaders.
7192 29 Aug 16 nicklas 814     They may not be needed immediately. The proxy will also make it
7192 29 Aug 16 nicklas 815     possible to avoid infinite recursion in case two extensions 
7192 29 Aug 16 nicklas 816     depend on each other.
7192 29 Aug 16 nicklas 817     <p>
7192 29 Aug 16 nicklas 818     
7192 29 Aug 16 nicklas 819     Once a real class loader has been aquired it will be kept 
7192 29 Aug 16 nicklas 820     until the main class loader is discared. Note that the 
7192 29 Aug 16 nicklas 821     {@link JarClassLoader#hasChanged(boolean)} is not used for 
7192 29 Aug 16 nicklas 822     automatic reloading since that may cause class-cast exceptions.
7192 29 Aug 16 nicklas 823     <p>
7192 29 Aug 16 nicklas 824     
7192 29 Aug 16 nicklas 825     The proxy is also used to limit the search for classes to 
7192 29 Aug 16 nicklas 826     classes that are directly handled directly by the other class 
7192 29 Aug 16 nicklas 827     loader. The system and parent class loaders are not searched again
7192 29 Aug 16 nicklas 828     since they are already searched in the main class loader.
7192 29 Aug 16 nicklas 829
7192 29 Aug 16 nicklas 830     @since 3.10
7192 29 Aug 16 nicklas 831   */
7192 29 Aug 16 nicklas 832   static class JarClassLoaderProxy
7192 29 Aug 16 nicklas 833   {
7192 29 Aug 16 nicklas 834     
7192 29 Aug 16 nicklas 835     final String jarPath;
7192 29 Aug 16 nicklas 836     private JarClassLoader loader;
7192 29 Aug 16 nicklas 837     private boolean isInitialized;
7192 29 Aug 16 nicklas 838     
7192 29 Aug 16 nicklas 839     /**
7192 29 Aug 16 nicklas 840       Creates a proxy class loader for the given JAR file.
7192 29 Aug 16 nicklas 841     */
7192 29 Aug 16 nicklas 842     JarClassLoaderProxy(String jarPath)
7192 29 Aug 16 nicklas 843     {
7192 29 Aug 16 nicklas 844       this.jarPath = jarPath;
7192 29 Aug 16 nicklas 845       this.isInitialized = false;
7192 29 Aug 16 nicklas 846     }
7192 29 Aug 16 nicklas 847     
7192 29 Aug 16 nicklas 848     /**
7192 29 Aug 16 nicklas 849       Initialize the proxy.
7192 29 Aug 16 nicklas 850     */
7192 29 Aug 16 nicklas 851     private synchronized void init()
7192 29 Aug 16 nicklas 852     {
7192 29 Aug 16 nicklas 853       if (isInitialized) return;
7192 29 Aug 16 nicklas 854       if (log.isDebugEnabled()) log.debug("JarClassLoaderProxy.init[" + jarPath + "]");
7192 29 Aug 16 nicklas 855       
7192 29 Aug 16 nicklas 856       try
7192 29 Aug 16 nicklas 857       {
7192 29 Aug 16 nicklas 858         loader = (JarClassLoader)JarClassLoader.getInstance(jarPath, true);
7192 29 Aug 16 nicklas 859       }
7192 29 Aug 16 nicklas 860       catch (IOException ex)
7192 29 Aug 16 nicklas 861       {}
7192 29 Aug 16 nicklas 862       isInitialized = true;
7192 29 Aug 16 nicklas 863     }
7192 29 Aug 16 nicklas 864     
7192 29 Aug 16 nicklas 865     
7192 29 Aug 16 nicklas 866     Class<?> findClass(String name)
7192 29 Aug 16 nicklas 867     {
7192 29 Aug 16 nicklas 868       if (log.isDebugEnabled()) log.debug("JarClassLoaderProxy.findClass[" + jarPath + "]: " + name);
7192 29 Aug 16 nicklas 869       if (!isInitialized) init();
7192 29 Aug 16 nicklas 870       
7192 29 Aug 16 nicklas 871       Class<?> c = null;
7192 29 Aug 16 nicklas 872       if (loader != null)
7192 29 Aug 16 nicklas 873       {
7192 29 Aug 16 nicklas 874         try
7192 29 Aug 16 nicklas 875         {
7192 29 Aug 16 nicklas 876           // 1. Check if the class has already been loaded
7192 29 Aug 16 nicklas 877           c = loader.findLoadedClass(name);
7192 29 Aug 16 nicklas 878           if (log.isDebugEnabled()) log.debug("JarClassLoaderProxy.findClass[" + jarPath + "]: loaded class (" + name + "): " + c);
7192 29 Aug 16 nicklas 879           
7192 29 Aug 16 nicklas 880           // 2. Otherwise try to load the class without searching parent or system class loaders
7192 29 Aug 16 nicklas 881           if (c == null)
7192 29 Aug 16 nicklas 882           {
7192 29 Aug 16 nicklas 883             c = loader.findClass(name);
7192 29 Aug 16 nicklas 884             if (log.isDebugEnabled()) log.debug("JarClassLoaderProxy.findClass[" + jarPath + "]: found class (" + name + "): " + c);
7192 29 Aug 16 nicklas 885           }
7192 29 Aug 16 nicklas 886         }
7192 29 Aug 16 nicklas 887         catch (ClassNotFoundException ex)
7192 29 Aug 16 nicklas 888         {}
7192 29 Aug 16 nicklas 889       }
7192 29 Aug 16 nicklas 890       return c;
7192 29 Aug 16 nicklas 891     }
7196 30 Aug 16 nicklas 892     
7196 30 Aug 16 nicklas 893     URL findResource(String name)
7196 30 Aug 16 nicklas 894     {
7196 30 Aug 16 nicklas 895       if (log.isDebugEnabled()) log.debug("JarClassLoaderProxy.findResource[" + jarPath + "]: " + name);
7196 30 Aug 16 nicklas 896       if (!isInitialized) init();
7196 30 Aug 16 nicklas 897       
7196 30 Aug 16 nicklas 898       URL url = null;
7196 30 Aug 16 nicklas 899       if (loader != null)
7196 30 Aug 16 nicklas 900       {
7196 30 Aug 16 nicklas 901         url = loader.findResource(name);
7196 30 Aug 16 nicklas 902       }
7196 30 Aug 16 nicklas 903       return url;
7196 30 Aug 16 nicklas 904     }
7196 30 Aug 16 nicklas 905     
7196 30 Aug 16 nicklas 906     Enumeration<URL> findResources(String name)
7196 30 Aug 16 nicklas 907     {
7196 30 Aug 16 nicklas 908       if (log.isDebugEnabled()) log.debug("JarClassLoaderProxy.findResources[" + jarPath + "]: " + name);
7196 30 Aug 16 nicklas 909       if (!isInitialized) init();
7196 30 Aug 16 nicklas 910       
7196 30 Aug 16 nicklas 911       Enumeration<URL> url = null;
7196 30 Aug 16 nicklas 912       if (loader != null)
7196 30 Aug 16 nicklas 913       {
7196 30 Aug 16 nicklas 914         url = loader.findResources(name);
7196 30 Aug 16 nicklas 915       }
7196 30 Aug 16 nicklas 916       return url;
7196 30 Aug 16 nicklas 917     }
7196 30 Aug 16 nicklas 918     
7196 30 Aug 16 nicklas 919     boolean hasChanged()
7196 30 Aug 16 nicklas 920     {
7196 30 Aug 16 nicklas 921       if (!isInitialized) return false;
7196 30 Aug 16 nicklas 922       if (loader != null) return loader.hasChanged(true);
7196 30 Aug 16 nicklas 923       return new File(jarPath).exists();
7196 30 Aug 16 nicklas 924     }
7192 29 Aug 16 nicklas 925   }
1343 15 Sep 05 nicklas 926 }