3
votes

I am using Clojure's java-time library, which is wrapper of java.time [1].

My program works when I invoke lein run or when I call the functions through the repl. On the other hand when I try to do lein uberjar I get the following error with the classes involved:

$ lein uberjar
Compiling foo.cli
Compiling foo.core
Compiling foo.holidays
nil
Syntax error (ClassCastException) compiling at (/tmp/form-init8528909580728167374.clj:1:73).
class java_time.graph.Types cannot be cast to class java_time.graph.Types (java_time.graph.Types is in unnamed module of loader 'app'; java_time.graph.Types is in unnamed module of loader clojure.lang.DynamicClassLoader @141c66db)

Full report at:
/tmp/clojure-6795557396101417445.edn
Compilation failed: Subprocess failed

I have isolated which expression causes this problem and it happens to be:

(java-time/minus  (java-time/local-date) (java-time/period 2 :days))

I am not sure why it is failing and the error is quite cryptic. My bet is that the compiler gets confused casting some classes when generating the uberjar but it surprises me that the repl works fine. Is it related to the :aot used for the uberjar creation?

This is a snippet of the project.clj file concerning that:

(defproject foo
  ...
  :main ^:skip-aot foo.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

[1] https://github.com/dm3/clojure.java-time

1
I also wanted to add that there is not much information in the .edn created by the compiler that is not in the error message... - Navarro
The error message says java_time.graph.Types, loaded by "app", is not compatible with java_time.graph.Types, loaded by clojure.lang.DynamicClassLoader... Are you doing something fancy with classloaders? - Biped Phill
Nothing at all! The only thing done in this project which is not purely Clojure is reaching to the org.apache.poi library to write excel files. And that works fine! I am only having this issues with the java.time wrapper java-time, with the plus and minus functions... - Navarro
I have exactly the same symptoms with (some-> 1614703906000 instant) (but not with (-> 1614703906000 instant) and again it only happens with the direct-linking flag. It seems like the same problem, but I don't get your problem with minus. curiouser and curiouser - Andy

1 Answers

2
votes

It appears you have something wrong in your environment. A sample program works fine for me:

    (ns demo.core
      (:use tupelo.core)
      (:require [java-time :as jt])
      (:gen-class))
    
    (defn -main [& args]
      (print-versions)
      (let [result (java-time/minus
                     (java-time/local-date)
                     (java-time/period 2 :days))]
        (println :result result)))

with result:

    ~/expr/demo > lein clean ; lein run
    
    --------------------------------------
       Clojure 1.10.2-alpha1    Java 15
    --------------------------------------
    :result #object[java.time.LocalDate 0x61a309fe 2020-10-05]
    
    
    ~/expr/demo > lein clean ; lein uberjar 
    Compiling demo.core
    Created /home/alan/expr/demo/target/uberjar/demo-0.1.0-SNAPSHOT.jar
    Created /home/alan/expr/demo/target/uberjar/demo-0.1.0-SNAPSHOT-standalone.jar

For completeness, here is the project.clj:

(defproject demo "0.1.0-SNAPSHOT"
  :license {:name "Eclipse Public License"
            :url  "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [
                 [clojure.java-time "0.3.2"]
                 [org.clojure/clojure "1.10.2-alpha1"]
                 [prismatic/schema "1.1.12"]
                 [tupelo "20.08.27"]
                 ]
  :plugins [
            [com.jakemccrary/lein-test-refresh "0.24.1"]
            [lein-ancient "0.6.15"]
            [lein-codox "0.10.7"]
            ]

  :profiles {:dev     {:dependencies []}
             :uberjar {:aot :all}}

  :global-vars {*warn-on-reflection* false}
  :main ^:skip-aot demo.core

  :source-paths ["src/clj"]
  :java-source-paths ["src/java"]
  :test-paths ["test/clj"]
  :target-path "target/%s"
  :compile-path "%s/class-files"
  :clean-targets [:target-path]

  :jvm-opts ["-Xms500m" "-Xmx4g"]
  )