Hey all,
Once again I'm lending my hand at trying to get some clojure code working under OSGi, and once again hitting issues with the good ole class loader. I have a simple OSGi bundle project which embeds clojure.jar, and contains a single source file:
(ns com.theoryinpractise.activator.osgi.components
(:import (org.osgi.framework BundleActivator)))
(deftype MyActivator []
BundleActivator
(start [this context]
(println "Hello from service"))
(stop [this context]
(println "stopping")))
In my OSGi metadata I have com.theoryinpractise.activator.osgi.components.MyActivator
listed as the Bundle-Activator
for the bundle, and when I load it I get the following exception:
Caused by: java.lang.ExceptionInInitializerError
at com.theoryinpractise.activator.osgi.components.MyActivator.<clinit>(components.clj:4)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at java.lang.Class.newInstance(Class.java:374)
at org.apache.felix.framework.Felix.createBundleActivator(Felix.java:4336)
at org.apache.felix.framework.Felix.activateBundle(Felix.java:2141)
... 32 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:443)
at clojure.lang.RT.load(RT.java:411)
at clojure.lang.RT.doInit(RT.java:447)
at clojure.lang.RT.<clinit>(RT.java:329)
... 40 more
java.lang.ExceptionInInitializerError
When I look at the decompiled class ( using JD-GUI ) I see there's a static initialiser pulling in the println
function:
public final class MyActivator implements BundleActivator, IType {
public static final Var const__0 = (Var)RT.var("clojure.core", "println");
…
Tracing this down into RT.baseLoader()
:
static public ClassLoader baseLoader(){
if(Compiler.LOADER.isBound())
return (ClassLoader) Compiler.LOADER.deref();
else if(booleanCast(USE_CONTEXT_CLASSLOADER.deref()))
return Thread.currentThread().getContextClassLoader();
return Compiler.class.getClassLoader();
}
I find that Compiler.LOADER.isBound()
is false, and booleanCast(USE_CONTEXT_CLASSLOADER.deref())
is true, which leads to using the class loader for the OSGi runtime and not the class loader that happens to actually know anything about Clojure, which would be the OSGi Bundle classloader for my package.
Does anyone know of a way around this? Or will I need to give up on trying to use pure-clojure here and use Java for my activator, and call Compiler.LOADER.set(myclassloader)
and then do some RT.*
love?
Cheers,
Mark
Cheers,
I took the time this morning to hack around a bit, and forked the repository and updated it to:
maven-bundle-plugin
From there I just adapted my internal packages to use it, and now have something up and running our OSGi/Karaf based application, now to actually get some real code written and we'll be well on our way to being free from the shackles of Java.
I'm thinking it may be a good idea to move the clojure-osgi
project to it's own repository so that it can be released out the wild. Now that I've got things working, I can see I'll be adding some more support functions to handle configuration admin queries etc.
Mark
On 5 Feb 2016, at 9:07, Evile Cruela wrote:
Is there a complete example of a bundle activator in clojure that you could point me to please. I have written them in java and I'd like to try it in clojure.
<dependency>
<groupId>com.theoryinpractise</groupId>
<artifactId>clojure.osgi</artifactId>
<version>1.8.0-1</version>
</dependency>
Should be winging it's way to Maven Central in an hour or so on the next sync cycle.