Accessibility of nonexported types

Jesse Glick jesse.glick at oracle.com
Tue Dec 27 11:31:40 PST 2011


On 12/27/2011 12:48 PM, David M. Lloyd wrote:
> If we have a module-level accessibility modifier, then providing an additional check for non-exported public classes

If we have a module-level accessibility modifier - or just treat default access this way (cf. thread I renamed "Module accessibility") - then you could argue that a 
non-exported public class should be an oxymoron: public should really mean public.

> impossible for users to provide a public class under a non-exported package (think again of frameworks, JavaBeans spec etc.)

I guess the use case you are thinking of is something like Introspector.getBeanInfo(Class) where the bean is in an exported package but the BeanInfo is in a nonexported 
package in the search path. The case of a framework which loads a class reflectively from another module's ClassLoader, say to interpret some XML configuration file it 
has found in that module, is similar; in that case the module may be providing some sort of service but not intending to expose any direct API. Here the framework is 
trusted to only perform reflection which the module's author expected; you would expect it to not access unrelated elements in your module, and you would not want other 
modules to reflectively access your classes (even those mentioned in the configuration file). Trusting the framework means it can use setAccessible(true) and assume 
corresponding permissions from a security manager, though there is the compatibility issue of existing frameworks which do not yet use setAccessible (many do).

The question is whether a module system ought to have an accessibility level which basically means "inaccessible at compile time but accessible at runtime", contrary to 
the requirement of fidelity between phases.


A broader question is how to indicate in the language that a class or class member is expected to be used reflectively. This not only affects security; such members are 
also unsafe to refactor in certain ways, whether by IDEs or obfuscators. [1] discusses this in detail and proposes an @Opaque metaannotation. In conjunction with a module 
system, accessibility rules could be relaxed so that an opaquely annotated member would be considered accessible to the module defining the opaque annotation (i.e. the 
framework) without needing to call setAccessible. Thus

---%<--- org.runtasks/src/module-info.java
module org.runtasks {
   exports org.runtasks;
   provides service javax.annotation.processing.Processor with org.runtasks.internal.TaskProc;
}
---%<--- org.runtasks/src/org/runtasks/Task.java
package org.runtasks;
@Opaque(Opacity.SIGNATURE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Task {}
---%<--- org.runtasks/src/org/runtasks/Tasks.java
package org.runtasks;
public class Tasks {
   public static void runAll() {
     for (Module m : ...find loaded modules...) {
       URL xml = m.getClassLoader().findResource("META-INF/tasks.xml");
       if (xml == null) continue;
       for (String clazz : ...parse XML...) {
         ((Runnable) m.getClassLoader().loadClass(clazz).newInstance()).run();
       }
     }
   }
}
---%<--- org.runtasks/src/org/runtasks/internal/TaskProc.java
package org.runtasks.internal;
@SupportedAnnotationTypes("org.runtasks.Task")
public class TaskProc extends AbstractProcessor {...create tasks.xml...}
---%<--- com.stuff/src/module-info.java
module com.stuff {
   requires org.runtasks;
   // no exports
}
---%<--- com.stuff/src/com/stuff/MyTask.java
package com.stuff;
@org.runtasks.Task
/* not public */class MyTask implements Runnable {
   @Override public void run() {...}
}
---%<--- com.stuff/build/META-INF/tasks.xml
<!-- generated by TaskProc -->
<tasks>
   <task class="com.stuff.MyTask"/>
</tasks>
---%<---

could work under a security manager without giving org.runtasks nondefault permissions, without letting other modules load MyTask directly, or even letting org.runtasks 
load unrelated classes in com.stuff. Furthermore, com.stuff.jmod could be safely passed through a compliant obfuscator without any manual configuration, since @Task would 
protect MyTask.class from being renamed or stripped.


[1] http://blogs.oracle.com/jglick/entry/many_annotations_are_referentially_opaque



More information about the jigsaw-dev mailing list