>
>
> Could that work if we complemented the "instanceof URLClassLoader" with
> some check for "is the system classloader" and then use
> System.getProperty("java.class.path") (or the equivalent
> ManagementFactory.getRuntimeMXBean().getClassPath()) to get the system
> classpath entries? (do we also need the bootclasspath?)
>
Here is the hack in full. Could be better using a more standard "get my
classpath" mechanism (though, to be honest, I think it might be better to
have an alternate means of specifying classpath for Gwt; for example,
specifying maven-repo coordinates and resolving them via maven cli)
String java9Modules = System.getProperty("gwt.java9.modules");
for (ClassLoader candidate = wrapped; candidate != null; candidate =
candidate.getParent()) {
if (candidate instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) candidate).getURLs()) {
result.put(url.toExternalForm(), url);
}
} else if (java9Modules != null) {
// If the user specified java 9 modules to load for classpath,
// we'll want to do it all through reflection,
// to avoid losing support for java 8
try {
final Class<?> modRef =
candidate.loadClass("java.lang.module.ModuleReference");
final Method getLocation = modRef.getMethod("location");
// In the case of java 9, the only way to scrape classloaders is this
ugly reflection
// on an internal class (used to be a URLClassLoader).
// you will need to add `--add-opens
java.base/jdk.internal.loader=myJava9ModuleName`
// to your gwt runtime as a VM argument
final Class<?> loader =
candidate.loadClass("jdk.internal.loader.BuiltinClassLoader");
// TODO: Don't use classloader for resource loading
final Method findMod = loader.getDeclaredMethod("findModule",
String.class);
// This is why we have to open the package; just being visible is not
enough
// to be allowed to act reflectively
findMod.setAccessible(true);
for (String source : java9Modules.split(",")) {
System.out.println("Loading java 9 module " + source);
Object mod = findMod.invoke(candidate, source);
// Safe to cast; we must be in java 9 to get here,
// so we know that this cast should be safe
Optional<URI> location = (Optional<URI>) getLocation.invoke(mod);
if (location.isPresent()) {
final URL url = location.get().toURL();
result.put(url.toExternalForm(), url);
}
}
} catch (Exception ignored) {
if (ignored instanceof InterruptedException) {
Thread.currentThread().interrupt();
throw new RuntimeException(ignored);
}
ignored.printStackTrace();
}
}
}
My method uses a system property to specify which java 9 modules to use as
source, then rips those out of the BuiltinClassloader.
This is obviously too egregious to use in production, but worked enough to
get a HelloWorld example to compile (w/ new language features for testing).
Due to java 9's encapsulation, we can't just load the whole classpath
anyway, since the modules we want to read must be opened (thus the
property, which could be a config prop instead).
Because we will already have to name the modules we want, it's not a far
cry to specify source coordinates and resolve the jars as well (I have
other tools that do this nicely).
It's not pretty, but at least it is possible. :-)
>
> IIRC, when CodeServer was added, with its -src argument, I suggested
> passing classpaths as argument rather than using the system classpath.
> At the time though, there was even more code that relied on the context
> classloader than today (rather than using the ResourceOracle). This was
> fixed later for the most part, but I believe there's still code that uses
> the context classloader (including looking up annotations from
> com.google.gwt.core.ext.typeinfo, and AFAIK also JDT loading bytecode that
> ends up being looked up in the context classloader;
> see 8b584f412d055ff39949d8b28a8e78d6b0bfe9b6 for example)
> I suppose that could be fixed by using a child URLClassLoader from the
> classpath argument (that could be necessary anyway for generators and
> linkers)
>
>
Aye... the way we load/compile annotations has always irked me.
Java 9 also comes with full-fledged support for dynamically compiling
arbitrary source, and it is my opinion that we should use this for
annotations (so we don't need to run javac before gwtc).
Because annotations contain classes and enums, those classes and enums must
also be loaded to get annotation support, so it gets... sticky, especially
once java 9 modules get involved.
By running the compiler on source, then the myriad of original modules
won't really matter...
Another alternative is to go the way of apt and use something akin to
AnnotationMirror... meaning you can't just do MyAnnoClass anno =
member.getAnnotation(MyAnnoClass.class), but will instead be stuck
operating on a generic mirror which can get members by string name. Not
exactly pretty, but if it's good enough for apt, it can clearly be made to
work (plus, with the impetus to ditch generators, we'd be stuck with it
anyway).
I (also) have another project which converts AnnotationMirror to a proxy of
MyAnnoClass that can either load a dependent Class/Enum, or fail if that
class is not on classpath (but only fail if you try to access that member).
Adding a dynamic runtime compiler (I didn't have support for that when I
build the annotation proxy bit) would mean we could fallback to compiling
the necessary classes, and still get nice typed annotations w/out requiring
them to be precompiled (and thus remove all need for inspecting
ClassLoader).
> The one big caveat is code modularity. We can't release two jars with
>> code in the same packages (unless recent changes to module system now
>> allowed "concealed package conflicts").
>>
>
> Apparently not:
> http://openjdk.java.net/projects/jigsaw/spec/issues/#AvoidConcealedPackageConflicts
> IIUC, they say this can be worked around using classloaders, or
> shading/shadowing (i.e. what you're doing)
>
>
Right, if you, yourself, load up a URLClassLoader and use it, it's business
as usual. But, that also means we need to populate said URLClassLoader
correctly, which means either we manage classpath ourselves (then no need
for URLClassLoader inspection, since we are supplying its URLs), or we
force end users to figure this out themselves (ick).
The shading I am doing is too hideous to consider for anything serious. It
took like, four invocations of maven-dependency-plugin to unzip stuff in
all the right locations, and resulted in an output artifact containing the
entire user+dev gwt sdk.
So, the time has finally come that modularization is no longer
nice-to-have, but now must-have.
> So, that means either one big, ugly uber-jar for compilation (my hack
>> unzips gwt-user and gwt-dev locally to avoid these issues)... or we
>> actually, finally modularize the codebase.
>>
>
> Does that also apply to subpackage? Because it looks like the only
> conflicting packages are c.g.g.core.client and c.g.g.core.shared, so maybe
> moving classes around could be enough?
>
>
It could be possible to get away with light refactoring, yourself and Colin
have more experience in actually making an effort towards this.
I would be happy with a lighter weight solution for now, to make this
workable, and a full solution later, as time permits.
--
You received this message because you are subscribed to the Google Groups "GWT
Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/google-web-toolkit-contributors/405d8326-332c-45d6-b503-d6a44dfe2bdf%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.