请在 2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请参阅 关于 页面以了解更多有关此工具的信息。

0

嘿,

我有两个编译良好的 Leiningen 项目,之前在共享一个公共的包含文件中的函数时它们工作得很好。
这两个项目都运行在 tomcat 服务器上

第一个项目创建一个 .jar 文件,用作从 Java 源编译的 Java 代码的库。
它是以以下方式创建的
lein new eu.oca.jclojure
并以这种方式编译
lein uberjar

另一个项目是一个完整的 Clojure 网络应用,也运行在 tomcat 上。
它是以以下方式创建的

lein new compojure sidonie-admin

并以以下方式编译
lein ring uberwar

该公共文件以这种方式包含在源 Clojure 代码中

(load-file "src/sidonie_admin2/sidonie-common.clj") 在第二个项目中,以及在第一个项目中是这样 (两个项目编译时均无错误)
(load-file "../sidonie-admin2/src/sidonie_admin2/sidonie-common.clj")

错误发生在运行时

问题出在有共同定义的文件未被包含在 war 文件中,也许也没被包含在 jar 文件中,我在运行一个项目时遇到了这个错误
java.io.FileNotFoundException: ../sidonie-admin2/src/sidonie_admin2/sidonie-common.clj

以及另一个项目的错误
原因:java.io.FileNotFoundException: src/sidonie_admin2/sidonie-common.clj (文件或目录不存在)

这是相同类型的错误,抱怨 JVM 字节码中缺少源文件!
我承认,在错误打印堆栈跟踪时需要一些信息来提供错误行号。

所以问题很简洁,如何在两个项目间共享公共文件并且没有错误?

最好的祝愿,

Damien

以下是服务器日志的一些输出

08-Oct-2019 12:40:51.275 SEVERE [http-nio-127.0.0.1-8080-exec-8] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [eu.oca.ApplicationConfig] in context with path [/Sidonie] threw exception [org.glassfish.jersey.server.ContainerException: java.lang.ExceptionInInitializerError] with root cause
java.io.FileNotFoundException: ../sidonie-admin2/src/sidonie_admin2/sidonie-common.clj (文件或目录不存在)

at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at clojure.lang.Compiler.loadFile(Compiler.java:7314)
at clojure.lang.RT$3.invoke(RT.java:320)
at eu.oca.jclojure__init.load(Unknown Source)
at eu.oca.jclojure__init.<clinit>(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at clojure.lang.RT.classForName(RT.java:2168)
at clojure.lang.RT.classForNam

...

un(NioEndpoint.java:1500)

at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

还有

08-Oct-2019 12:21:42.761 SEVERE [http-nio-127.0.0.1-8080-exec-11] org.apache.catalina.core.StandardContext.listenerStart 发生在发送上下文初始化事件到 sidonie_admin2.listener 类听器实例时的错误
在 (sidonie_admin2/handler.clj:32:1) 编译时出现语法错误。

at clojure.lang.Compiler.load(Compiler.java:7647)
at clojure.lang.RT.loadResourceScript(RT.java:381)
at clojure.lang.RT.loadResourceScript(RT.java:372)
at clojure.lang.RT.load(RT.java:463)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvo

...

at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

原因:java.io.FileNotFoundException: src/sidonie_admin2/sidonie-common.clj (文件或目录不存在)

at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInp

3 个答案

+3

最好的方法可能是将共享代码转变为自己的项目,并从该项目产生一个工件(一个普通的,非uber JAR)。但这意味着您需要将工件发布到某处——或者也可以让每个原始项目的构建过程也构建并“安装”共享项目。

或者,您可以让两个原始项目都使用Leiningen的“checkouts”功能,通过与Git依赖公共代码项目。

正如Alex在其回答中所说:不要使用load-file。将公共代码变成真正共享的,您可以通过其命名空间简单地:require使用。这意味着需要将共享代码放入每个项目类路径(通过checkouts或额外的构建步骤将公共代码打包成JAR)。

+1

为什么您使用load-file?当clj文件在jar或war文件内部时,您不能将其作为文件加载(在Clojure中使用load-file非常不寻常)。通常,您通过require将它们加载到命名空间中。

他使用load-file是将源代码在两个项目间共享。
0

感谢您的回复,但我不能这样做。实际上它不是一个库或可重用的东西,可以设置在库或命名空间中,我认为,因为我可以把它全部放在war文件中,而不需要分开的jar库,供NetBeans使用网站公开部分。在我开发了一个使用compojure的密码保护网站部分之后,事实就是我可以把所有代码重构到一个单一的.war compojure文件中。但是保持现状会更快...

所以我找到了这个解决方案,哈哈,这基本上是一个窍门,我承认这是一个UNIX窍门,为了共享通用的文件,我创建了一个符号链接

ln -s ../../../../sidonie-admin2/src/sidonie_admin2/sidonie-common.clj symbolic-link-sidonie-common.clj

我稍微改变了加载文件的方式,不再使用load-file,而是使用load

(load "symbolic-link-sidonie-common")

它也可以与硬链接一起使用,但是我的Dropbox帐户不支持硬链接,此外,此解决方案也应该在Windows上运行,因为它也支持链接。

因此,我可以编辑一个在许多项目中共享的简单文件,并且可以从任何目录中编辑它。

在emacs中,需要在 .emacs 中设置 (setq backup-by-copying-when-linked t),以避免emacs的 .~ 备份文件中的 mv (移动) 功能给链接带来麻烦。

我承认这并不是完美的解决方案,但问题本身也不是完美的 ;-)

Damien

...