在Eclipse插件中加载Clojure代码

时间:2012-10-02 11:44:27

标签: eclipse clojure

我有这种问题。我在* .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。这看起来很酷,该项目是否有任何文档?

2 个答案:

答案 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,

- 劳伦