Closed FroMage closed 11 years ago
When you say:
We already automatically include every jdk package at runtime for jars/java modules
do you mean that literally we add the list of all the packages defined in the "package-list.jdk7" and "package-list.oracle.jdk7" or do we just accept everything that's on the classpath?
We add the packages, as that's how JBoss Modules works, for system classpath.
Atm we only add pure JDK packages, and not Oracle packages, by default.
ah so that's why swing apps won't work on OSX...
I guess the good news for @chochos is that it also appears to fail on Linux.
shit... does it even work on windows at least?
No idea. @alesj: the JDK is hiding a class not found error or a resource not found error and Ican't figure out what it is. Is there a way to get jboss modules to log everything that it tries to load through its class loader?
Found it: Module.setModuleLogger(new StreamModuleLogger(System.out));
So this is what I have:
modules TRACE: Finding class javax.swing.JFrame from Module "ceylon.examples.gameoflife:1.0.0" from Ceylon ModuleLoader: RootRepositoryManager: FileContentStore: /home/stephane/.ceylon/cache
modules TRACE: Finding class javax.swing.plaf.basic.BasicPanelUI from Module "ceylon.runtime:0.5" from local module loader @1f493111 (roots: /home/stephane/src/java-eclipse/ceylon-dist/dist/repo)
modules TRACE: Class javax.swing.plaf.basic.BasicPanelUI not found from Module "ceylon.runtime:0.5" from local module loader @1f493111 (roots: /home/stephane/src/java-eclipse/ceylon-dist/dist/repo)
UIDefaults.getUI() failed: no ComponentUI class for: javax.swing.JPanel[,0,0,0x0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]
I don't see what ceylon.runtime
is doing here.
Looks like ceylon.runtime
is automatically added as as dependency to every module. Still doesn't explain why it would not try the JDK module after it fails to find it there.
Do we have a test for this?
Not automated. But you only need very little Ceylon code to reproduce:
import javax.swing { JFrame }
void run(){
JFrame("hello");
}
I managed to write a test and here's the hidden exception:
java.lang.ClassNotFoundException: javax.swing.plaf.basic.BasicPanelUI from [Module "ceylon.runtime:0.5" from local module loader @11b1db9a (roots: /home/stephane/src/java-eclipse/ceylon-runtime/build/dist/repo)]
at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:190)
at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:468)
at org.jboss.modules.ConcurrentClassLoader.performLoadClassChecked(ConcurrentClassLoader.java:456)
at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:120)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at javax.swing.SwingUtilities.loadSystemClass(SwingUtilities.java:1870)
at org.jboss.swing.run_.main(run_.java:49)
at ceylon.modules.api.runtime.SecurityActions.invokeRunInternal(SecurityActions.java:58)
at ceylon.modules.api.runtime.SecurityActions.invokeRun(SecurityActions.java:48)
at ceylon.modules.api.runtime.AbstractRuntime.invokeRun(AbstractRuntime.java:82)
at ceylon.modules.api.runtime.AbstractRuntime.execute(AbstractRuntime.java:126)
at ceylon.modules.api.runtime.AbstractRuntime.execute(AbstractRuntime.java:114)
at ceylon.modules.Main.execute(Main.java:91)
at ceylon.modules.Main.main(Main.java:46)
at org.jboss.modules.Module.run(Module.java:270)
at org.jboss.modules.Main.main(Main.java:294)
at org.jboss.ceylon.test.modules.ModulesTest.execute(ModulesTest.java:202)
at org.jboss.ceylon.test.modules.ModulesTest.execute(ModulesTest.java:180)
at org.jboss.ceylon.test.modules.ModulesTest.testArchive(ModulesTest.java:124)
at org.jboss.ceylon.test.modules.ModulesTest.testArchive(ModulesTest.java:86)
at org.jboss.ceylon.test.modules.smoke.test.SmokeTestCase.swingModule(SmokeTestCase.java:45)
This is the code in question, that I copied from Swing to unhide the exception:
JPanel jPanel = new javax.swing.JPanel();
UIDefaults defaults = UIManager.getDefaults();
Object cl = defaults.get("ClassLoader");
ClassLoader uiClassLoader =
(cl != null) ? (ClassLoader)cl : jPanel.getClass().getClassLoader();
try {
String className = (String)defaults.get(jPanel.getUIClassID());
if (className != null) {
Class cls = (Class)defaults.get(className);
if (cls == null) {
if (uiClassLoader == null) {
Method m = SwingUtilities.class.getDeclaredMethod("loadSystemClass", String.class);
m.setAccessible(true);
m.invoke(null, className);
// cls = SwingUtilities.loadSystemClass(className);
throw new RuntimeException("FAIL");
}
else {
cls = uiClassLoader.loadClass(className);
}
}
}
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (ClassCastException e) {
e.printStackTrace();
}
It tells us that UIDefaults.get("ClassLoader")
returns null, and so does JPanel.class.getClassLoader()
(for some reason, perhaps this is normal?), but then SwingUtilities.loadSystemClass
will use Class.forName(className, true, Thread.currentThread().getContextClassLoader());
which appears to be the runtime classloader and that one says nope, class not there, and does not delegate.
So is this normal? Is it normal that Thread.currentThread().getContextClassLoader()
returns the runtime module classloader? Is this a recent change by @quintesse?
I just pushed the test.
I guess it can't be what @quintesse did with context class loaders since the one he sets is CeylonClassLoader
which is not the guilty party here: we have a ModuleClassLoader
.
So it appears that JBoss modules sets the context class loader automatically. It also appears it has special cases for things that come from java.
but not javax.
, and that it does not delegate to the system class loader.
What I don't understand is why we can get a JFrame
and it has no classloader (that seems normal, if it's loaded by the system), but then we can't get system classes from the ceylon.runtime
module classloader. I mean, which classloader did the loading of JFrame
then? Oh, I guess that one was loaded by the test module, which does have a system dependency, so that would delegate. I wonder then why the context class loader is not set to the module we're running? I'll try that.
Perhaps @dmlloyd can make sense of all this?
So, setting the context class loader to the executed module's class loader works for swing. but it breaks an existing test io.xov.yalp
which imports the org.jboss.jboss-vfs
module, and tries to load org.jboss.vfs.VFS
which fails.
Note that I've no idea why that test even worked, I don't see why we allowed it to import that jboss-vfs
module if it's not available.
Oh, it's supposed to find it from aether…
Code is here: https://github.com/ceylon/ceylon-runtime/tree/31
Error is here:
Caused by: java.lang.IllegalArgumentException: No resource META-INF/services/org.jboss.shrinkwrap.resolver.api.maven.ConfigurableMavenResolverSystem was found configured for user view class org.jboss.shrinkwrap.resolver.api.maven.ConfigurableMavenResolverSystem
at org.jboss.shrinkwrap.resolver.api.ResolverSystemFactory.getImplClassForUserView(ResolverSystemFactory.java:145)
at org.jboss.shrinkwrap.resolver.api.ResolverSystemFactory.createFromUserView(ResolverSystemFactory.java:91)
at org.jboss.shrinkwrap.resolver.api.Resolvers.use(Resolvers.java:79)
at org.jboss.shrinkwrap.resolver.api.ConfiguredResolverSystemFactory.create(ConfiguredResolverSystemFactory.java:156)
at org.jboss.shrinkwrap.resolver.api.ConfiguredResolverSystemFactory.fromFile(ConfiguredResolverSystemFactory.java:96)
at org.jboss.shrinkwrap.resolver.api.ConfiguredResolverSystemFactory.fromFile(ConfiguredResolverSystemFactory.java:115)
at com.redhat.ceylon.cmr.maven.AetherUtils.getResolver(AetherUtils.java:114)
at com.redhat.ceylon.cmr.maven.AetherUtils.fetchDependencies(AetherUtils.java:77)
at com.redhat.ceylon.cmr.maven.AetherUtils.findDependencies(AetherUtils.java:71)
at com.redhat.ceylon.cmr.maven.AetherUtils.findDependency(AetherUtils.java:53)
at com.redhat.ceylon.cmr.maven.AetherContentStore.find(AetherContentStore.java:63)
at com.redhat.ceylon.cmr.impl.AbstractOpenNode.getNode(AbstractOpenNode.java:202)
at com.redhat.ceylon.cmr.impl.AbstractOpenNode.getChild(AbstractOpenNode.java:191)
at com.redhat.ceylon.cmr.impl.AbstractNodeRepositoryManager.fromRepository(AbstractNodeRepositoryManager.java:354)
at com.redhat.ceylon.cmr.impl.AbstractNodeRepositoryManager.fromRepositories(AbstractNodeRepositoryManager.java:327)
at com.redhat.ceylon.cmr.impl.AbstractNodeRepositoryManager.getFromAllRoots(AbstractNodeRepositoryManager.java:303)
at com.redhat.ceylon.cmr.impl.AbstractNodeRepositoryManager.getLeafNode(AbstractNodeRepositoryManager.java:235)
at com.redhat.ceylon.cmr.impl.RootRepositoryManager.getArtifactResult(RootRepositoryManager.java:64)
at ceylon.modules.jboss.runtime.CeylonModuleLoader.findArtifact(CeylonModuleLoader.java:202)
at ceylon.modules.jboss.runtime.CeylonModuleLoader.findModule(CeylonModuleLoader.java:213)
at org.jboss.modules.ModuleLoader.loadModuleLocal(ModuleLoader.java:275)
at org.jboss.modules.ModuleLoader.preloadModule(ModuleLoader.java:222)
at ceylon.modules.jboss.runtime.CeylonModuleLoader.preloadModule(CeylonModuleLoader.java:187)
at org.jboss.modules.Module.addPaths(Module.java:851)
at org.jboss.modules.Module.link(Module.java:1206)
at org.jboss.modules.Module.getPaths(Module.java:1166)
at org.jboss.modules.Module.getPathsUnchecked(Module.java:1189)
... 69 more
modules TRACE: Class org.jboss.vfs.VFS not found from Module "io.xov.yalp:11.0.2.Final" from Ceylon ModuleLoader: RootRepositoryManager: FileContentStore: /home/stephane/.ceylon/cache
Just checked, and that file is indeed found in the JBoss module com.redhat.ceylon.maven-support
, though perhaps we need something more in its module descriptor?
Oh, I guess I know what's happening. We should restore the ceylon-runtime
context class loader when we're in runtime code?
Indeed, that fixes the error, but, is it the proper solution?
I've pushed my latest fix for you to look at.
Just verified that the swing example works on OSX too.
However ugly this is, it appears to be working better than what we had before, so I've merged this on master, and we can open new issues later if we find a bug related to what I did.
It used to be that the whole runtime classpath was included automatically. I removed that and forced people to declare their JDK dependencies explicitely. Which makes sense at compile-time but not at runtime. Because at compile-time we want people to explicitely mark their dependencies on jdk and oracle-jdk-specific modules, but at runtime, the JDK itself needs the whole JDK to be available: jdk modules are all interdependent and we should not try to fix/control that.
We already automatically include every jdk package at runtime for jars/java modules, we should do that for Ceylon modules too and ignore declared dependencies on jdk modules at runtime.