[jsr294-modularity-eg] Runtime model
Bryan Atsatt
bryan.atsatt at oracle.com
Mon Jun 4 18:45:23 EDT 2007
I'd like to start a discussion on this topic...
Given a class that represents a superpackage at runtime:
public class SuperPackage {
public String getName() {}
public List<Package> getMemberPackages() {}
public List<SuperPackage> getMemberSuperPackages() {}
public List<String> getExportedClassNames() {}
public boolean isExported(String className){}
public boolean isReExported();
}
and a Class.getSuperPackage() method, access control can simply be
implemented as follows:
public static void checkSuperPackageAccess(Class source, Class target) {
SuperPackage sourceSpkg = source.getSuperPackage();
SuperPackage targetSpkg = target.getSuperPackage();
if (sourceSpkg != targetSpkg &&
!targetSpkg.isExported(target.getName()) {
throw new LinkageError(...);
}
}
But how is a SuperPackage instantiated and bound to a Class? One obvious
model is to update ClassLoader with methods similar to those used for
loading a Class:
public class ClassLoader {
// Load SuperPackage by name.
public abstract SuperPackage loadSuperPackage(String name);
// Check cache for previous instantiation.
protected native SuperPackage findLoadedSuperPackage(String name);
// Instantiate SuperPackage from binary and update cache.
protected native SuperPackage defineSuperPackage(String name,
byte[] data);
}
Now, when defineClass() is called, and *assuming the class data contains
its superpackage name*, the JVM can check the cache and call
loadSuperPackage(). And the loader is responsible for the mapping from
name to data, just as it is for classes. The JVM assigns the
SuperPackage to the Class instance.
The previously suggested file name/location convention allows
superpackage "a.b.c" to be found using a resource path of
"a/b/c/super-package.spkg". So this makes for an easy default
implementation:
public SuperPackage loadSuperPackage(String name) {
SuperPackage result = findLoadedSuperPackage(name);
if (result == null) {
URL spkg = findResource(toResourcePath(name));
if (res != null) {
byte[] data = readData(spkg);
result = defineSuperPackage(name, data);
}
}
return result;
}
And of course subclasses are free to change the implementation.
(Obviously we can debate the merits of making this method public vs.
protected, or using native vs. local for the other methods, etc.)
So far, this seems pretty straightforward. But how would this work if
the class *doesn't* name its enclosing superpackage? (I'm assuming here
that supporting such legacy classes is an important requirement.)
It seems to me that there are really two different legacy class cases:
1. SuperPackage "aware" ClassLoader.
2. Unmodified legacy ClassLoader.
I think #1 is easy: allow the *loader* to assign the SuperPackage by
passing it as a parameter in an overloaded version of defineClass(). A
277 loader, for example, will easily be able to do this. (You may
recognize this as the model I proposed in the document I circulated
before this JSR officially started. :^)
With this model, the JVM would only call loadSuperPackage() when the
SuperPackage is *not* passed to defineClass(). (And we may want to make
it an error for an "aware" loader to use the older variant, to avoid
issues with legacy classes.)
But #2 is a bit harder. It seems to me that we have the following
options in this case:
A. No support for superpackages.
B. Change the convention for the top-level superpackage so that it can
always be found at a fixed resource path (e.g.
"META-INF/super-package.spkg").
C. Provide additional meta-data to specify the name of the top-level
superpackage (e.g. a new manifest attribute).
This latter option seems the best to me. Yes, it requires updating a
legacy jar, but we're *already* assuming additions to add the .spkg
file(s). (Neither of which may be possible with signed jars.)
But re-compilation is *not* required, which is the critical part.
Thoughts on any of this?
// Bryan
More information about the jsr294-modularity-eg
mailing list