I've just started development of a service in Clojure. I'm a little lost in how to approach server shutdown.
I'm using Clojure 1.5.1. For logging, I'm using Timbre 1.5.3. I want to embed NREPL into my server for hot code deployment.
This is my core.clj file. Core is my main and I'm using the app shell that "lein new app" generates.
(ns extenium.core
(:require [taoensso.timbre :as t]
[clojure.tools.nrepl.server :as nrs])
(:gen-class))
(def nrepl-server)
(defn start-nrepl-server []
(t/info "Starting nrepl server on port 8628")
(alter-var-root #'nrepl-server
(constantly
(nrs/start-server :port 8628)))
(t/info "Started nrepl server"))
(defn stop-nrepl-server []
(t/info "Stopping nrepl server")
(nrs/stop-server nrepl-server)
(t/info "Stopped nrepl server"))
(defn start []
(alter-var-root #'*read-eval*
(constantly
false))
(start-nrepl-server))
(defn stop []
(stop-nrepl-server))
(defn -main [& args]
(start))
When I "lein run" the nrepl-server starts as expected, with info messages going to the Terminal. I then connect with "nrepl" from Emacs. No problem. From Emacs I execute "(extenium.core/stop)". At that point, I get the "Stopping nrepl server" message on Emacs (meaning that stdout was redirected to the client). The connection closes (as expected) and I never see the "Stopped nrepl server" message. Fine.
I look at the Terminal and I don't see the "Stopping..." or "Stopped..." messages. Instead, I get the following exception:
Exception in thread "nREPL-worker-0" java.lang.Error: java.net.SocketException: Socket closed
Ideally, I want the following:
To be able to initiate shutdown from an NREPL client. Gracefully close all NREPL client connections without exception. Shutdown with the logging messages directed towards the Terminal (or eventually, a rotating log file).
For the duration of the NREPL connection, clone the logging output to both the server (Terminal or log) and to the NREPL output.
Capture information about incoming NREPL connections, name them, and log connect and disconnect activity.
Eventually, authenticate incoming NREPL connections.
Even later, authorize them for actions they perform.
Disclaimer: I haven't done this, but it seems interesting.
Per the README, specifically the section "Why nREPL?" you should be able to implement 2 - 4 by implementing your own custom transport, perhaps merely extending one of the out-of-the-box ones.
Stopping the server gracefully may entail extending the socket transport. What you are describing as "graceful shutdown" sounds like implementation of a "Logout" protocol.
Authorizing actions they perform seems less straightforward. It looks like you would need to implement a custom Handler, perhaps again wrapping the default-handler with the authorization code.
Good luck!
Related
For a bit now I've just been running lein figwheel to start up my application. I generated the project using the luminus template. However, recently when connecting to a database I found that lein figwheel does not allow me to connect to database. I'm assuming this is because it does not initialize the app. Which is weird because it will reload my plain clj files with no problem.
However, if I just use lein run then I have the database connection.
After poking around I reread the Luminus docs and it says that I need to run both. And this is where I am very confused. lein run opens a port to 3000, and figwheel opens up a port to 3449. Like I said, the first lets me connect to the database and the other gives me autobuilding. Do I connect to both ports?
So what step am I missing to get both worlds?
Harley mentioned this in the comment to the other answer, but I wanted to vouch for it. First, run your server however you'd like (I launch from the repl, but you can also use lein run). Then use your browser navigate to whatever port you choose to run on. Then run lein figwheel in another terminal and it will connect.
I too, have never done both at the same time. However, I have done them separately and the different ports handle different "services". Port 3000 is the http port. If you built this from a luminus template, then you are probably building a web site. So, connect to 3000 with your browser.
Port 3449 is the web socket port that figwheel uses to push changes to the browswer. So,you start up a command-line lein figwheel an it goes looking for your 3449 port to talk to the browser. Now mind you, this command-line is both a repl and a monitor program. As you make changes to the source, figwheel recompiles your changes and sends the to the page at 3449. You can also execute code from the repl by changing the namespace of the repl (i.e. in-ns).
So, two different ports, two different protocols. What does port 3000 return to the browser? Well, depending on your server, whatever page you tell it to. Whereas, the 3449 is whatever figwheel defaults it to.
The real question is how to get figwheel to connect up to an existing page in a browser, not started by figwheel. I thought I had seen where someone had a JavaScript function that you embedded in your page and it talked/listened to figwheel.
I have a Clojure-based chat bot that I start up in typical leiningen fashion with lein run. I'd like to add a front end to this app, but not totally sure how to go about it. From reading docs on compojure, lib-noir and ring, looks like the standard way to serve is with lein ring server. I'd rather just start up the app and the front end with a single lein command if possible. Would this involve manually starting up the server (in another thread perhaps) with something like (run-jetty handler {:port 3000}), or could anyone recommend a better approach?
What do you understand under 'frontend' in your case? I see two possibilities.
First one is when your Clojure bot is completely standalone and has some external interface to interact with. In this case your frontend will be separate application talking with the bot via this external interface, and indeed in this case if you want to start your programs with single lein command you should be using explicit -main function in your webapp which will first run your bot and then start the server. I don't know exact command to start the server though; yours looks fine for me, but I think I read somewhere that this kind of startup was deprecated...
Another possibility is when you want the webapp to be integrated into the bot. In this case you just write the webapp in such way that it uses namespaces of the bot directly; no -main function is required, and all you have to do is run the lein ring server command.
The second one looks clearer to me, but it depends on overall architecture of your bot.
Update.
I have looked more thoroughly at how ring and leiningen work together, and it seems that the simplest way for you to get what you want is as follows. First, install lein-ring plugin as its readme directs.
Next, configure your project.clj similarly to the following:
(defproject your-project "0.0.1"
:dependencies [...]
... ; All other configuration
:ring {:handler your-namespace.web/handler
:init your-namespace.bot/init})
See, you should have additional options in your project.clj file (they are described in the readme I have linked to above). :handler is your main web application handler (refer to ring documentation on what it is and why it is needed). :init should be your initialization function. This is exactly the place you should add the code to to start your bot.
Finally, issue lein ring server command to start your webapp. This will first call a function you specified as :init in your project.clj, which in turn will start your bot, and then your webapp will be launched.
I am trying to connect to a remote Redis server, but am having extreme trouble with figuring out how to set it up. I can telnet into the Redis server perfectly fine. From Redis-cli I can connect to the Redis server fine.
But when I setup Redis as I am below I just get:
ConnectException Connection refused java.net.PlainSocketImpl.socketConnect (PlainSocketImpl.java:-2)
(require clj-redis.client :as redis)
(def db (redis/init :url "redis://0.0.0.0:6379))
I've also tried it without the "redis://" section of the url and got the same results.
Does anyone have an idea of what is going on or things to try? Thanks.
I don't know redis specifically but the 0.0.0.0 ip address in the url looks odd. 0.0.0.0 is a non-routable meta-address used to designate an invalid, unknown or non applicable target. When opening a (server) socket for listening you'd do this to signal that the socket should be bound to all ip addresses on the machine, but as a client it doesn't make sense.
;If your redis server is running locally you should do
(require clj-redis.client :as redis)
(def db (redis/init :url "redis://127.0.0.1:6379"))
; better? probably leave out the default port...
(require clj-redis.client :as redis)
(def db (redis/init :url "redis://localhost"))
;if it's remote change ``127.0.0.1`` to the correct address.
i'm using accession
(def c (accession.core/connection-map {})) ;; {} - default settings
and then
(with-connection c "... redis-cmd")
I'm wondering if I can make a Vim plugin that sends forms over to the REPL session that lein repl starts. Is there a way to send messages to an existing jline.ConsoleRunner process?
Slightly OT, but this is what nrepl is for. IIUC, vimclojure uses nrepl now for the REPLs it starts, and allows you to connect to remote Clojure processes that have started an nrepl server. I don't think Leiningen has an nrepl plugin, though (yet).
You may find the slimv vim plugin helpful.
see this SO question Sending input to a screen window from vim
A general approach is to start a screen session run lein repl, then sent the vim output to the screen session.
https://github.com/sattvik/lein-tarsier does more or less that ... I don't know if it's using nrepl or not, but with lein-tarsier you can have vimclojure talk via nailgun to a JVM which is also running a REPL. HTH
I am writing a web application using ring and clojure. I am using the jetty adapter for the development server and emacs/SLIME for IDE. While wrap-reload does help, run-jetty blocks my slime session and I would like to be able to start/stop it at will without having to run it in a separate terminal session. Ideally, I would like to define a server agent and functions start-server and stop-server that would start/stop the server inside the agent. Is this possible?
I usually have a line in my Ring app that looks like the following:
(defonce server (run-jetty #'my-app {:port 8080 :join? false}))
This prevents locking up the REPL. It also allows me to recompile this file without worrying that my server will get redefined. It also lets you interact at the REPL like so:
user=> (.stop server)
and
user=> (.start server)
The Jetty documentation has some information on graceful shutdown of Jetty. That's probably not enough information but it may get you started.
I haven't started playing with compojure yet, but overall I prefer to work with Tomcat. It's more full-featured; among other things, there is a well-documented API for starting it up and shutting it down, it listens for the shutdown command on a dedicated port; there are ant tasks to do this, and they could of course be called from a Java app as well. I just don't know what kind of magic Compojure does with connecting the REPL to a running instance of the Web container, and if/how automatic class reloading happens... hopefully someone else will be able to provide more information.
12 years later.
VSCode / Calva:
If you are like me starting jetty from the repl inside VSCode / Calva
you have to CTRL-C the server process at the terminal not the REPL.
In fact the server process is bound to the terminal not to the REPL.