Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Dave's setup for starting a prepl server in tools.deps projects
;; ~/.config/conjure/conjure.edn
{:conns
;; My `clj -Aprepl-server` alias spits out a `.socket-port` file when it starts
;; a prepl server. This configuration allows Conjure to find the prepl server
;; without needing to specify the port explicitly.
{:cwd {:port #slurp-edn ".socket-port"}}}
$ tree ~/.clojure
/home/dave/.clojure
├── deps.edn
└── src
└── dave
└── prepl_server.clj
2 directories, 2 files
{:deps
{org.clojure/clojure {:mvn/version "1.10.1"}}
:aliases
{:prepl-server
{:extra-paths ["/home/dave/.clojure/src"]
:main-opts ["-m" "dave.prepl-server"]}}}
(ns dave.prepl-server
(:require [clojure.core.server :as server]
[clojure.java.io :as io])
(:import [java.net ConnectException Socket]))
(defn port-listening?
[host port]
(try
(.close (Socket. host port))
true
(catch ConnectException _ false)))
(defn- log
[& args]
(println (apply format args)))
(defn start-prepl-server!
[port]
(let [host "localhost"]
(when (port-listening? host port)
(log "Unable to start prepl server on port %d; that port is in use." port)
(System/exit 1))
(let [socket (server/start-server
{:accept `server/io-prepl
:address host
:port port
:name "Dave's amazing prepl server"})
effective-port (.getLocalPort socket)]
(doto (io/file ".socket-port")
.deleteOnExit
(spit effective-port))
(log "Started prepl server on port %d." effective-port))
;; Wait until process is interrupted.
@(promise)))
(defn -main
[& [port]]
(start-prepl-server! (if port (Integer/parseInt port) 0)))
# Run this in the root directory of any project with a deps.edn
$ clj -Aprepl-server
@gtoast

This comment has been minimized.

Copy link

@gtoast gtoast commented Feb 4, 2020

Can you explain why your evaluate true in your try/catch on line 10 of prepl_server.clj?

@daveyarwood

This comment has been minimized.

Copy link
Owner Author

@daveyarwood daveyarwood commented Feb 5, 2020

port-listening? attempts to connect to the port in order to determine if the port is listening.

Creating a Socket with that host and port as arguments will either connect successfully or it will throw a ConnectException. If it throws a ConnectException, we catch it and return false because we can conclude that the port is not listening.

We don't want the Socket hanging around, so we close it immediately. Then we return true because if we got that far (i.e. a ConnectException was not thrown), then we can conclude that the port is listening.

@gtoast

This comment has been minimized.

Copy link

@gtoast gtoast commented Feb 5, 2020

Ah, I think I get it. You need to return a truthy value when closing the port succeeds, and the .close() method on Socket instances has a void return value which probably translates to a null value in Clojure, so you throw in a true. As the last evaluation in the function, that true becomes the return value for a successful call to port-listening?.

Thank you!

@daveyarwood

This comment has been minimized.

Copy link
Owner Author

@daveyarwood daveyarwood commented Feb 5, 2020

Yes, exactly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.