Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-26579

j:getStatic/invokeStatic does not work on plugin classes

      If in j:getStatic or j:invokeStatic you specify a className which is in a plugin, then try to render the resulting Jelly view, you get

      java.lang.ClassNotFoundException: some.Class
      	at org.jenkinsci.maven.plugins.hpi.ServletApiOnlyClassLoader.findClass(ServletApiOnlyClassLoader.java:25)
      	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
      	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
      	at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:363)
      	at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:325)
      	at org.apache.commons.jelly.tags.core.GetStaticTag.doTag(GetStaticTag.java:106)
      

      Indeed GetStaticTag does

      Class type = ClassLoaderUtils.getClassLoader(getClass()).loadClass(className);
      

      which calls

      public static ClassLoader getClassLoader(Class clazz) {
          ClassLoader callersLoader = clazz.getClassLoader();
          if (callersLoader == null) {
              callersLoader = ClassLoader.getSystemClassLoader();
          }
          return callersLoader;
      }
      

      so there is no way this could work.

      Workaround:

      ${app.pluginManager.uberClassLoader.loadClass('some.Class').getField('field').get(null)}
      ${app.pluginManager.uberClassLoader.loadClass('pkg.SomeExtension').getMethod('all', null).invoke(null, null)}
      

          [JENKINS-26579] j:getStatic/invokeStatic does not work on plugin classes

          Jesse Glick added a comment -

          Other workarounds:

          • Use a Groovy view.
          • Avoid static calls by arranging to pass in an instance of this or a related class (for example as it) so you can make instance calls.

          Jesse Glick added a comment - Other workarounds: Use a Groovy view. Avoid static calls by arranging to pass in an instance of this or a related class (for example as it ) so you can make instance calls.

          In the version of commons-jelly that we use in the core (org.jenkins-ci:commons-jelly:jar:1.1-jenkins-20120928), the loadClass method looks like this:

              /**
               * Loads the given class using the current Thread's context class loader first
               * otherwise use the class loader which loaded this class.
               */
              public static Class loadClass(String className, Class callingClass) throws ClassNotFoundException {
                  ClassLoader loader = Thread.currentThread().getContextClassLoader();
                  if (loader == null) {
                      return getClassLoader(callingClass).loadClass(className);
                  } else {
                      return loader.loadClass(className);
                  }
              }
          

          ... so while I haven't verified whether we set context classloader, it seems like we've made an attempt to get this done correctly.

          Kohsuke Kawaguchi added a comment - In the version of commons-jelly that we use in the core (org.jenkins-ci:commons-jelly:jar:1.1-jenkins-20120928), the loadClass method looks like this: /** * Loads the given class using the current Thread 's context class loader first * otherwise use the class loader which loaded this class. */ public static Class loadClass( String className, Class callingClass) throws ClassNotFoundException { ClassLoader loader = Thread .currentThread().getContextClassLoader(); if (loader == null ) { return getClassLoader(callingClass).loadClass(className); } else { return loader.loadClass(className); } } ... so while I haven't verified whether we set context classloader, it seems like we've made an attempt to get this done correctly.

          Jesse Glick added a comment -

          Similar problem seems to have hit svanoort when (unnecessarily) using the class argument to st:include and specifying a String argument: worked during hpi:run but not in production mode. (Using a Class argument does work.)

          Jesse Glick added a comment - Similar problem seems to have hit svanoort when (unnecessarily) using the class argument to  st:include  and specifying a String argument: worked during hpi:run but not in production mode. (Using a Class argument does work .)

          Jesse Glick added a comment -

          so while I haven't verified whether we set context classloader

          AFAICT we do not set Thread.contextClassLoader to UberClassLoader; currently it is WebAppClassLoader. Would be helpful for some purposes, but risky.

          Jesse Glick added a comment - so while I haven't verified whether we set context classloader AFAICT we do not set Thread.contextClassLoader to UberClassLoader ; currently it is WebAppClassLoader . Would be helpful for some purposes, but risky.

          Jesse Glick added a comment -

          Also cf. JENKINS-41827 for mismatches between production and JenkinsRule testing.

          Jesse Glick added a comment - Also cf.  JENKINS-41827 for mismatches between production and JenkinsRule testing.

          Jesse Glick added a comment -

          JENKINS-31203 would obviate this sort of problem and make rendering more easily debugged.

          Jesse Glick added a comment - JENKINS-31203 would obviate this sort of problem and make rendering more easily debugged.

            Unassigned Unassigned
            jglick Jesse Glick
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: