NoClassDefFoundError: clout/core/CompiledRoute with Compjure, Liberator and Ring (possibly)

clojure

#1

Hi!

I am honestly out of ideas and I need help. Trying to start uberjar of my little program causes following stacktrace:

[[email protected] mysoftwared]$ java -jar target/uberjar/mysoftwared-0.1.0-SNAPSHOT-standalone.jar
Exception in thread "main" java.lang.ExceptionInInitializerError
        at clojure.lang.Namespace.<init>(Namespace.java:34)
        at clojure.lang.Namespace.findOrCreate(Namespace.java:176)
        at clojure.lang.Var.internPrivate(Var.java:153)
        at mysoftwared.core.<clinit>(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: clout/core/CompiledRoute, compiling:(user.clj:1:1)
        at clojure.lang.Compiler.load(Compiler.java:7526)
        at clojure.lang.RT.loadResourceScript(RT.java:379)
        at clojure.lang.RT.loadResourceScript(RT.java:366)
        at clojure.lang.RT.maybeLoadResourceScript(RT.java:362)
        at clojure.lang.RT.doInit(RT.java:482)
        at clojure.lang.RT.<clinit>(RT.java:336)
        ... 4 more
Caused by: java.lang.NoClassDefFoundError: clout/core/CompiledRoute
        at mysoftwared.api__init.__init0(Unknown Source)
        at mysoftwared.api__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:2204)
        at clojure.lang.RT.classForName(RT.java:2213)
        at clojure.lang.RT.loadClassForName(RT.java:2232)
        at clojure.lang.RT.load(RT.java:450)
        at clojure.lang.RT.load(RT.java:426)
        at clojure.core$load$fn__6548.invoke(core.clj:6046)
        at clojure.core$load.invokeStatic(core.clj:6045)
        at clojure.core$load.doInvoke(core.clj:6029)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invokeStatic(core.clj:5848)
        at clojure.core$load_one.invoke(core.clj:5843)
        at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
        at clojure.core$load_lib.invokeStatic(core.clj:5887)
        at clojure.core$load_lib.doInvoke(core.clj:5868)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invokeStatic(core.clj:659)
        at clojure.core$load_libs.invokeStatic(core.clj:5925)
        at clojure.core$load_libs.doInvoke(core.clj:5909)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invokeStatic(core.clj:659)
        at clojure.core$require.invokeStatic(core.clj:5947)
        at clojure.core$require.doInvoke(core.clj:5947)
        at clojure.lang.RestFn.invoke(RestFn.java:619)
        at mysoftwared.systems$loading__6434__auto____11944.invoke(systems.clj:1)
        at mysoftwared.systems__init.load(Unknown Source)
        at mysoftwared.systems__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:2204)
        at clojure.lang.RT.classForName(RT.java:2213)
        at clojure.lang.RT.loadClassForName(RT.java:2232)
        at clojure.lang.RT.load(RT.java:450)
        at clojure.lang.RT.load(RT.java:426)
        at clojure.core$load$fn__6548.invoke(core.clj:6046)
        at clojure.core$load.invokeStatic(core.clj:6045)
        at clojure.core$load.doInvoke(core.clj:6029)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load.doInvoke(core.clj:6029)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invokeStatic(core.clj:5848)
        at clojure.core$load_one.invoke(core.clj:5843)
        at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
        at clojure.core$load_lib.invokeStatic(core.clj:5887)
        at clojure.core$load_lib.doInvoke(core.clj:5868)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invokeStatic(core.clj:659)
        at clojure.core$load_libs.invokeStatic(core.clj:5925)
        at clojure.core$load_libs.doInvoke(core.clj:5909)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invokeStatic(core.clj:659)
        at clojure.core$require.invokeStatic(core.clj:5947)
        at clojure.core$require.doInvoke(core.clj:5947)
        at clojure.lang.RestFn.invoke(RestFn.java:482)
        at user$eval13$loading__6434__auto____14.invoke(user.clj:1)
        at user$eval13.invokeStatic(user.clj:1)
        at user$eval13.invoke(user.clj:1)
        at clojure.lang.Compiler.eval(Compiler.java:7062)
        at clojure.lang.Compiler.eval(Compiler.java:7051)
        at clojure.lang.Compiler.load(Compiler.java:7514)
        ... 9 more
Caused by: java.lang.ClassNotFoundException: clout.core.CompiledRoute
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 70 more

lein run and lein repl works just fine. I’ve tried to replicate it with different project, but without any success.

I don’t know what can be helpful, so I’d post some possibly important things (sorry for some obfuscation, but I can’t share it as-is):

[[email protected] hermesd]$ java -version
openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
[[email protected] hermesd]$ javac -version
javac 1.8.0_181
  • project.clj
(defproject mysoftwared "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.9.0"]
                 [org.clojure/test.check "0.9.0"]
                 [org.clojure/tools.cli "0.3.7"]
                 [org.clojure/tools.logging "0.4.1"]
                 [org.danielsz/system "0.4.1"]
                 [environ "1.1.0"]
                 [compojure "1.6.1"]
                 [ring/ring-core "1.6.3"]
                 [ring/ring-jetty-adapter "1.6.3"]
                 [liberator "0.15.2"]
                 [com.cognitect/transit-clj "0.8.309"]
                 [io.clojure/liberator-transit "0.3.1"]
                 [clj-systemd "0.1.0-SNAPSHOT"]
                 [expound "0.7.1"]
                 [me.raynes/fs "1.4.6"]]
  :plugins [[lein-ring "0.12.2"]
            [lein-environ "1.0.0"]]
  :ring {:handler mysoftwared.api/handler}
  :main ^:skip-aot mysoftwared.core
  :target-path "target/%s"
  :repl-options {:init-ns user}
  :profiles {:uberjar {:aot :all}
             :dev {:source-paths ["src" "test"]}})
  • api.clj

