Reviewer and committer request for 7198496

David Holmes david.holmes at oracle.com
Thu Oct 4 10:37:19 UTC 2012


Paul,

On 4/10/2012 7:53 PM, Paul Sandoz wrote:
> On Oct 4, 2012, at 11:34 AM, David Holmes<david.holmes at oracle.com>  wrote:
>>>
>>>> These parts of the javadoc for get/setContextClassLoader are simply wrong.
>>>>
>>>
>>> Wrong or not because of such JavaDoc developers have been coding using TCCL with certain expectations of "null" different to that of just meaning the bootstrap CL.
>>>
>>> How would you propose to fix it?
>>
>> I don't see anything that actually needs fixing (apart from the javadoc). Anyone using getCCL has to expect null as a possibility and they are then free to proceed however they like. The obvious ways to proceed are as I outlined earlier:
>> - use current classloader
>> - use system loader
>> - use bootstrap loader
>>
>
> So you are proposing to widen the scope? since i interpret the current JavaDoc to correspond to only the latter two options.

I think you are missing the point. If getCCL returns null it returns 
null - end of story. What the caller of getCCL does after that is their 
business, it has nothing to do with the spec for getCCL. As Chris said 
getCCL can suggest that if null is returned then the caller might use 
the system or bootloader but that is all it is - a suggestion.

The issue here is what ServiceLoader says. First load(Class, 
ClassLoader) states:

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

which has the same language as used for the CCL. However here it is 
mostly well defined: if the passed in loader is null then use the system 
loader. The "or failing that, the bootstrap class loader" seems 
redundant - as the system loader (unless overridden I suppose) will 
delegate to the bootstrap loader first, there would be no point in then 
trying the bootstrap loader directly. But conservatively, if the system 
loader had been overridden you might also try the bootloader just in 
case. (But the implementation does not do that.)

Then load(Class) says:

/**
  * Creates a new service loader for the given service type, using the
  * current thread's {@linkplain java.lang.Thread#getContextClassLoader
  * context class loader}.

This is fine. If the CCL is null then the semantics for 
load(service,null) come into play.

So now we come to code in question:

private ServiceLoader(Class<S> svc, ClassLoader cl) {
     service = Objects.requireNonNull(svc, "Service interface cannot be 
null");
    loader = cl;
    reload();
}

No problem so far - loader can be null. The question is then how it is 
used later ...

    if (loader == null)
       configs = ClassLoader.getSystemResources(fullName);
    else
        configs = loader.getResources(fullName);

So the question now is what does ClassLoader.getSystemResources do? It says:

"Finds all resources of the specified name from the search path used to 
load classes. "

That sounds to me like it uses the system classloader, which in turn 
would first delegate to the bootstrap loader. All good. But we also have:

  try {
     S p = service.cast(Class.forName(cn, true, loader)
                 .newInstance());

and there is the bug because if you pass null to forName it will use the 
bootstrap loader, not the system loader. So it seems to me that a fix 
here is to just change the above to:

    Class.forName(cn, true, loader != null ? loader : 
Classloader.getSystemClassLoader())

Instead you have opted to deal with a null loader at construction time:

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

which has the same semantic effect, but leaves some "dead" code 
internally as now loader can not be null.

David




> Paul.



More information about the core-libs-dev mailing list