I have a problem with importing jars in clojure.
I used lein to add dependencies.
This is code from project.clj
(defproject recommendation "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.5.1"]
[org.clojure/java.jdbc "0.0.6"] ;; jdbc
[mysql/mysql-connector-java "5.1.6"]]
:aot :all
:main recommendation.core)
I typed in the command lein deps, and it downloaded 3 jars in lib folder.
This is code from recommendation.core
(ns recommendation.core
(:require [clojure.java.jdbc :as sql]) )
And I get exception:
FileNotFoundException Could not locate clojure/java/jdbc__init.class or clojure/java/jdbc.clj on classpath: clojure.lang.RT.load (RT.java:443)
Can anybody tell me where i am wrong and what to do?
here's my dependency line:
[org.clojure/java.jdbc "0.2.3"]
Heres the require line from one of my projects that use jdbc
(:require [clojure.java [jdbc :as sql]])
If none of this helps here are my standard leiningen fixing steps:
lein deps and look for errors
lein clean
lein upgrade
lein deps :tree and look for strangeness
rm -rf target/ # this one has only helped me with messed up native deps.
rm ~/.m2/repository -rf # this is the last resort, it's lots of downloading.
join #leiningen on irc.freenode.net and ask for help
PS: I found this video helpful in getting a handle on namespaces.
update the dependencies line to the latest jdbc.java version:
https://github.com/clojure/java.jdbc
at the time of writing it is version 0.7.3:
:dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/java.jdbc "0.7.3"]]
Related
I have a program written in Clojure and JavaFX back in 2014. A dependency for the program was revised recently to use Java 17. Simply substituting the new version of the dependency produces an error related to not being able to read the new class file format. I would like to update the application but have not been able to generate an uberjar with current versions of Java (17) and JavaFX (17.0.1).
Here are the project.clj and source file for an SSCCE.
(defproject sutest "0.1.0-SNAPSHOT"
:description "Test for including JavaFX components in uberjar"
:dependencies [[org.clojure/clojure "1.10.3"]
[org.openjfx/javafx-controls "17.0.1"]]
:aot :all
:main sutest.core)
(ns sutest.core
(:gen-class
:extends javafx.application.Application)
(:import
[javafx.application Application Platform]
[javafx.event EventHandler]
[javafx.geometry Insets Pos]
[javafx.scene Scene]
[javafx.scene.control Button Label]
[javafx.scene.layout VBox]))
(defn -start [this stage]
(let [hiLbl (Label. "Hello World!")
exitBtn (Button. "Exit")
root (VBox. 12.0)]
(.setOnAction exitBtn (reify EventHandler (handle [_ _]
(Platform/exit))))
(.setPadding root (Insets. 0 10 0 10))
(.addAll (.getChildren root) [hiLbl exitBtn])
(.setAlignment root Pos/CENTER)
(.setScene stage (Scene. root 250 150)))
(.show stage))
(defn -main [& args]
(Application/launch sutest.core args))
The program works as expected when executed directly with lein run or from an IntelliJ IDEA/Cursive "run" configuration. Running lein uberjar completes without errors, but attempting to run the uberjar with
java -jar target/sutest-0.1.0-SNAPSHOT-standalone.jar
produces
Error: JavaFX runtime components are missing, and are required to run this application
I'm using Leiningen 2.9.6 because of this issue with 2.9.7. When running the program, Leiningen builds a classpath containing the needed jars in the local dependency repository.
I've seen a few questions about including the newer JavaFX modules in "fat" jars for Java and tried including them in the Leiningen build. Few related to Clojure and Java. For example, see the project.clj for the fn-fx library. That resorts to a special "leaky" profile to include the modules. That didn't work for me for some reason.
I've tried adding the modules to the IDEA/Cursive project. That correctly downloaded the modules in the project information, but still does not build an uberjar with the modules.
I've fiddled with the "Artifacts" section of the IDEA/Cursive project too. But that was unsuccessful.
There are tutorials on the Gluon site that walk through making a "fat" jar containing the JavaFX components, but those are directed towards Java projects.
Can Leiningen be used to create an uberjar containing the dependencies?
If not Leiningen, how about tools.deps or boot?
Has anyone been successful falling back to a plain Maven build by adapting the Gluon instructions?
After #jewelsea's suggestion, I looked at the contents of the uberjar created in the original question. I used
jar tvf jar tvf target/sutest-0.1.0-SNAPSHOT-standalone.jar
which produced pages and pages of class listings.
Near the top were the lines:
...
306 Sat Oct 23 15:23:46 EDT 2021 javafx-controls-17.0.1.jar
306 Sat Oct 23 15:23:46 EDT 2021 javafx-graphics-17.0.1.jar
302 Sat Oct 23 15:23:46 EDT 2021 javafx-base-17.0.1.jar
746012 Sat Oct 23 15:23:46 EDT 2021 javafx-base-17.0.1-mac.jar
2545243 Sat Oct 23 15:23:48 EDT 2021 javafx-controls-17.0.1-mac.jar
4852153 Sat Oct 23 15:23:48 EDT 2021 javafx-graphics-17.0.1-mac.jar
...
The same jars with the same sizes as used in the classpath built by the lein run command.
So, the JavFX module jars needed were already included in the uberjar.
That triggered a faint memory of a message thread about special treatment of JavaFX applications by the sun.launcher.LauncherHelper class of the java.base module. (The source for the Java 17 version of the class is here if you are interested.) The launcher does a special check of the main class being launched. If it extends javafx.application.Application, the JavaFX modules must be present on the module path, otherwise the error message in the original question is displayed and the launch is aborted. The message thread mentioned above and this StackOverflow answer give a much better explanation.
The net result is that you can't have the main program entry for a JavaFX "fat" jar extend from javafx.application.Application class.
Instead, you can use a "normal" main class that then calls the JavaFX part of the program.
The project listings below are slight edits of those in the original question and implement the solution described.
Here is the revised project.clj. Only the name of the :main class with the program entry point has changed.
(defproject sutest "0.1.0-SNAPSHOT"
:description "Test for including JavaFX components in uberjar"
:dependencies [[org.clojure/clojure "1.10.3"]
[org.openjfx/javafx-controls "17.0.1"]]
:aot :all
:main sutest.launcher)
Here is the new class used to launch the JavaFX portion:
(ns sutest.launcher
(:gen-class)
(:require [sutest.core :as sc]))
(defn -main [& args]
(sc/start-it args))
It calls into the sutest.core namespace with the new start-it function
Here is the revised version of the original JavaFX program. The start-it function launches things instead of the -main function that was used earlier. Note the change to the args parameters though. It no longer has the & rest signature. It must be a (possibly empty) vector of strings. The names of the variables for the label and button have been changed to reflect Clojure variable naming conventions.
(ns sutest.core
(:gen-class
:extends javafx.application.Application)
(:import
[javafx.application Application Platform]
[javafx.event EventHandler]
[javafx.geometry Insets Pos]
[javafx.scene Scene]
[javafx.scene.control Button Label]
[javafx.scene.layout VBox]))
(defn -start [_ stage]
(let [hi-lbl (Label. "Hello World!")
exit-btn (Button. "Exit")
root (VBox. 12.0)]
(.setOnAction exit-btn (reify EventHandler (handle [_ _]
(Platform/exit))))
(.setPadding root (Insets. 0 10 0 10))
(.addAll (.getChildren root) [hi-lbl exit-btn])
(.setAlignment root Pos/CENTER)
(.setScene stage (Scene. root 250 150)))
(.show stage))
(defn start-it [args]
(Application/launch sutest.core args))
That's it. Now you can build an uberjar and run it from the command line. Also, it can still be run directly from Leiningen with the lein run command and from within the IDE (after changing the name of the class with the entry point in the run configuration.)
This is a dirty hack
It is based on detailed knowledge of the internals of the launcher rather than using modules. When launched, the program still gives a warning like:
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module #25b2b199'
Hopefully it will work long enough to figure out how to use Java modules in "fat" jars in Clojure though.
I published some libraries using sbt publishLocal it worked and published to my ~/.ivy2/local dir.
I then have a project that depends on these libraries but sbt update can't find them.
specifically, my project has these dependencies:
lazy val myDependencies = commonDependencies ++ Seq(
"my.corp" %% "lib1" % "1.0.1-SNAPSHOT" withSources () withJavadoc (),
"my.corp" %% "lib2" % "2.0.2-SNAPSHOT" withSources () withJavadoc ()
)
sbt update has this error:
[error] (services / update) lmcoursier.internal.shaded.coursier.error.FetchError$DownloadingArtifacts: Error fetching artifacts:
[error] file:////home/myuser/.ivy2//local/my.corp/lib1_2.12/1.0.1-SNAPSHOT/jars/lib1.jar: not found: /home/myuser/.ivy2//local/my.corp/lib1_2.12/1.0.1-SNAPSHOT/jars/lib1.jar
[error] file://///home/myuser/.ivy2//local/my.corp/lib2_2.12/2.0.2-SNAPSHOT/jars/lib2.jar: not found: /home/myuser/.ivy2//local/my.corp/lib2_2.12/2.0.2-SNAPSHOT/jars/lib2.jar
when I look in the dir I can see the published jars but their name has the scala version appended, which is why the update resolving fails, I think :
$ ls /home/myuser/.ivy2/local/my.corp/lib1_2.12/1.0.1-SNAPSHOT/jars
lib1_2.12.jar lib1_2.12.jar.md5 lib1_2.12.jar.sha1
$ ls /home/myuser/.ivy2/local/my.corp/lib2_2.12/2.0.2-SNAPSHOT/jars
lib2_2.12.jar lib2_2.12.jar.md5 lib2_2.12.jar.sha1
If I publish to a repo for real I can resolve the libs.
Does anyone know the sbt incantation to fix this? ;-)
Cheers
Karl
Update:- I think coursier is the problem, not sure how to tell it to look for lib2_2_12.jar. Could it have a bad cached reference?
Caused by: lmcoursier.internal.shaded.coursier.cache.ArtifactError$NotFound: not found: /home/myuser/.ivy2/local/my.corp/lib2_2.12/2.0.2-SNAPSHOT/jars/lib2.jar
Update:-
disabling coursier worked
from the sbt REPL I can run
sbt:my-project> set ThisBuild / useCoursier := false
and then
sbt:my-project> update
and that worked, but setting it back to true update failed again, so this looks like a coursier issue.
Update:
coursier fetch from the command line worked!!
coursier fetch my.corp::lib1:1.0.1-SNAPSHOT --no-default -r central -r ivy2Local
downloaded the transitive dependencies and found my jar in my local ivy2 dir
so this is back to looking like an sbt - coursier interaction issue
I've just now run into this and after lots of diffs between an older working version and this non-working version I figured out that it is the withSources() that triggers the bug in sbt and/or coursier.
I have a Play server application.
Currently, I have 20-line bash script that creates this deb:
/srv
/foo
/conf
<unmanaged resources>
/staged
<jars>
I'd like to use sbt native packager to generate this.
Currently, sbt debian:package-bin gives me
etc/
default/
foo
foo
init/
foo.conf
usr/
bin/
foo
share/
foo/
bin/
foo
conf/
<unmanaged resources>
lib/
<jars>
share/
doc/
api/
<docs>
logs
README
var/
log/
foo/
How do I do I get my desired layout? Do I need to implement an archetype?
I'm using SBT 0.13.7 and SBT native packager 1.0.0-M1.
If your layout is close to the one already generated, you could use settings like defaultLinuxInstallLocation and defaultLinuxConfigLocation.
Or modify linuxPackageSymlinks and linuxPackageMappings directly, something like
linuxPackageSymlinks := Seq(),
linuxPackageMappings := {
val libPath = "/srv/foo/staged"
val libs = scriptClasspathOrdering.value.map { case (file, classpath) =>
file -> classpath.replaceFirst("^lib", Matcher.quoteReplacement(libPath))
}
Seq(LinuxPackageMapping(libs))
// plus configuration
},
If you have lots of binaries to archive (i.e. you have lots of dependencies), debian:packageBin is pretty slow. For debugging, consider using debianExplodedPackage.
Also, know that whatever is in the directory debianExplodedPackage will get included in the archive, so if there's extra stuff in the .deb at the end, you may need to delete that directory.
I have created a new project with
lein new jar-clash-test
cd jar-clash-test/
I have put the following in project.clj
(defproject jar-clash-test "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.5.0"]
[io.pedestal/pedestal.service "0.1.2"]
...]
:main ^{:skip-aot true} jar-clash-test.core
)
I have put the following in jar-clash-test/src/jar_clash_test/core.clj
(ns jar-clash-test.core
(:require [io.pedestal.service.http :as bootstrap]))
When I run this with
lein repl
I get the following error:
CompilerException java.lang.RuntimeException: No such var: content-type/content-type-response, compiling:(io/pedestal/service/http/ring_middlewares.clj:46:3)
When I look at:
/.m2/repository/io/pedestal/pedestal/0.1.2/pedestal.service-0.1.2/io/ring_middlewares.clj
On line 46 I see:
(leave-interceptor ::content-type-interceptor content-type/content-type-response opts))
Which is defined in the requirements as:
[ring.middleware.content-type :as content-type]
Which means it is trying to bring in the ring-middleware jar.
My hypothesis is that there is a jar version clash for the ring middleware implementation.
This is based on:
[compojure "1.1.3"] [has a dependency]2 on [ring/ring-core "1.1.5"]
[io.pedestal/pedestal.service "0.1.2"] [has a dependency on]3 [ring/ring-core "1.2.0-beta1"]
When I look at:
/.m2/repository/ring/ring-core/1.2.0-beta1/ring-core-1.2.0-beta1/ring/middleware/content_type.clj
The function
(defn content-type-response
exists. When I look at:
/.m2/repository/ring/ring-core/1.1.5/ring-core-1.1.5/ring/middleware/content_type.clj
The function doesn't exist.
My question is - how do I know which version lein has picked up? I can 'assume' it has picked up the earlier one - but how I can know for certain?
My second question is - how can I guarantee which one lein will pick?
You can say lein classpath to get a printout of the computed value of the classpath for your project. Earlier jars win. Another approach: examine the value of (System/getProperty "java.class.path") at the REPL.
If you want to pick a version of an indirect dependency by hand, make it direct, that is, add it to your project.clj; this entry will then override the dependencies' choices in the context of this project. Alternatively, you could add :exclusions to all but one of your dependencies which cause the indirect dependency to be pulled in.
So.. I have this problem (the one in the title). Just to give a background about what I did:
Create a Java class called Carro:
public class Carro{
public Carro(){}
public void turnon(String sound){
System.out.println(sound);
}
}
I've compile it:
javac Carro.java
And created a .jar:
jar -cf Carro.jar Carro.class
So, I created a new lein project:
lein new test
Created a /lib directory and pasted the Carro.jar in it.
Create a folder called carro in test/src/ directory and create a .clj file, called car.clj:
(ns carro.car
(:import [Carro] )
)
(defn callCarro []
(let [car (new Carro)]
(.turnon "vruuum!" car)
)
)
After all of that, I edited the project.clj file and add a :import [Carro] after the last parenthesis.
So, when I run the project using lein repl, I get this error:
$ lein repl
user=> (require 'carr.car :reload)
CompilerException java.lang.IllegalArgumentException:
Unable to resolve classname: Carro, compiling:(carro/car.clj:6)
Any ideas to solve this... Problem?
Leiningen 2 relies on maven to manage dependencies. Version 1 used to copy jars to the lib directory but this behavior was removed (see https://github.com/technomancy/leiningen/blob/stable/doc/FAQ.md). If you need a certain jar for your project, the easiest way is to install that jar on your local maven repo and let leiningen do the rest.
Maven (and so leiningen) needs a version, a group and an artifact for the jar you are working with (see https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md#artifact-ids-groups-and-versions). So our first step will be to rename Carro.jar to Carro-0.1.0.jar. To make things a little easier, lets use a leiningen plugin to do the rest. Add the lein-localrepo plugin to your project.clj, something like:
(defproject foo "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.4.0"]]
:plugins [[lein-localrepo "0.4.1"]])
Then you can ask leiningen for the coordinates of the Carro dependency:
$ lein localrepo coords Carro-0.1.0.jar
Carro-0.1.0.jar Carro/Carro 0.1.0
That last part is the info needed to install to the mvn repo:
$ lein localrepo install Carro-0.1.0.jar Carro/Carro 0.1.0
After that,the jar is installed in your mvn repository: ~/.m2/repository/Carro/Carro/0.1.0/Carro-0.1.0.jar. So finally, add the newly installed dependency to project.clj:
(defproject foo "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.4.0"]
[Carro/Carro "0.1.0"]]
:plugins [[lein-localrepo "0.4.1"]])
And then from the repl:
$ lein repl
user=> (new Carro)
#<Carro Carro#7c04703c>
I suspect there may be multiple causes:
Clojure has trouble loading classes without a package which causes this.
that is not the typical location for test classes if you are using Leiningen
If you didn't run (import 'Classname) from the repl you will need to do that as well.