Skip to content

Instantly share code, notes, and snippets.

@daveyarwood
Last active February 5, 2020 17:22
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daveyarwood/f890bf1529cb633c04b90ce5d5201d6d to your computer and use it in GitHub Desktop.
Save daveyarwood/f890bf1529cb633c04b90ce5d5201d6d to your computer and use it in GitHub Desktop.
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
Copy link

gtoast commented Feb 4, 2020

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

@daveyarwood
Copy link
Author

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
Copy link

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
Copy link
Author

Yes, exactly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment