This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-29
Channels
- # announcements (1)
- # babashka (15)
- # beginners (37)
- # calva (94)
- # cider (3)
- # clj-kondo (17)
- # cljsrn (2)
- # clojure (45)
- # clojure-europe (39)
- # clojure-germany (1)
- # clojure-norway (2)
- # clojurescript (16)
- # component (18)
- # conjure (1)
- # cursive (13)
- # datalevin (3)
- # datomic (12)
- # docker (2)
- # duct (5)
- # eastwood (2)
- # emacs (4)
- # events (8)
- # fulcro (8)
- # inf-clojure (5)
- # kaocha (8)
- # lsp (24)
- # malli (11)
- # meander (3)
- # off-topic (19)
- # polylith (11)
- # remote-jobs (4)
- # sci (61)
- # shadow-cljs (9)
- # spacemacs (34)
- # sql (10)
- # tools-deps (27)
- # xtdb (10)
I'm trying to create a component for my jetty webserver. My first try is this:
(defrecord Server [config http-server shutdown]
component/Lifecycle
(start [this]
(log/info ";; Starting Server" this http-server)
(if http-server
this
(assoc this
:http-server (ring.adapter.jetty/run-jetty app {:port 3000 :join? false})
:shutdown (promise))))
(stop [this]
(log/info ";; Stopping Server" this http-server)
(if http-server
(do
(.stop http-server)
(deliver shutdown true)
(assoc this :http-server nil))
this)))
(defn new-server [config]
(map->Server {:config config}))
Now this works fine when I start and stop the system however this is not idemponent. For some reason the (assoc this :http-server) thingie ain't working. So if I try to run (start) twice it will throw an error address already bound on the 2nd tryDo you have any idea what could be the problem? the http-server
is always nil !
is it possible that (ring.adapter.jetty/run-jetty app {:port 3000 :join? false})
returns nil ? if it returns nil how is it working here: https://github.com/seancorfield/usermanager-example/blob/develop/src/usermanager/main.clj#L164
No it doesn't return nil i checked it.
I'm trying toi debug that... I've added this:
(comment
(def s (new-server {}))
(.start s ))
When I run (.start s) the s
has the correct info (i.e http-server is not nil). However if i run (.start s) again it will get nil for the http-server and will throw the bind errorAm i doing something wrong? I understand that my example isn't working because of the immutability of s. But why the component isn't working?
For reference I'm using this lib https://github.com/stuartsierra/component.repl
@spapas A common pattern for REPL usage is this: https://github.com/seancorfield/usermanager-example/blob/develop/src/usermanager/main.clj#L218-L220
For a -main
function, you might do this instead: https://github.com/seancorfield/usermanager-example/blob/develop/src/usermanager/main.clj#L254-L259
Or you might do this sort of thing:
(defn -main
"Start Jetty and run auth Ring app."
[& [port]]
(let [port (or port (get (System/getenv) "PORT" 9110))
port (cond-> port (string? port) Integer/parseInt)
running-system (component/start (new-system port))]
(alter-var-root #'sys (constantly running-system))
(start-telemetry (:application running-system)
"api" "Application API" `probe)
;; wait for web server to shutdown
(-> running-system :web-server :shutdown deref)))
That's one of our actual applications at work that uses the same pattern. In both cases the running system is stored in a global purely so you can start the app with a Socket REPL (via a JVM option) and then connect into the app from an editor or from the command-line, and then inspect the state of the app and run its code.If you did:
(let [running-system (component/start (new-system ..))]
the-system (component/start running-system)]
...)
you should find the second call to start
will "work" and be a no-op.The key to this is that you must capture the result of calling start
(or stop
) and use it in the next expression that operates on it -- that's the "Clojure way" due to immutability. In your code, you're calling start
but "throwing away" the result and trying to start
s
again from its original state (and using .start
is not idiomatic for calling functions from protocols -- the .fn
approach is intended for Java interop).
Hope that helps?
Thank you @seancorfield for the help. The thing is that the https://github.com/stuartsierra/component.repl I'm using is supposed to behave this way ... Ie replace the system with the new version after calling start. Maybe I'm doing something wrong, I'll research it a bit more and if i can't fix it I'll do it manually as you propose
I avoid the "reloaded" stuff altogether -- we don't use that component.repl
stuff. We don't use tools.namespace
for it either.
Can you clarify why not?
Because a) it isn't needed b) it's fragile and can break in unexpected ways (I see a lot of beginners get into trouble with it) and c) it requires additional dependencies. Developing a good, REPL-friendly workflow without tools like that will benefit your life 🙂
Yes i guess you are right! I'll do it with the simple way at first so i can understand everything happening 😁 thank you