Clojure: How to include Resources with lein-uberjar - jar

I want to use a changeable file in clojure-project. (Manjaro Linux & Leiningen 2.8.0 on Java 1.8.0_144 OpenJDK 64-Bit Server VM)
So, I tried ... ($ echo resources/temp.txt => Hello )
(ns test.core
(:require [clojure.java.io :refer [writer input-stream]]
[clojure.java.io :as io])
(:gen-class))
(str (io/resource ""))
(defn -main
[]
(with-open [r (input-stream (io/resource "temp.txt"))]
(loop [c (.read r)]
(if (not= c -1)
(do
(print (char c))
(recur (.read r))))))
(with-open [r (writer (.getFile (io/resource "temp.txt")))]
(.write r "See you!"))
)
and project.clj is here ...
(defproject test "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.8.0"]]
:main test.core)
This program can run in lein-run
$ lein run
Hello
$
But this cannot run in lein-uberjar -> java -jar test-0.1.0-SNAPSHOT-standalone.jar
$ lein uberjar
$ java -jar test-0.1.0-SNAPSHOT-standalone.jar
Exception in thread "main" java.io.FileNotFoundException: /home/***/Documents/test/target/test-0.1.0-SNAPSHOT-standalone.jar!/temp.txt (
No such file or directory)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:270)
at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
at clojure.java.io$fn__9522.invokeStatic(io.clj:230)
at clojure.java.io$fn__9522.invoke(io.clj:230)
at clojure.java.io$fn__9459$G__9428__9466.invoke(io.clj:69)
at clojure.java.io$fn__9526.invokeStatic(io.clj:242)
at clojure.java.io$fn__9526.invoke(io.clj:240)
at clojure.java.io$fn__9459$G__9428__9466.invoke(io.clj:69)
at clojure.java.io$fn__9534.invokeStatic(io.clj:261)
at clojure.java.io$fn__9534.invoke(io.clj:259)
at clojure.java.io$fn__9459$G__9428__9466.invoke(io.clj:69)
at clojure.java.io$fn__9496.invokeStatic(io.clj:166)
at clojure.java.io$fn__9496.invoke(io.clj:166)
at clojure.java.io$fn__9472$G__9424__9479.invoke(io.clj:69)
at clojure.java.io$writer.invokeStatic(io.clj:119)
at clojure.java.io$writer.doInvoke(io.clj:104)
at clojure.lang.RestFn.invoke(RestFn.java:410)
at test.core$_main.invokeStatic(core.clj:15)
at test.core$_main.invoke(core.clj:7)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at test.core.main(Unknown Source)
How do I get correct path to it?
Thank you.

One problem is that .getFile doesn't work in a jar file, because you're reading from a zip file, not a directory structure on the file system.
Also, it's not recommended to change files inside a jar file (I'm not sure it's even possible). Also see Reading a resource file from within jar.

Related

Async Compojure-Api Middleware not Working

