Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-7198496

(sl) ServiceLoader.load(Class, null) behavior differs from spec

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P4
    • 8
    • 7
    • core-libs
    • b63
    • x86
    • linux
    • Verified

    Description

      FULL PRODUCT VERSION :
      java version "1.7.0_07"
      Java(TM) SE Runtime Environment (build 1.7.0_07-b10)
      Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)


      ADDITIONAL OS VERSION INFORMATION :
      Linux peterl.marand.si 3.5.3-1.fc17.x86_64 #1 SMP Wed Aug 29 18:46:34 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux


      A DESCRIPTION OF THE PROBLEM :
      There is a bug in or inconsisntecy between javadoc and java.util.ServiceLoader.load(Class service, ClassLoader loader) method.

      The javadoc for java.util.ServiceLoader.load(Class service, ClassLoader loader) method says about it's parameters:

           * @param service
           * The interface or abstract class representing the service
           *
           * @param loader
           * The class loader to be used to load provider-configuration files
           * and provider classes, or <tt>null</tt> if the system class
           * loader (or, failing that, the bootstrap class loader) is to be
           * used

      So one might think that calling:

          ServiceLoader.load(service, null);

      is equivalent to:

          ServiceLoader.load(service, ClassLoader.getSystemClassLoader());


      But it is not.

      If null is specified as ClassLoader then ServiceLoader locates META-INF/services/interface.name resources using system ClassLoader (using ClassLoader.getSystemResources), but loads implementation classes using bootstrap ClassLoader (using Class.forName(className, true, null)).

      If this is not the expected behaviour then the fix is simple (in the private constructor of ServiceLoader[217]):

      - loader = cl;
      + loader = cl == null ? ClassLoader.getSystemClassLoader() : cl;


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      // create the following interface: serviceloader/Service.java

      package serviceloader;
      public interface Service { }


      // and the following implementation: serviceloader/ServiceImpl.java

      package serviceloader;
      public class ServiceImpl implements Service { }


      // and the following resource: META-INF/services/serviceloader.Service

      serviceloader.ServiceImpl


      // and the folowing test: serviceloader/Test.java


      package serviceloader;

      import java.util.ServiceLoader;

      public class Test
      {
          public static void main(String[] args)
          {
              Service service = ServiceLoader.load(Service.class, null).iterator().next();
              System.out.println(service);
          }
      }


      // compile all 3 sources and put the corresponding resource together with classes to
      // the java classpath and run the test:

      java -cp path_to_classes_and_resource serviceloader.Test


      This will produce:


      Exception in thread "main" java.util.ServiceConfigurationError: serviceloader.Service: Provider serviceloader.ServiceImpl not found
      at java.util.ServiceLoader.fail(ServiceLoader.java:231)
      at java.util.ServiceLoader.access$400(ServiceLoader.java:181)
      at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:367)
      at java.util.ServiceLoader$1.next(ServiceLoader.java:438)
      at serviceloader.Test.main(Test.java:9)


      If you exchange ServiceLoader.load(Service.class, null) with ServiceLoader.load(Service.class, ClassLoader.getSystemClassLoader()) the class is found and the test prints:

      serviceloader.ServiceImpl@c3b5587


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      calling ServiceLoader.load(Service.class, null) should be equivalent with ServiceLoader.load(Service.class, ClassLoader.getSystemClassLoader()) or the difference should be documented in javadoc
      ACTUAL -
      the javadoc and implementation differ

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      // create the following interface: serviceloader/Service.java

      package serviceloader;
      public interface Service { }


      // and the following implementation: serviceloader/ServiceImpl.java

      package serviceloader;
      public class ServiceImpl implements Service { }


      // and the following resource: META-INF/services/serviceloader.Service

      serviceloader.ServiceImpl


      // and the folowing test: serviceloader/Test.java


      package serviceloader;

      import java.util.ServiceLoader;

      public class Test
      {
          public static void main(String[] args)
          {
              Service service = ServiceLoader.load(Service.class, null).iterator().next();
              System.out.println(service);
          }
      }

      ---------- END SOURCE ----------

      Attachments

        Activity

          People

            psandoz Paul Sandoz
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: