ceylon / ceylon-runtime

DEPRECATED
24 stars 5 forks source link

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

Closed FroMage closed 11 years ago

FroMage commented 11 years ago

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 commented 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?

alesj commented 11 years ago

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 commented 11 years ago

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

FroMage commented 11 years ago

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

chochos commented 11 years ago

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

FroMage commented 11 years ago

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 commented 11 years ago

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

FroMage commented 11 years ago

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 commented 11 years ago

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.

alesj commented 11 years ago

Do we have a test for this?

FroMage commented 11 years ago

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

import javax.swing { JFrame }

void run(){
  JFrame("hello");
}
FroMage commented 11 years ago

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 commented 11 years ago

I just pushed the test.

FroMage commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

Perhaps @dmlloyd can make sense of all this?

FroMage commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

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

FroMage commented 11 years ago

Code is here: https://github.com/ceylon/ceylon-runtime/tree/31

FroMage commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

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

FroMage commented 11 years ago

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

FroMage commented 11 years ago

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

FroMage commented 11 years ago

Just verified that the swing example works on OSX too.

FroMage commented 11 years ago

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.