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 ----------
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 ----------