My middleware is throwing an error only for async requests, not sure why:
project.clj
(defproject asyncy "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.9.0"]
[metosin/compojure-api "1.1.11"]
[org.clojure/core.async "0.3.442"]]
:ring {:handler asyncy.handler/app}
:uberjar-name "server.jar"
:profiles {:dev {:dependencies [[javax.servlet/javax.servlet-api "3.1.0"]]
:plugins [[lein-ring "0.12.0"]]}})
handler.clj
To make this even easier to talk about and diagnose, i've made a bare minimum version of the problem using the lein new compojure-api template
(ns asyncy.handler
(:require [compojure.api.sweet :refer :all]
[ring.util.http-response :refer :all]
[schema.core :as s]
[clojure.core.async :as async]))
(s/defschema Pizza
{:name s/Str
(s/optional-key :description) s/Str
:size (s/enum :L :M :S)
:origin {:country (s/enum :FI :PO)
:city s/Str}})
(def app
(api
{:async? true
:swagger
{:ui "/"
:spec "/swagger.json"
:data {:info {:title "Asyncy"
:description "Compojure Api example"}
:tags [{:name "api", :description "some apis"}]}}}
(context "/api" []
:tags ["api"]
:middleware [(fn [handler]
;; this sync-style handler always handles both sync and async requests
(fn ([request]
(clojure.pprint/pprint request)
(if (some-> request :params :x (= "1"))
(bad-request {:error true :message "one is the loneliest number that you'll ever do"})
;; this throws an error on async requests
(handler request)))
;; this async 3-arity handler is never called
([request b c]
(clojure.pprint/pprint ["never called, doesnt matter." request b c]))
))]
(GET "/plus" []
:return {:result Long}
:query-params [x :- Long, y :- Long]
:summary "adds two numbers together"
(ok {:result (+ x y)}))
(GET "/plus-async" []
:return {:result Long}
:query-params [x :- Long, y :- Long]
:summary "adds two numbers together"
(fn [request respond raise]
(respond (ok {:result (+ x y)}))))
)))
(handler request) in the middleware is what throws the error, for async requests only:
ERROR Wrong number of args (1) passed to: handler/fn--26289/fn--26301/fn--26303
clojure.lang.ArityException: Wrong number of args (1) passed to: handler/fn--26289/fn--26301/fn--26303
at clojure.lang.AFn.throwArity(AFn.java:429)
at clojure.lang.AFn.invoke(AFn.java:32)
at compojure.response$eval1960$fn__1961.invoke(response.clj:47)
at compojure.response$eval1882$fn__1883$G__1873__1890.invoke(response.clj:7)
at compojure.core$wrap_response$fn__3839.invoke(core.clj:158)
at compojure.core$pre_init$fn__3938.invoke(core.clj:328)
at compojure.api.coerce$body_coercer_middleware$fn__14642.invoke(coerce.clj:51)
at compojure.core$pre_init$fn__3940$fn__3943.invoke(core.clj:335)
at compojure.core$wrap_route_middleware$fn__3823.invoke(core.clj:127)
at compojure.core$wrap_route_info$fn__3828.invoke(core.clj:137)
at compojure.core$wrap_route_matches$fn__3832.invoke(core.clj:146)
at compojure.core$wrap_routes$fn__3950.invoke(core.clj:348)
at compojure.api.routes.Route.invoke(routes.clj:74)
at compojure.core$routing$fn__3847.invoke(core.clj:185)
at clojure.core$some.invokeStatic(core.clj:2693)
at clojure.core$some.invoke(core.clj:2684)
at compojure.core$routing.invokeStatic(core.clj:185)
at compojure.core$routing.doInvoke(core.clj:182)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$apply.invoke(core.clj:652)
at compojure.core$routes$fn__3851.invoke(core.clj:192)
at asyncy.handler$fn__26289$fn__26290$fn__26291.invoke(handler.clj:32)
at compojure.core$routing$fn__3847.invoke(core.clj:185)
at clojure.core$some.invokeStatic(core.clj:2693)
at clojure.core$some.invoke(core.clj:2684)
at compojure.core$routing.invokeStatic(core.clj:185)
at compojure.core$routing.doInvoke(core.clj:182)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$apply.invoke(core.clj:652)
at compojure.core$routes$fn__3851.invoke(core.clj:192)
at compojure.core$make_context$handler__3919.invoke(core.clj:285)
at compojure.core$make_context$fn__3921.invoke(core.clj:293)
at compojure.api.routes.Route.invoke(routes.clj:74)
at compojure.api.core$handle$fn__14853.invoke(core.clj:8)
at clojure.core$some.invokeStatic(core.clj:2693)
at clojure.core$some.invoke(core.clj:2684)
at compojure.api.core$handle.invokeStatic(core.clj:8)
at compojure.api.core$handle.invoke(core.clj:7)
at clojure.core$partial$fn__5561.invoke(core.clj:2616)
at compojure.api.routes.Route.invoke(routes.clj:74)
at ring.swagger.middleware$wrap_swagger_data$fn__14015.invoke(middleware.clj:35)
at ring.middleware.http_response$wrap_http_response$fn__8034.invoke(http_response.clj:19)
at ring.swagger.middleware$wrap_swagger_data$fn__14015.invoke(middleware.clj:35)
at compojure.api.middleware$wrap_options$fn__14077.invoke(middleware.clj:74)
at ring.middleware.format_params$wrap_format_params$fn__7105.invoke(format_params.clj:119)
at ring.middleware.format_params$wrap_format_params$fn__7105.invoke(format_params.clj:119)
at ring.middleware.format_params$wrap_format_params$fn__7105.invoke(format_params.clj:119)
at ring.middleware.format_params$wrap_format_params$fn__7105.invoke(format_params.clj:119)
at ring.middleware.format_params$wrap_format_params$fn__7105.invoke(format_params.clj:119)
at compojure.api.middleware$wrap_exceptions$fn__14067.invoke(middleware.clj:43)
at ring.middleware.format_response$wrap_format_response$fn__7930.invoke(format_response.clj:194)
at ring.middleware.keyword_params$wrap_keyword_params$fn__8076.invoke(keyword_params.clj:36)
at ring.middleware.nested_params$wrap_nested_params$fn__8134.invoke(nested_params.clj:89)
at ring.middleware.params$wrap_params$fn__4079.invoke(params.clj:67)
at compojure.api.middleware$wrap_options$fn__14077.invoke(middleware.clj:74)
at compojure.api.routes.Route.invoke(routes.clj:74)
at clojure.lang.Var.invoke(Var.java:381)
at ring.middleware.reload$wrap_reload$fn__1829.invoke(reload.clj:39)
at ring.middleware.stacktrace$wrap_stacktrace_log$fn__1211.invoke(stacktrace.clj:26)
at ring.middleware.stacktrace$wrap_stacktrace_web$fn__1277.invoke(stacktrace.clj:96)
at ring.adapter.jetty$proxy_handler$fn__487.invoke(jetty.clj:25)
at ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$ff19274a.handle(Unknown Source)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:499)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:258)
at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
at java.lang.Thread.run(Thread.java:748)
How does one do middleware with async compojure-api? Feel like i'm just totally off on how to implement this.

