Is the Java classpath final after JVM startup?

后端 未结 7 692
灰色年华
灰色年华 2021-02-02 11:11

I have read a lot about the Java class loading process lately. Often I came across texts that claimed that it is not possible to add classes to the classpath during runtime and

7条回答
  •  不要未来只要你来
    2021-02-02 11:36

    Bottom line: it is possible to add entries to the system classpath at runtime, and is shown how. This, however, has irreversible side-effects and relies on Sun JVM implementation details.


    Class path is final, in the most literal sense:

    The system class loader (the one that loads from the main class path) is sun.misc.Launcher$AppClassLoader in rt.jar.

    rt.jar:sun/misc/Launcher.class (sources are generated with Java Decompiler):

    public class Launcher
    {
     <...>
     static class AppClassLoader
        extends URLClassLoader
      {
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
    <...>
    

    rt.jar:sun/misc/URLClassLoader.class:

    protected Class findClass(final String paramString)
        throws ClassNotFoundException
      {
        <...>
              String str = paramString.replace('.', '/').concat(".class");
              Resource localResource = URLClassLoader.this.ucp.getResource(str, false);
      <...>
    

    But, even if the field is final, this doesn't mean we can't mutate the object itself if we somehow get access to it. The field is without an access modifier - which means, we can access it if only we make the call from the same package. (the following is IPython with JPype; the commands are readable enough to easily derive their Java counterparts)

    #jpype doesn't automatically add top-level packages except `java' and `javax'
    In [28]: jpype.sun=jpype._jpackage.JPackage("sun")
    
    In [32]: jpype.sun.misc.Launcher
    Out[32]: jpype._jclass.sun.misc.Launcher
    
    In [35]: jpype.sun.misc.Launcher.getLauncher().getClassLoader()
    Out[35]:     
    
    In [36]: acl=_
    
    In [37]: acl.ucp
    Out[37]: 
    
    In [48]: [u.toString() for u in acl.ucp.getURLs()]
    Out[48]: [u'file:/C:/Documents%20and%20Settings/User/']
    

    Now, URLClassPath has a public addURL method. Let's try it out and see what happens:

    #normally, this is done with Launcher.getFileURL but we can't call it directly
    #public static URLClassPath.pathToURLs also does the same, but it returns an array
    In [72]: jpype.sun.net.www.ParseUtil.fileToEncodedURL(
                 jpype.java.io.File(r"c:\Ivan\downloads\dom4j-2.0.0-RC1.jar")
                 .getCanonicalFile())
    Out[72]: 
    
    In [73]: _.toString()
    Out[73]: u'file:/C:/Ivan/downloads/dom4j-2.0.0-RC1.jar'
    
    In [74]: acl.ucp.addURL(_72)
    
    In [75]: [u.toString() for u in acl.ucp.getURLs()]
    Out[75]:
    [u'file:/C:/Documents%20and%20Settings/User/',
     u'file:/C:/Ivan/downloads/dom4j-2.0.0-RC1.jar']
    

    Now, let's try to load some class from the .jar:

    In [78]: jpype.org=jpype._jpackage.JPackage("org")
    
    In [79]: jpype.org.dom4j.Entity
    Out[79]: jpype._jclass.org.dom4j.Entity 
    

    Success!

    This will probably fail from a sandbox or such where there are custom class loaders or security settings in the way (AppClassLoader.loadClass does security checks before calling super).

    Further code inspection shows that addURL also disables the URLClassPath's lookup cache (implemented in a few native methods), and this is irreversible. Initially, the lookupCacheEnabled flag is set to the value of the sun.cds.enableSharedLookupCache system property.

    The interface provides no way to edit the entries. URLs are added to URLClassPath's private ArrayList path and Stack urls. urls is accessible, but it turns out, it only holds entries temporarily, before it's attempted to load from it, at which point the information moves to HashMap lmap and ArrayList loaders. getURLs() returns a copy of path. So, it's theoretically possible to edit it by hacking the accessible fields, but it's nowhere near reliable and won't affect getURLs result.

提交回复
热议问题