Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Atlassian resources not on classpath for v2 (OSGi) plugin

Charles Duffy
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 24, 2012

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).

1 answer

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

1 vote
Answer accepted
JamieA
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 24, 2012

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/).

Charles Duffy
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 25, 2012

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.

JamieA
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 25, 2012

The similar bit in scriptrunner is:

joinClassLoader = new JoinClassLoader(webAppClassLoader, this.class.getClassLoader());

However this has problems... class cast exceptions from two classes that appear to be the same, but are loaded from different classloaders etc. IMHO jira's plugin system (plugins2) doesn't work well with plugins like yours or script runner, where the list of imports can't be defined upfront (but to be fair I doubt it was created with this in mind, any why would it).
TAGS
AUG Leaders

Atlassian Community Events