Why does trying to read a line from console with a BufferedReader freeze Leiningen REPL?
lein repl
nREPL server started on port 65142
REPL-y 0.2.0
Clojure 1.5.1
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
user=> (import [java.io BufferedReader BufferedWriter InputStreamReader])
java.io.InputStreamReader
user=> (.readLine (BufferedReader. (InputStreamReader. System/in)))
Calling lein repl actually launches a new background server process for evaluating code; what you see in the console is a client process that sends code to the server for evaluation and then displays the results that are returned. What I suspect is happening here is that, when the REPL server evaluates System/in, it's actually referring to stdin of the server process, which you can't write to from the client.
Replacing (InputStreamReader. System/in) with *in* should allow your code to work from the REPL as well as when launched directly with lein run or java.
Related
Cannot connect remote REPL properly
Here are the steps I do:
Start local repl instance:
lein repl
Connect to local by remote repl instance(I do it through Intellij IDEA)
After this, every output in code goes only in local repl, in remote one there are nothing
What I need: to see all outputs in both repl instances
I have found partial solution, this code rebinds output of one repl to another. Just run it in remote one, and all output will go to it
(defn rebind-output []
(prn "Rebinding output...")
(System/setOut (PrintStream. (WriterOutputStream. *out*) true))
(System/setErr (PrintStream. (WriterOutputStream. *err*) true))
(alter-var-root #'*out* (fn [_] *out*))
(alter-var-root #'*err* (fn [_] *err*)))
out - is intstance of PrintWriter
However what I need is: see BOTH repls outputing the same, how to do it?
I can't seem to think that you are confused about how to connect to an existing REPL (the one you launch from the command line with lein repl). Did you check the section Remote REPLs in the Cursive manual?
Generally, you want only one of these:
Launch the REPL from Intellij itself on a project that is already managed with Leiningen (eg. it already has a project.clj file), or
Connect to an already running REPL, one that is running on the same host, or in a different machine.
If you are starting lein repl yourself in a console, you'll see that it prints some messages on startup:
$ lein repl
nREPL server started on port 39919 on host 127.0.0.1 - nrepl://127.0.0.1:39919
In this example, the server started listening on my own host (127.0.0.1 or localhost) on port 39919 (this port will change each time you launch the REPL with lein repl). You'll need to enter these values in Intellij to be able to connect to this REPL.
I have a Clojure app that I can use both from the command-line, and as a Compojure app.
I did that by putting a ring handler and a main function (clojure.tools.cli) in my leiningen project.clj.
{...
:main my-app.core
:ring {:handler my-app.handler/handler }}
The handler is defined (defroutes handler ...).
Now if I want to run the CLI app, I can run lein uberjar and then java -jar arguments.
And I can also run the Compojure app via lein ring server PORT.
Now, how do I deploy the thing as a Compojure app (and not a CLI app) in a production server ? (Bonus points for explaining how lein ring server works.)
Note : I already use nginx if that can help, and I'm flexible on the container to be used.
Here's kind of the default template I use for new projects. It allows you to do dependency injection into ring apps and run the app from the command line as an uberjar. You can read more here: http://www.sparxeng.com/blog/software/improved-clojure-workflow
; handler.clj
(defn wrap-inject-deps
"Ring middleware that injects the dependencies into each ring request map"
[handler deps]
(fn [req]
(handler (assoc req :deps deps))))
(defn create-handler
"Similar to the default ring 'handler', but with a parameter to let you inject dependencies"
[deps]
(-> (routes
api-routes
web-routes
(route/resources "/"))
(kwp/wrap-keyword-params)
(params/wrap-params)
(json/wrap-json-params)
(json/wrap-json-response)
(wrap-inject-deps deps))) ; this injects dependencies
(defn start-jetty
"Launch the Jetty instance"
[deps]
(let [port (Integer. (or (-> system :env :port) 5000))
handler (create-handler deps)]
(jetty/run-jetty handler {:port port :join? false})))
; system.clj
(defn get-env
"Initialize configuration vars"
[]
{:aws-access-key-id (System/getenv "AWS_ACCESS_KEY_ID")
:aws-secret-access-key (System/getenv "AWS_SECRET_ACCESS_KEY")
:mongo-url (System/getenv "MONGO_URL"))
(defn start
"Launch dependencies such as DB connection and Jetty. Return these as a map, for use in REPL"
[& [env]]
(let [env (or env (get-env))
deps {:env env
:monger (db/init env)}
jetty (handler/start-jetty deps)]
(assoc deps :jetty jetty)))
; program.clj
(defn -main [& [port]]
"App entrypoint"
(let [env (system/get-env) ; "env" is just a map of config variables, that can be hard-coded, read from file, or whatever you want.
env (if port (assoc env :port port) env)]
(system/start env)))
You can then use leiningen profiles if you need to create multiple apps with different entrypoints from your codebase.
lein ring server fires up jetty to serve the web application you've built. It does this by starting the server with the options you specify in :ring. If you specify LEIN_NO_DEV in your environment, the server will run in production mode. Whether jetty is the right fit for your productions needs or not, is up to you to decide.
If you would like to run your web application on a different server, there are multiple options. One of them is running on Tomcat. Take another look at the documentation for lein ring, in particular the section on war files. Basically, you would call lein war or lein uberwar to generate a war file. You can then e.g. drop the war file into the webapps directory of Tomcat.
Another option would be using immutant, which has substantial documentation on installation and running applications.
I would like to understand why my Ring application is attempting to open the log file at compile time.
I have a webservice in Compojure and Ring. The application works without issue but whenever I compile the application to an uberwar or run lein ring server I get the below error from slf4j.
I don't understand what is causing the Clojure compiler to attempt to access this log file during compilation. The only clue which I have is that it only started happening when I included the Korma library.
If I create the directory I can get rid of the error, however, I want to understand why the application is attempting to open the log file at compile time.
log4j:ERROR setFile(null,true) call failed.
java.io.FileNotFoundException: /var/log/ege/myservice/myservice.log (No such file or directory)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:270)
at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
at java.io.FileOutputStream.<init>(FileOutputStream.java:133)
at org.apache.log4j.FileAppender.setFile(FileAppender.java:294)
at org.apache.log4j.RollingFileAppender.setFile(RollingFileAppender.java:207)
at org.apache.log4j.FileAppender.activateOptions(FileAppender.java:165)
at org.apache.log4j.config.PropertySetter.activate(PropertySetter.java:307)
at org.apache.log4j.config.PropertySetter.setProperties(PropertySetter.java:172)
at org.apache.log4j.config.PropertySetter.setProperties(PropertySetter.java:104)
at org.apache.log4j.PropertyConfigurator.parseAppender(PropertyConfigurator.java:809)
at org.apache.log4j.PropertyConfigurator.parseCategory(PropertyConfigurator.java:735)
at org.apache.log4j.PropertyConfigurator.configureRootCategory(PropertyConfigurator.java:615)
at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:502)
log4j configuration is a runtime configuration of your app. Korma uses log4j configuration for its usage in runtime. So, it's not surprising that when running lein ring server, korma uses log4j configuration for its logging and the log4j logging throws error cause it can not find the log directory.
As for the error thrown during lein check or lein ring war. Maybe you have some forms which attempts to connect to database or even do some query to your database in top-level of some of your namespaces.
maybe you have something like this:
(ns my-app.db
(:use korma.db)
(:use korma.core))
(def db (create-db (mysql {:db "my_db"
:user "root"
:password ""})))
;; this form below attempts to get a db connection
(def conn (get-connection db))
(defentity users)
;; this form below attempts to query the database
(select users)
when clojure compiles, it executes every form, thus, clojure connects to db or query the db. In other words, clojure runs a part of your app during compilation.
unrelated note
Everything above aside, you should separate the log4j configuration or any other runtime-configs for development environment and production environment.
I usually separate development environment and production environment configuration using :profiles in project.clj.
:profiles
{:production
{:ring
{:open-browser? false, :stacktraces? false, :auto-reload? false}
:source-paths ["prod-config"]}
:dev
{:dependencies [[ring-mock "0.1.5"] [ring/ring-devel "1.3.1"]]
:source-paths ["dev-config"]}}
I put my development's log4j.properties in dev-config dir and production's log4j.properties in prod-config dir.
Update: this question has changed since the original set of commenters left responses. Apologies for any confusion.
This is my code repository https://github.com/Integralist/spurious-clojure-example you can use it as an example for what I'm working with.
Note that the above repo relies on a library I've not yet published to Clojars (as I'm still testing it - hence this question opened). You can see the source code of the library here: https://github.com/Integralist/spurious-clojure-aws-sdk-helper
I have a "hello world" Clojure web app written with Compojure that I have working fine when run using lein ring server and lein run (as I have a -main function now created). It also runs to a certain extent when compiled down into a jar and I run java -jar app.jar.
My problem now is that if I try to run the default java -jar app.jar from within a Docker container I get the following error telling me...
spurious-clojure-example is starting
2015-02-14 00:58:03.812:INFO:oejs.Server:jetty-7.x.y-SNAPSHOT
2015-02-14 00:58:03.854:INFO:oejs.AbstractConnector:Started SelectChannelConnector#0.0.0.0:8080
Started server on port 8080
Exception in thread "main" java.awt.HeadlessException:
My code is currently using a -main function like so...
(ns spurious-clojure-example.repl
(:use spurious-clojure-example.handler
ring.server.standalone
[ring.middleware file-info file])
(:gen-class))
(defonce server (atom nil))
(defn get-handler []
(-> #'app
(wrap-file "resources")
(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 true}))
(println (str "You can view the site at http://localhost:" port))))
(defn stop-server []
(.stop #server)
(reset! server nil))
(defn -main []
(start-server))
...but how do I get the server to start headless? I can't quite follow the Compojure boilerplate code to decipher where or how it knows when to run headlessly or via browser?
I know that on the command line you can do lein ring server-headless so what's the programmatic equivalent of that?
Because ring-server is primarily meant for development, it tries to open a browser when the server starts. This fails with a java.awt.HeadlessException on platforms without a GUI. You'll want to set the :open-browser? option to false to prevent this.
From the offical Docker docs on EXPOSE
The EXPOSE instructions informs Docker that the container will listen
on the specified network ports at runtime. Docker uses this
information to interconnect containers using links (see the Docker
User Guide) and to determine which ports to expose to the host when
using the -P flag. Note: EXPOSE doesn't define which ports can be
exposed to the host or make ports accessible from the host by default.
To expose ports to the host, at runtime, use the -p flag or the -P flag.
So if you're setting the ring server port manually in your project.clj, ensure that you're using the same port also in your Dockerfile and then provide a mapping via -p or -P when starting docker.
I can't say for sure but I believe that your headless error message doesn't contribute to your problem.
My clojure noir app works 100% fine locally and connects to RedisToGo no problem.
The problem is when I deploy to Heroku (git push heroku master), I get a timeout error:
Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch
Full log is here: https://gist.github.com/1842439
When I remove this redis connection code, it deploys fine:
(:use [aleph.redis :only (redis-client)])
(def r (redis-client {:host redis-url :password redis-pass :port redis-port}))
The weird thing is that when I run "heroku run lein run repl" and paste in the aleph code above, it connects to redis fine and I can read/write data.
So it something about how heroku boots up the app thats breaking the connection to RedisToGo and timing it out.
Doing something side-effecty at the top level is very suspect - that code is executed when compiling as well as when executing, so probably the automatic uberjar Heroku does is failing because redis isn't available at compile time, or something like that.
Instead, initialize your redis client after -main has been called, which will ensure you are in a production environment. You can accomplish this in a number of ways, for example by initially defining it to nil and then performing an alter-var-root in -main. My preferred solution would probably be something like:
(def r (delay (redis-client ...)))
(defn get-stuff []
(let [client #r] ...))
(defn -main [& args]
(get-stuff)
...)
Now the code to connect isn't performed until someone derefs the client, which they should never do until the app is up and running.