How to search for files with a wildcard in Common Lisp?

I am not satisfied to find files matching a string like this:
(remove-if-not (lambda (it)
(search "wildcard" (namestring it)))
(uiop:directory-files "./"))
;; I'll ignore case with str:contains?
;; https://github.com/vindarel/cl-str
How would one search for files with unix-style wildcards ?
If it is not built-in, I'd enjoy a solution with uiop. Maybe there is with Osicat or cl-fad (with which it doesn't seem so, the documentation oftentimes says "non-wild pathname").
Bonus if it is possible to use the double wildcard to traverse directories recursively (./**/*.jpg).
edit: I have tried variants of (directory #p"./**/*.jpg") and it returns nil :( Also tried #p".*jpg", #p"./.*jpg",…
(wild-pathname-p (pathname "*.jpg"))
(:WILD :WILD-INFERIORS)
(make-pathname :name :wild :type "jpg")
#P"*.jpg"
The following gets me files by jpg extension, but it isn't a proper wildcard yet:
(directory *)
(#P"/home/vince/cl-cookbook/AppendixA.jpg"
#P"/home/vince/cl-cookbook/AppendixB.jpg"
#P"/home/vince/cl-cookbook/AppendixC.jpg")
Documentation on pathnames and make-pathname: http://gigamonkeys.com/book/files-and-file-io.html (no mentions of wildcards)
SBCL
SBCL supports wildcards in names. First, create some files:
(loop
with stem = #P"/tmp/stack/_.txt"
initially (ensure-directories-exist stem)
for name in '("abc" "def" "cadar" "cdadr" "cddr")
for path = (make-pathname :name name :defaults stem)
do (open path :direction :probe :if-does-not-exist :create))
Then, list all files that contains an "a":
CL-USER> (directory #P"/tmp/stack/*a*.txt")
(#P"/tmp/stack/abc.txt" #P"/tmp/stack/cadar.txt" #P"/tmp/stack/cdadr.txt")
The pathname contains an implementation-specific (valid) name component:
CL-USER> (describe #P"/tmp/stack/*a*.txt")
#P"/tmp/stack/*a*.txt"
[structure-object]
Slots with :INSTANCE allocation:
HOST = #<SB-IMPL::UNIX-HOST {10000F3FF3}>
DEVICE = NIL
DIRECTORY = (:ABSOLUTE "tmp" "stack")
NAME = #<SB-IMPL::PATTERN :MULTI-CHAR-WILD "a" :MULTI-CHAR-WILD>
TYPE = "txt"
VERSION = :NEWEST
; No value
SBCL also defines sb-ext:map-directory, which process files one by one, instead of first collecting all files in a list.
Portable solutions
If you need to stick to standard pathname components, you can first call directory with normal wildcards, and filter the resulting list:
CL-USER> (remove-if-not (wildcard "*a*")
(directory #P"/tmp/stack/*.txt")
:key #'pathname-name)
(#P"/tmp/stack/abc.txt" #P"/tmp/stack/cadar.txt" #P"/tmp/stack/cdadr.txt")
... where wildcard might be based on regex (PPCRE):
(defun parse-wildcard (string)
(delete ""
(map 'list
(lambda (string)
(or (cdr (assoc string
'(("*" . :wild)
("?" . :char))
:test #'string=))
string))
(ppcre:split '(:sequence
(:negative-lookbehind #\\)
(:register (:alternation #\* #\?)))
string
:with-registers-p t))
:test #'string=))
(note: the above negative lookbehind does not eliminate escaped backslashes)
(defun wildcard-regex (wildcard)
`(:sequence
:start-anchor
,#(loop
for token in wildcard
collect (case token
(:char :everything)
(:wild '(:greedy-repetition 0 nil :everything))
(t token)))
:end-anchor))
(defun wildcard (string)
(let ((scanner (ppcre:create-scanner
(wildcard-regex (parse-wildcard string)))))
(lambda (string)
(ppcre:scan scanner string))))
Intermediate functions:
CL-USER> (parse-wildcard "*a*a\\*a?\\?a")
(:WILD "a" :WILD "a\\*a" :CHAR "\\?a")
CL-USER> (wildcard-regex (parse-wildcard "*a*a\\*a?\\?a"))
(:SEQUENCE :START-ANCHOR #1=(:GREEDY-REPETITION 0 NIL :EVERYTHING) "a" #1# "a\\*a" :EVERYTHING "\\?a" :END-ANCHOR)
no current directory and no home directory characters
The concept of . denoting the current directory does not exist in portable Common Lisp. This may exist in specific filesystems and specific implementations.
Also ~ to denote the home directory does not exist. They may be recognized by some implementations as non-portable extensions.
In pathname strings you have * and ** as wildcards. This works in absolute and relative pathnames.
defaults for the default pathname
Common Lisp has *default-pathname-defaults* which provides a default for some pathname operations.
Examples
CL-USER 46 > (directory "/bin/*")
(#P"/bin/[" #P"/bin/bash" #P"/bin/cat" .... )
Now in above it is already slightly undefined or diverging what implementations do on Unix:
resolve symbolic links?
include 'hidden' files?
include files with types?
Next:
CL-USER 47 > (directory "/bin/*sh")
(#P"/bin/zsh" #P"/bin/tcsh" #P"/bin/sh" #P"/bin/ksh" #P"/bin/csh" #P"/bin/bash")
Using a relative pathname:
CL-USER 48 > (let ((*default-pathname-defaults* (pathname "/bin/")))
(directory "*sh"))
(#P"/bin/zsh" #P"/bin/tcsh" #P"/bin/sh" #P"/bin/ksh" #P"/bin/csh" #P"/bin/bash")
Files in your home directory:
CL-USER 49 > (let ((*default-pathname-defaults* (user-homedir-pathname)))
(directory "*"))
The same:
CL-USER 54 > (directory (make-pathname :name "*"
:defaults (user-homedir-pathname)))
Finding all files ending with sh in /usr/local/ and below:
CL-USER 54 > (directory "/usr/local/**/*sh")
Constructing pathnames with MAKE-PATHNAME
Three ways to find all .h files under /usr/local/:
(directory "/usr/local/**/*.h")
(directory (make-pathname :name :wild
:type "h"
:defaults "/usr/local/**/")
(directory
(make-pathname :name :wild
:type "h"
:directory '(:ABSOLUTE "usr" "local" :WILD-INFERIORS)))
Problems
There are a lot of different interpretations of implementations across platforms ('windows', 'unix', 'mac', ...) and even on the same platform (especially 'windows' or 'unix'). Stuff like unicode in pathnames creates additional complexity - not describe in the CL standard.
We still have a lot of different filesystems ( https://en.wikipedia.org/wiki/List_of_file_systems ), but they are different or different in capabilities from what was typical when Common Lisp was designed. Implementations may have tracked the changes, but not necessarily in portable ways.

clojure.java.io/resource returns nil

i need to load an fxml based JavaFX Scene in Clojure,
but when i try to load the resource "view.fxml" it returns nil.
Here is my present code:
(ns ui.ui_controller
(:import (javafx.application Application)
(javafx.fxml FXMLLoader)
(javafx.scene Scene)
(javafx.stage Stage)))
(gen-class
:name ui.ui_controller
:extends javafx.application.Application)
(defn -main [& args]
(Application/launch ui.ui_controller args))
(defn -start [this stage]
(let [loc (clojure.java.io/resource "view.fxml")
root (FXMLLoader/load loc)]
(.setScene stage (Scene. root))
(.setTitle stage "JavaFXML with Clojure Example")
(.show stage)))
And in the resources folder is the view.fxml file, which should be loaded.
When in call (println (clojure.java.io/resource "view.fxml")) it returns nil...
Any idea what goes wrong here?
Thanks!
Here is an example
(ns tst.clj.core
(:use clj.core
clojure.test )
(:require
[clojure.java.io :as io]
))
(def words (slurp (io/file (io/resource "count.txt"))))
(println words)
> ls -ldF resources/count.txt
-rw-rw-r-- 1 alan alan 14 Jan 3 09:01 resources/count.txt
> cat resources/count.txt
one
two
three
> lein test
one
two
three

Writing a JavaFX project in Clojure

I'm trying to understand how to properly setup JavaFX to work with a Clojure project. By reading various sources this is what I've come up with:
This is project.clj:
(defproject cljfx "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.8.0"]]
:resource-paths ["lib/jfxrt.jar"]
:main ^:skip-aot cljfx.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
I don't know if I should use :resource-paths or add JavaFX to the classpath via the :dependencies vector...
This is core.clj:
I've basically translated to Clojure an example from this tutorial:
http://docs.oracle.com/javafx/2/get_started/hello_world.htm
(ns cljfx.core
(:gen-class
:extends javafx.application.Application)
(:import
[javafx.application Application]
[javafx.stage Stage]
[javafx.scene Scene]
[javafx.scene.control Button]
[javafx.scene.layout StackPane]
[javafx.event ActionEvent EventHandler]))
(defn -main [& args]
(Application/launch cljfx.core args))
(defn button [text]
(doto (Button.)
(.setText (str "Say " text))
(.setOnAction (proxy [EventHandler] []
(handle [event]
(println text))))))
(defn -start [primaryStage]
(let [root (doto (StackPane.)
(-> (.getChildren)
(.add (button "Hello World!"))))]
(doto primaryStage
(.setScene (Scene. root 300 250))
(.show))))
This doesn't compile, and I don't know what I'm doing wrong... can you help me?
Here is the error:
http://pastebin.com/sYeK7MJd
There may be other problems, but the root problem in the pastebin log is:
Caused by: clojure.lang.ArityException: Wrong number of args (2) passed to: core/-start
When using gen-class and providing method implementations, every method needs to take the instance itself as the first parameter. The convention is to use "this":
(defn -start [this primaryStage]
Try that, and ensure that local instance method calls are applied to "this".

How to get the shell function's output in slime?

In REPL, we can get its output normally:
(shell "head -3 ~/misc.lisp")
(asdf:make-build 'stumpwm :type :program :monolithic t
:move-here "."
:name-suffix ""
However, we get nil from (shell "head -3 ~/misc.lisp") in slime.
Sincerely!
I don't know which Lisp implementation you're using, but normally you should need a package to run shell command like:
trivial-shell
inferior-shell
clesh (build on top of trivial-shell)
use #'run-shell-command instead of shell:
(let ((str (ext:run-shell-command cmd :output :stream)))
(loop for line = (read-line str nil)
until (null line)
do (print line)))
it works now.

Resources