Description
We use the Tika Parser in a wrapped version behind pantomime with no config (default), which uses the AutoDetectParser. It was essential to upgrade the Tika dependency due to missing functionality. After overwriting tika as a dependency of 1.27 along other dependencies, we observed some unexpected behaviour. While running the service in a lein repl, tika correctly converted the document in question (.doc).
Afterwards we packed the clj-code with lein uberjar.
Now the fun part began:
A wild bug hunt into the dep(th)s of tika and type casts followed.
The issue was found to be a different parsed data type. While in the REPL the parsed data type was a javax.mail.internet.MimeMessage, with the produced .jar the parsed data type was a javax.mail.util.SharedByteArrayInputStream.
A log excerpt:
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
jar
[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
Findings
I suspect that there might be a clash of dependencies, as both the tika-parser and the simplejavamail modules use an implementation of MimeMessage, which is provided by both jakarta.mail and javax.mail. I already resolved the dependency clashes hinted by leiningen, but suspect there might be something going on in how lein repl resolves dependencies, which makes it work in that case.
This behaviour was reproduced with a clean profiles.clj.
Logging and stacktrace hint to this function .getContent, which is responsible for returning the appropriate Object.
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})
affected code
(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))
{}))})))
Unfortunately I can't share the corresponding .doc file. However it is an official document with a common setup for plain mail with tables, images, etc.
Why does it happen and how do I fix this?