(ns mysoftwared.api
  (:require [clojure.java.io :as io]
            [clojure.spec.alpha :as spec]
            [cognitect.transit :as transit]
            [compojure.core :refer [ANY defroutes]]
            [environ.core :refer [env]]
            [expound.alpha :as expound]
            [mysoftwared.config.config :as config]
            [io.clojure.liberator-transit]
            [liberator.core :refer [defresource]]
            #_[liberator.dev :as lrdev]
            [ring.middleware.params :as ring-params]))


(defn- parse-transit
  "Parse transit data from `CTX` and verify it against `SPEC`"
  ([ctx spec]
   (when (#{:put :post} (get-in ctx [:request :request-method]))
     (try
       (let [body (-> (get-in ctx [:request :body]))
             data (when (.ready (io/reader body))
                    (-> body
                        (transit/reader :json)
                        (transit/read)))]
         (if (spec/valid? spec data)
           [false {::data data}]
           [true {:message (expound/expound-str spec data)}]))
       (catch Exception e
         (.printStackTrace e)
         [true {:message (format "Exception durign TRANSIT parsing: %s" (.getMessage e))}]))))
  ([ctx] (parse-transit ctx (constantly true))))

(defresource config-system
  :available-media-types ["application/transit+json"]
  :allowed-methods [:get :put]
  :respond-with-entity? true
  :malformed? #(parse-transit % ::config/system)
  :new? (constantly false) ;; `config/system` always exists
  :handle-ok (fn [_] (config/get-system (env :config-path)))
  :put! (fn [ctx] (config/change-system (env :config-path) (::data ctx))))


(defroutes app
  (ANY "/config/system" [] config-system))

(def handler
  (ring-params/wrap-params app)
  #_(if (env :dev)
      (lrdev/wrap-trace app :header :ui)
      (ring-params/wrap-params app)))
  • systems.clj
(ns mysoftwared.systems
  (:require [com.stuartsierra.component :as component]
            [mysoftwared.api :as api]
            [me.raynes.fs :as fs]
            [system.components.jetty :refer [new-web-server]]
            [clojure.spec.gen.alpha :as gen]
            [environ.core :refer [env]]
            [clojure.spec.alpha :as spec]))

(defn prod-system
  []
  (component/system-map :web (new-web-server (Integer/parseUnsignedInt (env :api-port))
                                             api/handler)))

  • core.clj

(ns mysoftwared.core
  (:gen-class)
  (:require [clojure.tools.cli :as cli]
            [mysoftwared.api :as api]
            [mysoftwared.systems :refer [prod-system]]
            [system.repl :refer [set-init! start]]
            [environ.core :refer [env]]))

(def cli-options
  [["-h" "--help"]])

(def help (format "* CONFIG_PATH: %s\n* API_PORT: %s\n"
                  (env :config-path)
                  (env :api-port)))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (let [{:keys [options summary errors]} (cli/parse-opts args cli-options)]
    (cond
      (:help options) (println help)
      :else (do (set-init! #'prod-system)
                (start)))))

I see no clue here. Do you have any ideas?

Thanks,
Slawek


#2

To me it looks like your user.clj somehow ended up in your uberjar. I’m not sure that is related though. You might also check using lein deps :tree whether clout is actually a dependency in your system, or if two of your declared dependencies are using conflicting versions of clout.

I’m sorry I cannot be of more help, I do not have access to my computer.


#3

Yes! Removing user.clj resolved the issue!

Now, why it is getting included?


#4

Followup:

So indeed user.clj got compiled:

$ lein uberjar       
[main] INFO org.eclipse.jetty.util.log - Logging initialized @8229ms                                                                                                                                               
Compiling user
(...)

Deleting user.clj works around this issue - but that seems bad.

So I tried tried this in project.clj:

  :profiles {:uberjar {:aot [#"mysoftwared\..*"]}

as expected, user is no longer reported as being compiled by lein uberjar but… trying to start this jar file still crashes. That’s weird!

EDIT: Things are getting ever weirder:
Renaming (file and namespace) user to dev also workrounds the problem… After that, there’s obvious:

$ lein uberjar; java -jar /home/otwieracz/Work/cyberrr/hermes/hermesd/target/uberjar/hermesd-0.1.0-SNAPSHOT-standalone.jar -h                                                 
Compiling dev
(...)

but uberjar loads just fine.


#5

Clojure uses the user.clj file, if it is on the classpath, to do some initialization of the user namespace. Usually you do not want user.clj to be on the classpath of deployment artifacts.

Without knowing the exact shape and placement of the user.clj in your project, I am guessing it is placed in src/user.clj. I would suggest that you move it to dev/user.clj, and modify the source paths of you :dev profile to point to dev/ as well as src/.

If I may make another suggestion: For clarity I think you should add a top-level :source-paths key that just includes src/, even though that is the default.