I have a clojure ring project and I want to be able to set the port number based on the profile. Currently I have the following snippet from project.clj
:plugins [[lein-ring "0.8.13"]]
:ring {:handler project.handler/webServer
:init project.init/initialize
:port 80}
:profiles {:dev {:jvm-opts ["-Dproperty-file=dev.properties"]}
:ci {:jvm-opts ["-Dproperty-file=ci.properties"]}
:uberjar {:aot :all}})
What I would like to do is to set the port to 8080 for development environments and then port 80 for the production environment. I would run on port 80 all the time but that requires root privilege and not something I want to do for a dev run. I have tried (blindly) to put the ring port into the uberjar profile but that didn't work. I also tried to use the environ project to set the ring port based on the environment variable but that didn't work either.
I am open to a solution that passes command line arguments to the java -jar [...]-standalone.jar command but I am stuck on how to get any approach to work.
You don't need environ. Use it when you need to access configuration variables in the source code. In project.clj you could directly do this:
:profiles {:dev {:jvm-opts ["-Dproperty-file=dev.properties"]
:ring {:port 8080}}
:ci {:jvm-opts ["-Dproperty-file=ci.properties"]
:ring {:port 80}}
:uberjar {:aot :all
:ring {:port 80}}})
I have tested this (without jvm-opts and port 8081 instead of 80) and it works.
Alternative: if they are different machines, you could use the OS's environment variables:
:ring {:handler project.handler/webServer
:init project.init/initialize
:port ~(System/getenv "RING_PORT")}
And then set RING_PORT to 8080 in your dev machine and 80 on the production machine.
$ export RING_PORT=80
It seems the alternative version doesn't work as stated. I get the exception "java.lang.String cannot be cast to java.lang.Number". Apparently we have to explicitly parse the value environment variable as integer, but then we also have to catch possible errors. Code that works for me
:port ~(try
(Integer/valueOf
(System/getenv "RING_PORT"))
(catch Exception e 3000))
Related
A) I'm having a problem where Cider can't cider-connect-clj (or cider-connect-cljs) to an nREPL server I launch in my application code. Cursive also doesn't work. It must be a simple configuration or middleware that needs to be included. But I've been looking at this code too long. What I missing?
I have a Clojure file that looks something like this.
(ns some.namespace
(:require nrepl.server
cider.nrepl))
(nrepl.server/start-server
:port 1234
:handler
(fn [_]
(nrepl.server/default-handler cider.nrepl/cider-middleware)))
nrepl.server/start-server starts ok.
From Emacs I can connect by calling cider-connect-clj (or cider-connect-cljs). But then the REPL fails to initialize on a "sync" request.
i. Emacs console
[nREPL] Direct connection to localhost:6776 established
nrepl-send-sync-request: Sync nREPL request timed out (op clone id 1 time-stamp 2021-02-04 23:13:46.789763000)
ii. nREPL console
[WARNING] No nREPL middleware descriptor in metadata of null, see nrepl.middleware/set-descriptor!
B) I'm actually running Figwheel-Main and connecting an nREPL, for a Clojurescript connection. All within my Clojure app.
;; λ clj -A:dev -m some.namespace
:dev
{:extra-paths ["dev" "test"]
:extra-deps {org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.10.520"}
com.bhauman/figwheel-main {:mvn/version "0.2.0"}
nrepl/nrepl {:mvn/version "0.8.3"}
cider/cider-nrepl {:mvn/version "0.25.8"}
cider/piggieback {:mvn/version "0.4.2"}}}
I don't think the Figwheel portion is affecting cider's attempts to connect. But I'm including it just for context.
(defn -main [& args]
;; Start Figwheel Server
(figwheel.main.api/start
{:mode :serve} "dev")
;; Start nREPL
(def server (nrepl.server/start-server
:port 1234
:handler
(fn [_]
(nrepl.server/default-handler cider.nrepl/cider-middleware))))
;; Start Figwheel REPL
(fig/cljs-repl "dev"))
It seems that you're starting the nREPL on port 1234, but then Emacs is trying to connect to port 6776. Could that be the reason?
When you run cider-connect-clj you can select the port you want connect to. Try changing the default port to 1234 and see if that helps.
I've had pretty simple use of Cider REPL in the past; within a project's clj file I use cider-jack-in and I'm good to go (assuming the following in my .lein/profiles.clj
;; ~/.lein/profiles.clj
{:user {:plugins [[lein-localrepo "0.5.2"]
[cider/cider-nrepl "0.10.0-SNAPSHOT"]]
:dependencies [[org.clojure/tools.nrepl "0.2.7"]]
}}
Now out of the box I do:
<user> Clojure/ 17:44$ lein new luminus wants-cider +cljs
Generating a Luminus project.
<user> Clojure/ 17:45$ cd wants-cider/
<user> wants-cider/ 17:45$ lein run
15-Sep-17 17:45:59 user-linuxbox INFO [wants-cider.core] - nREPL server started on port 7000
15-Sep-17 17:45:59 user-linuxbox INFO [wants-cider.handler] -
-=[wants-cider started successfully using the development profile]=-
17:45:59.789 INFO [org.projectodd.wunderboss.web.Web] (main) Registered web context /
15-Sep-17 17:45:59 user-linuxbox INFO [wants-cider.core] - server started on port: 3000
# new shell
<user> wants-cider/ 17:50$ lein figwheel
Figwheel: Starting server at http://localhost:3449
Focusing on build ids: app
Compiling "resources/public/js/app.js" from ["src-cljs" "env/dev/cljs"]...
Successfully compiled "resources/public/js/app.js" in 5.757 seconds.
Started Figwheel autobuilder
WARNING: unable to load "cemerick.piggieback/wrap-cljs-repl" middleware
Launching ClojureScript REPL for build: app
# ... insructions ...
Prompt will show when figwheel connects to your application
To quit, type: :cljs/quit
cljs.user=>
From here, how can I connect my Cider REPL?
You must have figwheel nrepl options to be specified, in :profiles :dev section of project.clj:
:profiles {:dev {
;; ....
:figwheel {:nrepl-port 7888 }
Then connect to nrepl from CIDER:
C-c M-c <or> M-x cider-connect
;; enter figwheel host, port, in this case -- localhost, 7888
;; then, in appeared REPL buffer:
user> (use 'figwheel-sidecar.repl-api)
user> (cljs-repl)
cljs.user=>
info:
https://github.com/bhauman/lein-figwheel/wiki/Using-the-Figwheel-REPL-within-NRepl
Based on some apprentice-level googling I've found that in project.clj
:ring {:handler pach.handler/app
:port 4004}
Will work fine, but trying to set the default port to 80 does not seem to play nicely.
How would I set this value for a production server?
I am running a server with the luminus web framework which uses ring/compojure and I want to be able to connect to my code with a clojure repl. I understand that there is an nrepl server which stands for network repl and that you can connect to it via: `lein repl :connect user#host:port (or some various protocol, I've tried a few things).
The following code from repl.clj is created when you auto-generate a luminus project with `lein new luminus [project-name] (actually I added the browser-repl piece necessary to attach a clojurescript repl).
(ns sophia-site.repl
(:require [cemerick.piggieback :as pig]
[weasel.repl.websocket])
(:use sophia-site.handler
ring.server.standalone
[ring.middleware file-info file]))
(defn browser-repl []
(pig/cljs-repl :repl-env
(weasel.repl.websocket/repl-env :ip "localhost" :port 9001)))
(defonce server (atom nil))
(defn get-handler []
;; #'app expands to (var app) so that when we reload our code,
;; the server is forced to re-resolve the symbol in the var
;; rather than having its own copy. When the root binding
;; changes, the server picks it up without having to restart.
(-> #'app
; Makes static assets in $PROJECT_DIR/resources/public/ available.
(wrap-file "resources")
; Content-Type, Content-Length, and Last Modified headers for files in body
(wrap-file-info)))
(defn start-server
"used for starting the server in development mode from REPL"
[& [port]]
(let [port (if port (Integer/parseInt port) 8080)]
(reset! server
(serve (get-handler)
{:port port
:init init
:auto-reload? true
:destroy destroy
:join? false}))
(println (str "You can view the site at http://some-ip:" port))))
(defn stop-server []
(.stop #server)
(reset! server nil))
I have been unsuccessful with lein repl :connect ...
What can I do to attach a clojure repl to my code on a server?
Thanks for all the help
In your project root you should run lein repl, once connected you can enter (start-server) at the repl prompt.
Your server and a browser tab will be launched and the repl prompt that you invoked (start-server) from can be used to interact with the running application.
In order to prevent a browser tab from launching you would need to add :open-browser? false to the options map passed to serve in repl.clj:
(serve (get-handler)
{:port port
:init init
:auto-reload? true
:destroy destroy
:open-browser? false
:join? false})
Running Leiningen 2.3.4 on Java 1.7.0_21 Java HotSpot(TM) 64-Bit Server VM
I am having trouble connecting to an nREPL server.
I setup a new project using lein new luminus, and then added a dependency to drawbridge ([com.cemerick/drawbridge "0.0.6"]).
I added a handler route for the repl as follows (based on https://devcenter.heroku.com/articles/debugging-clojure):
(def drawbridge-handler
(-> (cemerick.drawbridge/ring-handler)
(wrap-keyword-params)
(wrap-nested-params)
(wrap-params)
(wrap-session)))
(defn wrap-drawbridge [handler]
(fn [req]
(if (= "/repl" (:uri req))
(drawbridge-handler req)
(handler req))))
And added wrap-drawbridge to my middlewares.
I then start the server using
lein ring server-headless
The connection seems to be working well because performing a GET request on http:localhost:3000/repl yields a response: ["[\n","\n]"]
But I can't connect to the REPL:
> lein repl :connect 0.0.0.0:3000/repl
Connecting to nREPL at 0.0.0.0:3000/repl
And, after some time:
SocketException The transport's socket appears to have lost its connection to the nREPL server
clojure.tools.nrepl.transport/bencode/fn--4287/fn--4288 (transport.clj:95)
clojure.tools.nrepl.transport/bencode/fn--4287 (transport.clj:95)
clojure.tools.nrepl.transport/fn-transport/fn--4261 (transport.clj:42)
clojure.core/binding-conveyor-fn/fn--4107 (core.clj:1836)
java.util.concurrent.FutureTask$Sync.innerRun (FutureTask.java:334)
java.util.concurrent.FutureTask.run (FutureTask.java:166)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:615)
java.lang.Thread.run (Thread.java:722)
Bye for now!
Am I missing something?
Edit:
Added the following logging code to my handler:
(defn wrap-drawbridge [handler]
(fn [req]
(if (= "/repl" (:uri req))
(do (println "IN REPL ")
(drawbridge-handler req))
(handler req))))
When connecting with lein repl :connect http://localhost:3000/repl as suggested, I see the line IN REPL being printed in an infinite loop, on the servers console.
Not really a direct answer, but I've found that I was not approaching this problem the right way.
Luminus creates a project-name.repl namespace that should be used for interactive development.
I found that I could do what I wanted by adding to my project.clj
:repl-options {
:init-ns project-name.repl
:init (start-server)}
And then simply starting the server with lein repl.
Much simpler than setting up an nREPL middleware!
Have you made sure that there actually is a request entering your server (e.g. by printlining)?
However, my first guess would be that lein repl :connect behaves differently depending on whether you pass it an IP/Port pair or a fully-qualified URL. Since drawbridge seems to make nREPL accessible via HTTP I'd suggest you try:
lein repl :connect http://localhost:3000/repl
You need to add wrap-drawbridge at the first place in you middleware list.
If you do something like that
(def app (-> #'all-routes
wrap-drawbridge
...
...
...
))
It works like a charm.