Uploaded image for project: 'EJBCA'
  1. EJBCA
  2. ECA-7370

ServiceManifestBuilder does not run with Java 11

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Minor
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: EJBCA 7.0.0
    • Component/s: None
    • Labels:
      None

      Description

      NOTE: relates to the project in svn:trunk/servicemanifestbuilder, which is built separately and then included in EJBCA as lib/ext/servicemanifestbuilder*.jar, and used during ant build to populate META-INF/services automagically with interfaces.

       

      The following code in ServiceManifestBuilder.java related to classloading does not work with Java 11.

      URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();

       

      The system classloader is not a URLClassLoader anymore, and you can not modify the system class loader.

      https://stackoverflow.com/questions/46694600/java-9-compatability-issue-with-classloader-getsystemclassloader

       

      Here is the patch. Since ServiceManifestBuilder is in its own project I'll paste the patch here as well.

       

      Index: src-test/com/primekey/anttools/ServiceManifestBuilderTest.java
      ===================================================================
      --- src-test/com/primekey/anttools/ServiceManifestBuilderTest.java	(revision 30003)
      +++ src-test/com/primekey/anttools/ServiceManifestBuilderTest.java	(working copy)
      @@ -104,7 +104,7 @@
           public void testBuildServiceManifestToClassPath() throws IOException, ClassNotFoundException {
               Class<?> dummyInterfaceClass = Class.forName(DUMMY_PACKAGE + "." + FIRST_DUMMY_INTERFACE_NAME);
               //Phew, it won't get uglier than this at least. 
      -        ServiceManifestBuilder.buildServiceManifestToLocation(temporarySourceDirectory, dummyInterfaceClass);
      +        ServiceManifestBuilder.buildServiceManifestToLocation(temporarySourceDirectory, Thread.currentThread().getContextClassLoader(), dummyInterfaceClass);
               //Loverly. We should now have a manifest file on the classpath
               ServiceLoader<?> serviceLoader = ServiceLoader.load(dummyInterfaceClass);
               Iterator<?> serviceIterator = serviceLoader.iterator();
      @@ -122,7 +122,7 @@
               Class<?> secondDummyInterfaceClass = Class.forName(DUMMY_PACKAGE + "." + SECOND_DUMMY_INTERFACE_NAME);
       
               //Phew, it won't get uglier than this at least. 
      -        ServiceManifestBuilder.buildServiceManifestToLocation(temporarySourceDirectory, firstDummyInterfaceClass, secondDummyInterfaceClass);
      +        ServiceManifestBuilder.buildServiceManifestToLocation(temporarySourceDirectory, Thread.currentThread().getContextClassLoader(), firstDummyInterfaceClass, secondDummyInterfaceClass);
               //Loverly. We should now have a manifest file on the classpath
               {
                   ServiceLoader<?> serviceLoader = ServiceLoader.load(firstDummyInterfaceClass);
      Index: src/com/primekey/anttools/ServiceManifestBuilder.java
      ===================================================================
      --- src/com/primekey/anttools/ServiceManifestBuilder.java	(revision 30084)
      +++ src/com/primekey/anttools/ServiceManifestBuilder.java	(working copy)
      @@ -80,11 +80,11 @@
               }
               //Make sure that the directory to be modified is on the classpath
               // SInce Java 11 the systemclassloader is not a URLClassLoader, and we can not modify the system class loader
      -        // We have to set our own URL classloader in the currentThread and use that, java.util.ServiceLoader uses the thread's ClassLoader context
      +        // We have to use our own URL classloader and use that when we do Class.forName()
      +        URLClassLoader urlloader;
               try {
                   System.err.println("Setting URL to classloader: "+archive.toURI().toURL());
      -            URLClassLoader urlloader = new URLClassLoader(new URL[]{archive.toURI().toURL()});
      -            Thread.currentThread().setContextClassLoader(urlloader);
      +            urlloader = new URLClassLoader(new URL[]{archive.toURI().toURL()});
               } catch (Throwable t) {
                   throw new RuntimeException("Exception caught while trying to modify classpath for ServiceLoader", t);
               }
      @@ -93,14 +93,14 @@
               Class<?>[] classes = new Class<?>[classNames.length];
               for (int i = 0; i < classNames.length; ++i) {
                   try {
      -                classes[i] = Class.forName(classNames[i], true, Thread.currentThread().getContextClassLoader());
      +                classes[i] = Class.forName(classNames[i], true, urlloader);
                   } catch (ClassNotFoundException e) {
                       System.err.println("Class " + classNames[i] + " not found on classpath, cannot continue.");
                       return 1;
                   }
               }
               try {
      -            buildServiceManifestToLocation(archive, classes);
      +            buildServiceManifestToLocation(archive, urlloader, classes);
               } catch (IOException e) {
                   System.err.println("Disk related error occured while building manifest, see following stacktrace");
                   e.printStackTrace();
      @@ -148,10 +148,11 @@
            * 
            * @param location a writable location where a META-INF/services directory will be created (if not existing) and a manifest file placed,
            *                 and where suitable class files will be searched for.
      +     * @param classLoader the java classloader to use to look for the class with Class.forName
            * @param interfaceClass the interface to base the manifest on
            * @throws IOException for any file related errors
            */
      -    public static void buildServiceManifestToLocation(File location, Class<?>... interfaceClasses) throws IOException {
      +    public static void buildServiceManifestToLocation(File location, ClassLoader classLoader, Class<?>... interfaceClasses) throws IOException {
               if (!location.isDirectory()) {
                   throw new IOException("File " + location + " was not a directory.");
               }
      @@ -162,7 +163,7 @@
                   if (!interfaceClass.isInterface() && ! Modifier.isAbstract( interfaceClass.getModifiers())) {
                       throw new IllegalArgumentException("Class " + interfaceClass.getName() + " was not an interface or an asbtract class.");
                   }
      -            List<Class<?>> implementingClasses = getImplementingClasses(location, location, interfaceClass);
      +            List<Class<?>> implementingClasses = getImplementingClasses(location, location, interfaceClass, classLoader);
                   System.out.println("Added " + implementingClasses.size() + " implementations of " + interfaceClass.getName());
                   File metaInf = new File(location, META_INF);
                   if (!metaInf.exists()) {
      @@ -200,17 +201,18 @@
            * @param baseLocation File from which the search was started
            * @param location local file in which to search
            * @param interfaceClass an interface to search for
      +     * @param classLoader the java classloader to use to look for the class with Class.forName
            * @return a list of classes implementing the given interface
            * @throws IOException 
            */
      -    private static List<Class<?>> getImplementingClasses(final File baseLocation, File location, Class<?> interfaceClass) {
      +    private static List<Class<?>> getImplementingClasses(final File baseLocation, File location, Class<?> interfaceClass, ClassLoader classLoader) {
               final List<Class<?>> result = new ArrayList<>();
               if (location.isDirectory()) {
                   //Recurse to find all files in all subdirectories
                   final int baseLocationAbsolutePathLength = baseLocation.getAbsolutePath().length() + File.separator.length();
                   for (final File file : location.listFiles()) {
                       if (file.isDirectory()) {
      -                    result.addAll(getImplementingClasses(baseLocation, file, interfaceClass));
      +                    result.addAll(getImplementingClasses(baseLocation, file, interfaceClass, classLoader));
                       } else {
                           final String absolutePath = file.getAbsolutePath();
                           final int indexOfExtension = absolutePath.indexOf(CLASS_EXTENSION);
      @@ -218,7 +220,7 @@
                               final String className = absolutePath.substring(baseLocationAbsolutePathLength, indexOfExtension).replace(File.separatorChar, '.');
                               try {
                                   // We set the "initialize" parameter to false here, because initialization can also pull in a lot of other classes and isn't needed anyway (ECA-5501)
      -                            final Class<?> candidate = Class.forName(className, false, Thread.currentThread().getContextClassLoader());
      +                            final Class<?> candidate = Class.forName(className, false, classLoader);
                                   if (interfaceClass.isAssignableFrom(candidate) && !Modifier.isAbstract(candidate.getModifiers())
                                           && !candidate.isInterface()) {                              
                                       result.add(candidate);
       
      

       

        Attachments

          Activity

            People

            Assignee:
            tomas Tomas Gustavsson
            Reporter:
            tomas Tomas Gustavsson
            Verified by:
            Henrik Sunmark
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:

                Time Tracking

                Estimated:
                Original Estimate - 2 hours
                2h
                Remaining:
                Time Spent - 1 hour, 15 minutes Remaining Estimate - 45 minutes
                45m
                Logged:
                Time Spent - 1 hour, 15 minutes Remaining Estimate - 45 minutes
                1h 15m