How does one send a message to a running clojure application? For example, if I have a particular variable or flag I want to set from the terminal when the uberjar is running - is this possible?
One way is to read a file in the application that you can change, but that sounds clunky.
Thanks in advance!
One way to do this is by having your application host a nREPL (network REPL). You can then connect to your running app's nREPL and mess around.
For example, your app may look like this:
(ns sandbox.main
(:require [clojure.tools.nrepl.server :as serv]))
(def value (atom "Hey!"))
(defn -main []
(serv/start-server :port 7888)
(while true
(Thread/sleep 1000)
(prn #value)))
While that's running you can lein repl :connect localhost:7888 from elsewhere and change that value atom:
user=> (in-ns 'sandbox.main)
#object[clojure.lang.Namespace 0x12b094cf "sandbox.main"]
sandbox.main=> (reset! value "Bye!")
"Bye!"
By this time the console output of your app should look like this:
$ lein run
"Hey!"
"Hey!"
<...truncated...>
"Bye!"
"Bye!"
There are many options for interprocess communication on the JVM, but this approach is unique to Clojure.
Related
With the mount library, how do I reload (stop and start) an http-kit "mount state" on a -main function?
My current code is this:
(defstate server-config :start {:port 7890 :join? false})
(defn start-server [server-config]
(when-let [server (run-server myserv-ring-handler server-config)]
(println "Server has started!")
server))
(defstate myserv-server :start (start-server server-config)
:stop (myserv-server :timeout 100))
(defn system-port [args]
(Integer/parseInt
(or (System/getenv "PORT")
(first args)
"7890")))
(defn -main [& args]
(mount/start-with-states
{#'myserv/server-config
{:start #(array-map :port (system-port args)
:join? false)}}))
So when I "lein run" everything works, but whenever I change a file, and the http-kit server is stopped, the command stops. For the moment I'm doing "while true; do lein run; done" to work, so I've thought about adding an infinite loop to the -main function, but it doesn't feel like this is the right way.
How should I do this?
I would suggest adding some metadata to your http server defstate.
From the mount readme:
In case nothing needs to be done to a running state on reload /
recompile / redef, set :on-reload to :noop:.
So try something like this:
(defstate ^{:on-reload :noop}
myserv-server
:start (start-server server-config)
:stop (my-stop-func myserv-server))
This means that when you change a file, the affected code will be reloaded, but the http server will continue to run.
I hope that I've correctly understood your question and that this is what you wanted.
Could I also suggest that if you want to get up and running quickly, then there are various templated web app projects for Leiningen. For example, the Luminus project. You can pass an +http-kit parameter to the lein new luminus myapp command and that will wire up an app correctly for you. You can then go and read the generated code and learn how it all fits together.
So I had a couple of separate problems:
I didn't understand that now I didn't need to use lein run, but instead I could just do lein repl and start the server from there. The restarting problem is avoided that way.
The other one was that I was misusing start-with-states instead of a config state.
You can see a discussion about this with the author of the library here.
I've written a relatively simple HTTP server using Clojure's Aleph library. It's not very complicated:
(ns cxpond.xmlrpc.core
(:gen-class)
(:require [aleph.http :as http]))
(defn handler [req]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "HELLO, WORLD!"})
(defn -main [& args]
(http/start-server service/handler {:port 8005}))
Obviously it's pretty simple, and follows the example given in Aleph's doc pretty closely. It compiles fine, but when I run it (via lein run) it just...does nothing. The program just exits immediately; obviously it doesn't listen on port 8005 or anything like that. What am I missing here? Clearly there must be something else I need to do to start a server in Aleph.
You'll want to call 'aleph.netty/wait-for-close' on the value returned by 'start-server' to block until the server is closed.
http/start-server doesn't block, just returns an object, so with nothing else to do execution of -main finishes and the program ends.
I don't use aleph and don't see an obvious join-like pattern. It looks as though one has to do one's own lifecycle management, then call .close on the object returned from start-server to gracefully shut down.
The following program, when run from an überjar, exits at the end only when using the in-memory Datomic database; when connecting to the Datomic server, it hangs indefinitely rather than exiting the JVM:
(ns myns.example
(:use [datomic.api :only [db q] :as d])
(:gen-class))
;; WORKS: (def uri "datomic:mem://testdb")
(def uri "datomic:free://localhost:4334/testdb2")
(defn -main []
(println 1)
(when (d/create-database uri)
(d/connect uri))
(shutdown-agents)
(println 2))
Run as:
lein uberjar && java -cp target/myns-0.1.0-SNAPSHOT-standalone.jar myns.example
Outputs:
1
2
and hangs. It only hangs if the DB doesn't exist when the program starts.
Anyone know why, or how to fix? This is with both datomic-free-0.8.4020.26 and datomic-free-0.8.3941.
UPDATE -- the above program does actually terminate, but it takes a very long time (> 1 minute). I'd like to know why.
shutdown-agents takes up to one minute to complete (assuming no agents are running an action).
This is due to the way java.util.concurrent cached thread pools work.
Use datomic.api/shutdown
shutdown
function
Usage: (shutdown shutdown-clojure)
Shut down all peer
resources. This method should be called as part of clean shutdown of
a JVM process. Will release all Connections, and, if shutdown-clojure
is true, will release Clojure resources. Programs written in Clojure
can set shutdown-clojure to false if they manage Clojure resources
(e.g. agents) outside of Datomic; programs written in other JVM
languages should typically set shutdown-clojure to true.
Added in Datomic Clojure version 0.8.3861
(ns myns.example
(:require [datomic.api :as d])
(:gen-class))
(def uri "datomic:free://localhost:4334/testdb2")
(defn -main []
(d/create-database uri)
(let [conn (d/connect uri)]
(try
;; do something
(finally (d/shutdown true)))
A Clojure-based project of mine uses the netty (required by aleph) web server. I start the server, along with other components, in a web.clj file like this:
(ns myproject.web)
(def server (atom nil))
(defn initialize []
(if #server
(println "Warning: already initialized")
(let [port 8001]
(println (format "Starting http://localhost:%s/" port))
(swap! server (fn [_] (start-http-server
(wrap-ring-handler app-routes)
{:port port}))))))
(defn shutdown []
(when #server
(do
(println "Shutting down web server")
(#server)
(swap! server (fn [_] nil)))))
(defn reinitialize []
"Run this on the REPL to reload web.clj and restart the web server"
(myproject.web/shutdown)
(use :reload-all 'myproject.web)
(myproject.web/initialize))
The server instance is stored in a Clojure atom, so that it can be stopped later.
I use Emacs and Swank to directly launch the server on the REPL like this (after compiling web.clj with C-c C-k):
user> (myproject.web/initialize)
Whenever web.clj or other dependent modules are edited, I must
remember NOT to recompile web.clj using C-c C-k because the atom holding the running instance would vanish (due to the atom from a newly compiled module) from the REPL.
Run (myproject.web/reinitialize) which stops the server and then reloads the module before starting it again.
There are two problems with this:
Often I forget point #1 and press C-c C-k anyway. This causes the loss of server atom in REPL, leading to having to kill swank (or restart emacs) so that I can start the server at the same port number.
:reload-all doesn't report compile errors as friendly as C-c C-k (ugly traceback vs concise clickable errors).
How can I best address these two problems in this edit-compile-restart workflow?
You can replace
(def server (atom nil))
with
(defonce server (atom nil))
that way when you evaluate the buffer it will not redefine server.
for your first problem you can store the atom in a different namespace and on load only overwrite it if it is not yet defined. putting it in it's own namepspace will prevent it from being erased by the reload-all
I've written a small Swing App before in Clojure and now I'd like to create an Ajax-style Web-App. Compojure looks like the best choice right now, so that's what I'm going to try out.
I'd like to have a real tiny edit/try feedback-loop, so I'd prefer not to restart the web server after each small change I do.
What's the best way to accomplish this? By default my Compojure setup (the standard stuff with ant deps/ant with Jetty) doesn't seem to reload any changes I do. I'll have to restart with run-server to see the changes. Because of the Java-heritage and the way the system is started etc. This is probably perfectly normal and the way it should be when I start the system from command-line.
Still, there must be a way to reload stuff dynamically while the server is running. Should I use Compojure from REPL to accomplish my goal? If I should, how do I reload my stuff there?
This is quite an old question, and there have been some recent changes that make this much easier.
There are two main things that you want:
Control should return to the REPL so you can keep interacting with your server. This is accomplished by adding {:join? false} to options when starting the Jetty server.
You'd like to automatically pick up changes in certain namespaces when the files change. This can be done with Ring's "wrap-reload" middleware.
A toy application would look like this:
(ns demo.core
(:use webui.nav
[clojure.java.io]
[compojure core response]
[ring.adapter.jetty :only [run-jetty]]
[ring.util.response]
[ring.middleware file file-info stacktrace reload])
(:require [compojure.route :as route] view)
(:gen-class))
; Some stuff using Fleet omitted.
(defroutes main-routes
(GET "/" [] (view/layout {:body (index-page)})
(route/not-found (file "public/404.html"))
)
(defn app
[]
(-> main-routes
(wrap-reload '(demo.core view))
(wrap-file "public")
(wrap-file-info)
(wrap-stacktrace)))
(defn start-server
[]
(run-jetty (app) {:port 8080 :join? false}))
(defn -main [& args]
(start-server))
The wrap-reload function decorates your app routes with a function that detects changes in the listed namespaces. When processing a request, if those namespaces have changed on disk, they are reloaded before further request processing. (My "view" namespace is dynamically created by Fleet, so this auto-reloads my templates whenever they change, too.)
I added a few other pieces of middleware that I've found consistently useful. wrap-file handles static assets. wrap-file-info sets the MIME type on those static assets. wrap-stacktrace helps in debugging.
From the REPL, you could start this app by using the namespace and calling start-server directly. The :gen-class keyword and -main function mean that the app can also be packaged as an uberjar for startup from outside the REPL, too. (There's a world outside the REPL? Well, some people have asked for it anyway...)
Here's an answer I got from James Reeves in the Compojure Google Group (the answer's here with his permission):
You can reload a namespace in Clojure using the :reload key on the use
or require commands. For example, let's say you have a file "demo.clj" that contains your routes:
(ns demo
(:use compojure))
(defroutes demo-routes
(GET "/"
"Hello World")
(ANY "*"
[404 "Page not found"]))
At the REPL, you can use this file and start a server:
user=> (use 'demo)
nil
user=> (use 'compojure)
nil
user=> (run-server {:port 8080} "/*" (servlet demo-routes))
...
You could also put the run-server command in another clojure file.
However, you don't want to put it in the same file as the stuff you want to reload.
Now make some changes to demo.clj. At the REPL type:
user=> (use 'demo :reload)
nil
And your changes should now show up on http://localhost:8080
I wanted to add an answer, since things have changed a bit since the newest answer and I had spent a bit of time looking for this myself.
Install leiningen (just follow the instructions there)
Create project
lein new compojure compojure-test
Edit the ring section of project.clj
:ring {:handler compojure-test.handler/app
:auto-reload? true
:auto-refresh? true}
Start the server on whatever port you want
lein ring server-headless 8080
Check that the server is running in your browser, the default base route should just say "Hello world". Next, go modify your handler (it's in src/project_name). Change the hello world text, save the file and reload the page in your browser. It should reflect the new text.
Following up on Timothy's link to Jim Downing's setup, I recently posted on a critical addition to that baseline that I found was necessary to enable automatic redeployment of compojure apps during development.
I have a shell script that looks like this:
#!/bin/sh
CLASSPATH=/home/me/install/compojure/compojure.jar
CLASSPATH=$CLASSPATH:/home/me/clojure/clojure.jar
CLASSPATH=$CLASSPATH:/home/me/clojure-contrib/clojure-contrib.jar
CLASSPATH=$CLASSPATH:/home/me/elisp/clojure/swank-clojure
for f in /home/me/install/compojure/deps/*.jar; do
CLASSPATH=$CLASSPATH:$f
done
java -server -cp $CLASSPATH clojure.lang.Repl /home/me/code/web/web.clj
web.clj looks like this
(use '[swank.swank])
(swank.swank/ignore-protocol-version "2009-03-09")
(start-server ".slime-socket" :port 4005 :encoding "utf-8")
Whenever I want to update the server I create an ssh tunnel from my local machine to the remote machine.
Enclojure and Emacs (running SLIME+swank-clojure) can connect to the remote REPL.
This is highly configuration dependent but works for me and I think you can adapt it:
Put compojure.jar and the jars under the compojure/deps directory are in your classpath. I use clojure-contrib/launchers/bash/clj-env-dir to do this, all you need to do is set the directory in CLOJURE_EXT and it will find the jars.
CLOJURE_EXT Colon-delimited list of paths to directories whose top-level
contents are (either directly or as symbolic links) jar
files and/or directories whose paths will be in Clojure's
classpath.
Launch clojure REPL
Paste in hello.clj example from compojure root directory
Check localhost:8080
Re-define the greeter
(defroutes greeter
(GET "/"
(html [:h1 "Goodbye World"])))
Check localhost:8080
There are also methods for attaching a REPL to an existing process, or you could keep a socket REPL embedded in your server or you could even define a POST call that will eval on the fly to allow you to redefine functions from the browser itself! There are lots of ways to approach this.
I'd like to follow up on mtnygard's answer and post the full project.clj file and core.clj file that got the given functionality working. A few modifications were made, and it's more barebones
pre-setup commands
lein new app test-web
cd test-web
mkdir resources
project.clj
(defproject test-web "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"]
[compojure "1.1.6"]
[ring "1.2.1"]]
:main ^:skip-aot test-web.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
core.clj
(ns test-web.core
(:use
[clojure.java.io]
[compojure core response]
[ring.adapter.jetty :only [run-jetty]]
[ring.util.response]
[ring.middleware file file-info stacktrace reload])
(:require [compojure.route :as route])
(:gen-class))
(defroutes main-routes
(GET "/" [] "Hello World!!")
(GET "/hello" [] (hello))
(route/not-found "NOT FOUND"))
(def app
(-> main-routes
(wrap-reload '(test-web.core))
(wrap-file "resources")
(wrap-file-info)
(wrap-stacktrace)))
(defn hello []
(str "Hello World!"))
(defn start-server
[]
(run-jetty #'app {:port 8081 :join? false}))
(defn -main [& args]
(start-server))
Pay Attention to the change from (defn app ...) to (def app ...)
This was crucial to getting the jetty server to work correctly
Compojure uses ring internally (by the same author), the ring web server options allow automatic realoading. So two alternatives would be :
lein ring server
lein ring server-headless
lein ring server 4000
lein ring server-headless 4000
Note that :
You need to have a line in your project.clj file that looks like:
:ring {:handler your.app/handler}