Echtzeitsysteme / java-refactoring-ttc

Object-oriented Refactoring of Java Programs using Graph Transformation (TTC'2015)
0 stars 0 forks source link

ARTE can't load my solution #13

Closed tsdh closed 9 years ago

tsdh commented 9 years ago

When I do load --solution /home/horn/Repos/uni/ttc15-java-refactoring-funnyqt/target/ttc15-java-refactoring-funnyqt-0.1.0-SNAPSHOT-standalone.jar in ARTE, I get the following exception:

Exception in thread "main" java.util.ServiceConfigurationError: ttc.testsuite.interfaces.TestInterface: Provider ttc15_java_refactoring_funnyqt.TestInterfaceImpl could not be instantiated
    at java.util.ServiceLoader.fail(ServiceLoader.java:232)
    at java.util.ServiceLoader.access$100(ServiceLoader.java:185)
    at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:384)
    at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
    at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
    at arte.plugin.PluginLoader.setPlugin(PluginLoader.java:46)
    at arte.plugin.PluginLoader.setPlugin(PluginLoader.java:36)
    at arte.shell.commands.impl.support.SolutionLoader.loadSolution(SolutionLoader.java:19)
    at arte.shell.commands.impl.LoadCommand.execute(LoadCommand.java:34)
    at arte.shell.Shell.executeCommand(Shell.java:100)
    at arte.shell.Shell.shell(Shell.java:57)
    at arte.Arte.main(Arte.java:96)
Caused by: java.lang.ExceptionInInitializerError
    at clojure.lang.Namespace.<init>(Namespace.java:34)
    at clojure.lang.Namespace.findOrCreate(Namespace.java:176)
    at clojure.lang.Var.intern(Var.java:146)
    at clojure.java.api.Clojure.var(Clojure.java:82)
    at clojure.java.api.Clojure.<clinit>(Clojure.java:96)
    at ttc15_java_refactoring_funnyqt.TestInterfaceImpl.<clinit>(TestInterfaceImpl.java:18)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at java.lang.Class.newInstance(Class.java:442)
    at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:380)
    ... 9 more
Caused by: java.io.FileNotFoundException: Could not locate clojure/core__init.class or clojure/core.clj on classpath.
    at clojure.lang.RT.load(RT.java:449)
    at clojure.lang.RT.load(RT.java:412)
    at clojure.lang.RT.doInit(RT.java:454)
    at clojure.lang.RT.<clinit>(RT.java:330)
    ... 21 more

This looks exactly as discussed in http://blog.mattiasholmqvist.se/?p=631, so could you please make ARTE set the current thread's context class loader before calling ServiceLoader.load()?

I'd do it myself and submit a patch if I could find the ARTE code somewhere which I don't.

SvenPeldszus commented 9 years ago

We never had this problem and the tested it on several systems.

However, I included the suggested change. But I am not sure if this solves the problem.

tsdh commented 9 years ago

You never encountered this problem probably because you've never tested with solutions implemented in languages that rely on their own class loaders in order to dynamically load classes, e.g., solutions using Clojure, Scala, or Groovy.

But with the change, ARTE doesn't even start up anymore:

Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/xtext/ISetup
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at arte.Arte.<init>(Arte.java:62)
    at arte.Arte.main(Arte.java:88)
Caused by: java.lang.ClassNotFoundException: org.eclipse.xtext.ISetup
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 26 more

How about loading the TestInterface implementation class with a custom classloader which tries to load it as before your last change, and if that doesn't work out try alternative classloaders. E.g., something like this:

public class FallbackClassLoader extends ClassLoader {
//                                       ^^^^^^^^^^^
// Extend the ClassLoader class you are actually using.
    private ClassLoader[] fallbacks;

    public FallbackClassLoader(ClassLoader... fallbacks) {
        this.fallbacks = fallbacks;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            return super.findClass(name);
        } catch (ClassNotFoundException e) {
            for (ClassLoader cl : fallbacks) {
                try {
                    return cl.loadClass(name);
                } catch (ClassNotFoundException e2) {}
            }
            throw e;
        }
    }

    public static void main(String[] args) {
        // Use it like so, e.g., use the current threads classloader as fallback
        ClassLoader clForServiceLoader = new FallbackClassLoader(Thread
                .currentThread().getContextClassLoader());
        ServiceLoader<TestInterface> services = ServiceLoader.load(
                TestInterface.class, clForServiceLoader);
    }
}
tsdh commented 9 years ago

Ups, command back. The error above happened with 5f613a8b573a8e4dd0ead764bf103e1b482177a1. Now with 7d74786024b16c4a68a178d0cb4dd8c4a43ff65c I'm back to the exception from my original report.

If you make the ARTE sources available to me, I'll try to fix it myself. Or alternatively, I can send you my solution JAR so that you can test yourself.

tsdh commented 9 years ago

Ok, I'm marking this as done because I have found a way to set the right classloader from inside my solution. As a reference for others who might run into the same trouble, this is what I do in my TestInterface implementation:

static {
    Thread.currentThread().setContextClassLoader(TestInterfaceImpl.class.getClassLoader());
}