I have a v2 plugin which provides a network-accessible REPL, intended to allow rapid prototyping of code which interacts with a JIRA environment.
However, while connected to the socket, only an extremely minimal set of classes are available in the environment, excluding all portions of the com.atlassian hierarchy which I've attempted to access.
user=> (import com.atlassian.jira.extension.Startable) ClassNotFoundException com.atlassian.jira.extension.Startable java.net.URLClassLoader$1.run (URLClassLoader.java:202) user=> (pprint (seq (.getURLs (java.lang.ClassLoader/getSystemClassLoader)))) (#<URL file:/home/cduffy/.m2/repository/hsqldb/hsqldb/1.8.0.5/hsqldb-1.8.0.5.jar> #<URL file:/home/cduffy/.m2/repository/jta/jta/1.0.1/jta-1.0.1.jar> #<URL file:/home/cduffy/.m2/repository/ots-jts/ots-jts/1.0/ots-jts-1.0.jar> #<URL file:/home/cduffy/.m2/repository/jotm/jotm/1.4.3/jotm-1.4.3.jar> #<URL file:/home/cduffy/.m2/repository/jotm/jotm-jrmp_stubs/1.4.3/jotm-jrmp_stubs-1.4.3.jar> #<URL file:/home/cduffy/.m2/repository/jotm/jotm-iiop_stubs/1.4.3/jotm-iiop_stubs-1.4.3.jar> #<URL file:/home/cduffy/.m2/repository/jotm/jonas_timer/1.4.3/jonas_timer-1.4.3.jar> #<URL file:/home/cduffy/.m2/repository/jotm/objectweb-datasource/1.4.3/objectweb-datasource-1.4.3.jar> #<URL file:/home/cduffy/.m2/repository/carol/carol/1.5.2/carol-1.5.2.jar> #<URL file:/home/cduffy/.m2/repository/carol/carol-properties/1.0/carol-properties-1.0.jar> #<URL file:/home/cduffy/.m2/repository/xapool/xapool/1.3.1/xapool-1.3.1.jar> #<URL file:/home/cduffy/.m2/repository/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar> #<URL file:/home/cduffy/jira-nrepl/target/container/tomcat6x/apache-tomcat-6.0.20/bin/bootstrap.jar> #<URL file:/usr/lib/jvm/java-6-sun-1.6.0.26/lib/tools.jar>)
I can get a handle to the (Apache Felix) OSGi classloader used to load my plugin, but it too won't give me a handle on anything in com.atlassian.jira.*:
user=> (def mcl (.getClassLoader clojure.lang.DynamicClassLoader)) user=> mcl #<ModuleClassLoader 92.0> user=> (.loadClass mcl "com.atlassian.jira.extension.Startable") ClassNotFoundException com.atlassian.jira.extension.Startable org.apache.felix.framework.ModuleImpl.findClassOrResourceByDelegation (ModuleImpl.java:772)
Per https://developer.atlassian.com/display/CONFDEV/Packages+available+to+OSGi+plugins, I expect the com.atlassian hierarchy to be available, but it's very unclear to me how these classes can be loaded by a plugin at runtime.
(Moreover -- I would very much like to have access at runtime to resources from the SAL plugin, but this one is nowhere to be found in or around any accessible classloader, despite my atlassian-plugin declaration having a component-import from under com.atlassian.sal.api).
Community moderators have prevented the ability to post new answers.
You may need to import them in your plugin.xml... https://developer.atlassian.com/display/PLUGINFRAMEWORK/Creating+your+Plugin+Descriptor#CreatingyourPluginDescriptor-%7B%7Bbundleinstructions%7D%7Delement
I don't know anything about clojure, must it be statically typed? If not perhaps you don't need the imports.
Just out of interest I was looking at embedding a groovyshell into a web console thing in jira. It would work fine but I didn't see much advantage over just having the script runner eval a file as it does currently, unless there was some intelligence to it eg method completion, like you get in IronPython for instance. You could maybe take a look at script runner as imho it gives you the RAD environment you are looking for... (it may even work with clojure if you include http://code.google.com/p/clojure-jsr223/).
After appropriating ScriptRunner's import of jira-core, as opposed to going through jira-api for all access, I can now import com.atlassian.jira.ComponentManager... but still only after jumping through substantial hoops:
(defn get-full-classpath ([] "Find all classes available through not only immediate classloader and its parents, but the classloader which loaded it [and so forth]" (get-full-classpath (.getContextClassLoader (Thread/currentThread)))) ([classloader] (let [parent-classloader (.getClassLoader (.getClass classloader))] (concat (if (instance? java.net.URLClassLoader classloader) (.getURLs classloader) ()) (if parent-classloader (get-full-classpath parent-classloader) nil))))) (defn current-classpath-set [] (into #{} (seq (.getURLs (.getContextClassLoader (Thread/currentThread)))))) (defn available-classpath-set [] (into #{} (get-full-classpath))) (defn addable-classpath-set [] (set/difference (available-classpath-set) (current-classpath-set))) (defn load-available-classpath-entries [] "Load all classpath entries not present in our classloader into the current thread context classloader" (let [immediate-classloader (.getContextClassLoader (Thread/currentThread))] (doseq [classpath-url (addable-classpath-set)] (.addURL immediate-classloader classpath-url))))
I don't see similar logic anywhere in ScriptRunner, so presumably this is unnecessary complexity and could be done a better way.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
The similar bit in scriptrunner is:
joinClassLoader = new JoinClassLoader(webAppClassLoader, this.class.getClassLoader());
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.