Skip to content

Loading…

All JDK packages should be added to the classpath automatically even for Ceylon modules #31

Closed
FroMage opened this Issue · 27 comments

4 participants

@FroMage
Ceylon member

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.

@quintesse
Ceylon member

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?

@alesj
Ceylon member

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.

@chochos
Ceylon member

ah so that's why swing apps won't work on OSX...

@FroMage
Ceylon member

I guess the good news for @chochos is that it also appears to fail on Linux.

@chochos
Ceylon member

shit... does it even work on windows at least?

@FroMage
Ceylon member

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?

@FroMage
Ceylon member

Found it: Module.setModuleLogger(new StreamModuleLogger(System.out));

@FroMage
Ceylon member

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.

@FroMage
Ceylon member

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.

@FroMage FroMage added a commit that referenced this issue
@FroMage FroMage CeylonModuleLoader: fix for #31: automagically import/export jdk modu…
…le with every jdk package
4079311
@alesj
Ceylon member

Do we have a test for this?

@FroMage
Ceylon member

Not automated. But you only need very little Ceylon code to reproduce:

import javax.swing { JFrame }

void run(){
  JFrame("hello");
}
@FroMage
Ceylon member

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?

@FroMage
Ceylon member

I just pushed the test.

@FroMage
Ceylon member

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.

@FroMage
Ceylon member

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.

@FroMage
Ceylon member

Perhaps @dmlloyd can make sense of all this?

@FroMage
Ceylon member

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.

@FroMage
Ceylon member

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.

@FroMage
Ceylon member

Oh, it's supposed to find it from aether…

@FroMage
Ceylon member

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
@FroMage
Ceylon member

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?

@FroMage
Ceylon member

Oh, I guess I know what's happening. We should restore the ceylon-runtime context class loader when we're in runtime code?

@FroMage
Ceylon member

Indeed, that fixes the error, but, is it the proper solution?

@FroMage
Ceylon member

I've pushed my latest fix for you to look at.

@FroMage
Ceylon member

Just verified that the swing example works on OSX too.

@FroMage
Ceylon member

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.

@FroMage FroMage closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.