【发布时间】:2021-11-30 04:15:53
【问题描述】:
说明
我们在pantomime 后面的包装版本中使用Tika Parser,没有配置(默认),它使用AutoDetectParser。由于缺少功能,升级 Tika 依赖项至关重要。在将 tika 覆盖为 1.27 的依赖项以及其他依赖项之后,我们观察到了一些意想不到的行为。在lein repl 中运行服务时,tika 正确转换了相关文档 (.doc)。
之后我们用lein uberjar打包了clj代码。
现在有趣的部分开始了:
随后是对 tika 的深度(th)和类型转换的野生虫子狩猎。
发现问题出在不同的解析数据类型上。在 REPL 中解析的数据类型是 javax.mail.internet.MimeMessage,而生成的 .jar 中解析的数据类型是 javax.mail.util.SharedByteArrayInputStream。
日志摘录:
REPL
[2021-10-08 16:56:30,406] TRACE xxx - Unpacking MimeMessage -1
[2021-10-08 16:56:30,407] DEBUG xxx - |> type javax.mail.internet.MimeMessage
[2021-10-08 16:56:30,415] DEBUG xxx - ||> javax.mail.internet.MimeMultipart@397fca9a
[2021-10-08 16:56:30,415] DEBUG xxx - |-> transforming multipart/mixed to Mime for instance javax.mail.internet.MimeMultipart
罐子
[2021-10-08 17:02:55,181] TRACE xxx - Unpacking MimeMessage -1
[2021-10-08 17:02:55,183] DEBUG xxx - |> type javax.mail.internet.MimeMessage
[2021-10-08 17:02:55,191] DEBUG xxx - ||> javax.mail.util.SharedByteArrayInputStream@b83be05
[2021-10-08 17:02:55,193] WARN xxx - Mime unpacking unsuccessful: multipart/mixed - javax.mail.util.SharedByteArrayInputStream
调查结果
我怀疑可能存在依赖冲突,因为 tika-parser 和 simplejavamail 模块都使用 MimeMessage 的实现,jakarta.mail 和 javax.mail 都提供了该实现。我已经解决了 leiningen 暗示的依赖冲突,但怀疑lein repl 如何解决依赖关系可能会发生一些事情,这使得它在这种情况下工作。
使用干净的profiles.clj 复制了此行为。
对此函数.getContent 的日志记录和堆栈跟踪提示,该函数负责返回适当的对象。
project.clj
(defproject my-project :lein-v
:plugins [[lein-parent "0.3.8"]
[com.roomkey/lein-v "7.2.0"]]
:parent-project {:path "../../project.clj"
:inherit [:managed-dependencies :repositories :manifest :url :prep-tasks]}
:dependencies [[org.clojure/clojure]
[...]
[com.novemberain/pantomime "2.11.0" :exclusions
[org.apache.commons/commons-compress
org.apache.pdfbox/fontbox
org.apache.tika/tika-parsers]]
[org.apache.tika/tika-parsers "1.27"]
[com.sun.mail/javax.mail "1.6.2"]
[net.htmlparser.jericho/jericho-html "3.4"]
[org.simplejavamail/simple-java-mail "6.6.1"]
[org.simplejavamail/outlook-module "6.6.1"]]
:profiles {:uberjar {:global-vars {*warn-on-reflection* true}
:aot :all
:omit-source true
:uberjar-exclusions ["project.clj" #"xxx/.*\.clj$"]
:jvm-opts ["-XX:-OmitStackTraceInFastThrow"]
:uberjar-name "service.jar"}}
:repl-options {:init-ns xxx.init})
受影响的代码
(ns xxx.convert
(:import (java.time OffsetDateTime ZoneOffset)
(java.util Properties UUID Base64 Date)
(java.io InputStream ByteArrayInputStream)
(javax.activation MimeType)
(javax.mail.internet MimeMessage MimeMultipart MimeBodyPart InternetAddress MimeUtility)
(javax.mail Session Message Message$RecipientType)
(net.htmlparser.jericho Source Renderer)
(org.apache.commons.io IOUtils)
(defn- unpack-content [^Message message
{:keys [max-text-length max-attachments] :as params}
counter]
(log/debug "|> type" (type message))
(when (or (not max-attachments)
(< @counter max-attachments))
(let [base-type (-> message .getContentType MimeType. .getBaseType string/lower-case)
content (.getContent message)] ;; <<< [!] This is where the conversion happens
(log/debug "||>" (.toString ^MimeMultipart content))
{:content-type base-type
:content (cond
(instance? String content)
(do
(log/trace "|-> transforming" base-type "to String")
(swap! counter inc)
(->> (if (html? base-type)
(render-html content)
content)
(truncate max-text-length)))
(and (instance? InputStream content)
(.getFileName message))
(let [filename (MimeUtility/decodeText (.getFileName message))
encoded-file (do
(swap! counter inc)
(base64-file content filename))]
(log/trace "|-> transforming" base-type "to InputStream")
(.close ^InputStream content)
encoded-file)
(or (instance? MimeMessage content)
(instance? MimeMultipart content)
(instance? MimeBodyPart content))
(do
(log/debug "|-> transforming" base-type "to Mime for instance" (type content))
(unpack-mime content params counter))
:else
(do
(log/warn "Mime unpacking unsuccessful:" base-type "-" (type content))
{}))})))
很遗憾,我无法共享相应的 .doc 文件。然而,它是一个官方文档,带有表格、图像等普通邮件的通用设置。
为什么会发生,我该如何解决?
【问题讨论】:
-
这全是猜测:在不同的包上拥有两个类不是冲突或问题(如果某些代码尝试了 smart things™,则可能是这样)。从 javax 到 jakarta 的切换很可能是在上游的某个主要版本上完成的,不应该打击你。我的猜测是,你有 deps,可能会将两者都放在同一个类中——并且你在 repl-time 和 run-time 有不同的类路径顺序(通过生成的 uberjar)。我会检查,最终在 uberjar 中的文件是否实际上来自您期望的 dep。并且仍然进行三次检查,如果这不仅仅是由于输入而导致的侥幸......
-
如何检查这种实际的依赖关系?
-
如果您想排除类或 META-INF 中的冲突,您可以尝试运行生产代码,而不是作为 uberjar,而是作为常规 jar。例如。使用
lein with-profile uberjar cp获取所有库。然后使用这些和你的工件(不是 uberjar)并运行它。如果这再次起作用,您可以开始在您的部门中找到冲突(或放弃 uberjar) -
谢谢@cfrick。我用
java -cp $(lein with-profile uberjar) xxx.init运行了这个工件并且可以重现正确的行为 -
那么我的下一个猜测将是 META-INF 中的内容。我对 javax.mail 一无所知,但通常那些“旧”库允许通过一些工厂/配置/属性/...配置实现 - 很可能您的至少两个部门包含此类文件,错误的一个获胜。如果您可以查明问题,一些库还允许通过
-D属性进行设置。
标签: clojure jvm dependency-management leiningen apache-tika