我有这种问题。我在* .clj文件中包含Eclipse插件(表示为A)中包含的Clojure代码。我不想要AOT编译。但是,我需要从另一个Clojure插件B加载clojure代码。当B依赖于A时,这是可能的.Clojure可以轻松访问类路径,一切正常。但我希望插件A作为B的扩展插件。但是有一个问题,因为我找不到如何从B中包含的* .clj文件加载A中包含的Clojure文件的方法。我想使用Clojure'load'函数,它可以从类路径加载* .clj文件,但是当我明确地启动插件时,这个函数看不到插件的内容
(org.eclipse.core.runtime.Platform/getBundle "A")
对Laurent的回答的反应
劳伦特,非常感谢你!这很有趣。但是,我认为这可能解决了我原来的问题。你描述了如何从java插件调用clojure代码,这完全是非常棒的。我需要从clojure插件调用clojure代码,我认为这可能更容易。我想我会创建扩展点并提供像这样的clojure函数<extension point="transforms">
<function namespace="my.nemaspace" fn="my-transform"/>
</extension>
因此IExecutableExtensionFactory不需要任何魔法。我可以从clojure代码中读取扩展注册表。我不能做的是加载扩展中指定的功能。这是可行的还是我误解了什么?我注意到你正在使用clojure.osgi。这看起来很酷,该项目是否有任何文档?
答案 0 :(得分:6)
关于类加载器的问题是可以预期的:如果Eclipse插件/ OSGi包的依赖关系是A - &gt; B,从B引导的Clojure jar,然后无法从B看到A的资源是正常的。
Eclipse插件的依赖项之间没有循环,因此类加载器层次结构之间不存在循环。
如果您使用常规Eclipse机制从A编写B的扩展,那么您将面临同样的问题:插件B将声明一个接口和一个扩展点。然后插件A可以实现接口,并声明扩展点的扩展。最后一部分允许Eclipse框架使用bundle做一些技巧:它看到A声明了B的扩展,因此实现了A中实现B中声明的接口的类(这是因为A依赖于B),并给出了B来自A的实现实例也可以,因为它实现了B!
中的接口(不确定这是否清楚)。
无论如何,回到用Clojure编写的插件。
使用Clojure,您没有开箱即用的类加载器提供的独立环境,因为所有内容都聚集在一个“Clojure环境”中,该环境位于嵌入clojure jar的插件的类加载器领域中。
因此,当插件A启动时,一种可能性就是从A加载相关的命名空间。然后,它们将在正确的时间加载,并可用于任何其他Clojure代码。
另一种可能性是使用扩展点/扩展机制。 Eclipse提供了一种使用“工厂”来创建扩展点实例的方法。在逆时针方面,我们利用这个功能,并拥有一个通用工厂类(用java编写,所以没有AOT),它负责从正确的包中加载正确的命名空间。
以下是有关如何扩展扩展点的更多详细信息。
逆时针的一个例子:
Eclipse框架中有一个现有的扩展点,用于为控制台内容提供超链接检测器。逆时针扩展此扩展点以添加nrepl超链接
在java世界中,您必须在扩展中直接声明实现接口IPatternMatchListenerDelegate的某些类。
但是对于CCW,可能与你的原因相同,我试图不惜一切代价避免使用AOT,所以我不能在扩展中给出一个java类名,否则我要么必须在java中编写并编译它,或者在Clojure中编写gen-class
并进行AOT编译。
相反,CCW利用了plugin.xml的隐藏宝石的可能性:几乎在每个地方,当你必须提供一个类名时,你可以提供一个IExecutableExtensionFactory的实例,其create()
方法将由Eclipse框架调用以创建所需类的实例。
这允许我编写一个用于调用Clojure世界的泛型类:我只是用来代替我应该写的类名,类名ccw.util.GenericExecutableExtension
摘自plugin.xml:
<extension point="org.eclipse.ui.console.consolePatternMatchListeners">
<consolePatternMatchListener
id="ccw.editors.clojure.nREPLHyperlink"
regex="nrepl://[^':',' ']+:\d+">
<class class="ccw.util.GenericExecutableExtension">
<parameter
name="factory"
value="ccw.editors.clojure.nrepl-hyperlink/factory">
</parameter>
</class>
</consolePatternMatchListener>
请注意class
属性,以及如何通过parameter
元素向工厂提供参数(工厂必须实现接口IExecutableExtension才能使用参数进行初始化)。
最后,您可以在命名空间ccw.editors.clojure.nrepl-hyperlink
中看到函数factory非常简单,只需调用make函数:
(defn make []
(let [state (atom nil)]
(reify org.eclipse.ui.console.IPatternMatchListenerDelegate
(connect [this console] (dosync (reset! state console)))
(disconnect [this] (reset! state nil))
(matchFound [this event] (match-found event @state)))))
(defn factory "plugin.xml hook" [ _ ] (make))
请注意,我将此作为示例显示,并且逆时针方向的相关代码尚未准备好作为“准备好消费”的独立库发布。
但是你应该能够推出自己的解决方案(一旦你把所有的部分都放在脑中就很容易了)。
希望有所帮助,
- 劳伦
答案 1 :(得分:0)
另一个解决方案发生在我身上:尽管我在之前的回答中说过,但在Eclipse中,有可能在类加载器之间创建循环依赖关系!
Eclipse家伙需要引入这一点,以便某些类型的库(log4j等)可以在OSGi环境中运行(这是Eclipse所基于的)。
这需要利用Eclipse-BuddyPolicy
机制(Third Party libraries and classloading)。
这很简单:如果你想让插件B看到插件A的所有类和资源,只需将它添加到插件B的META-INF/MANIFEST.MF
文件中:
Eclipse-BuddyPolicy: dependent
上面一行表明插件B的类加载器将能够访问其依赖类加载器可以访问的内容。
我创建了一组名为A和B的插件示例,其中B有2个命令(在顶部菜单中可见):第一个通过调用B中的clojure代码对“hello”硬编码字符串应用文本转换。从插件A动态加载一个新的文本转换,这样当你再次调用第一个命令时,你会看到应用B转换和A转换的结果。
在您的情况下,甚至可能根本不需要使用Eclipse扩展点/扩展机制。这将取决于你如何让插件B“发现”插件A相关信息。
github存储库显示了这一点:https://github.com/laurentpetit/stackoverflow-12689605
HTH,
- 劳伦