determining repl environment vs. uberjar environment?

120 views
Skip to first unread message

Dave Tenny

unread,
Apr 22, 2014, 12:59:52 PM4/22/14
to clo...@googlegroups.com
I have an app I'm building.  It calls System/exit.  That doesn't works so well if I'm debugging in the REPL however.

What's the preferred method of determining whether I'm in REPL mode interaction vs running as a standalone app?

Also, long as I'm asking and being lazy, does the -main function return value translate to a System/exit value if it's numeric?
Or is System/exit the way to go?

Dave Tenny

unread,
May 8, 2014, 8:35:20 AM5/8/14
to clo...@googlegroups.com
Here's a solution to the problem I was trying to solve.  I'm not happy with it (the definition of has-repl?), but it's working for now.
It's also Sun/Oracle JVM specific with the sun.java.command clause. 

Again, the problem is that I wanted an exit routine I could call that wouldn't terminate the jvm when I'm running -main from the REPL, but that otherwise calls System/exit when running as an uberjar.

Other suggestions welcome.

(defn has-repl?
  "Return true if we appear to be running with a REPL, false otherwise."
  []
  ;; *TBD*: Consider looking for some particular repl routine in jvm stack traces
  ;; Don't care about the clojure.* prop vals, just that the property exists.
  (if (or (System/getProperty "clojure.debug")
          (System/getProperty "clojure.compile.path")
          (if-let [command (System/getProperty "sun.java.command")]
            (.contains command "clojure.main")))
    true
    false))

(defn- exit [status & [msg]]
  (when msg
    (println msg))
  (flush)
  (if (has-repl?)
    (throw (ex-info msg {:repl-exit true :status status}))
    (System/exit status)))

;; And in (-main)
    ...
      (catch Exception e
        (if-let [m (ex-data e)]
          ;; exception is of type clojure.lang.ExceptionInfo
          (if (:repl-exit m)
            (:status m)
            (exit 2 (str "Unexpected ex-data:" m "on" e)))
          ;; Caught an exception that wasn't thrown via pseudo-exit
          (if (has-repl?)
            (println (str e))           ;print to REPL
            (exit 2 (str e))))))))      ;print and exit java



--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/KZJQsmOeiHc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

James Reeves

unread,
May 8, 2014, 9:20:47 AM5/8/14
to clo...@googlegroups.com
Having your application call System/exit directly is often indicative of some unnecessary coupling in your code.

Under what circumstances does your application need to exit?

- James


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Dave Tenny

unread,
May 8, 2014, 9:23:03 AM5/8/14
to clo...@googlegroups.com
When running as an uberjar, i.e. "java -jar myapp.jar [args]"
the return code of the process to the shell is very important.  System/exit is how that return code is sent.


You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/KZJQsmOeiHc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.

David Powell

unread,
May 8, 2014, 9:47:01 AM5/8/14
to clojure

There are some hacky possibilities - eg testing for *1, but I don't think there is a definitive way; there are many repls too (the clojure.main repl, lein repl, lighttable), and a technique might not work on all of them.

Probably best to have some helper function you call from the repl, and have that return the status code.  Then make your -main call that, followed by System/exit with the return value.


James Reeves

unread,
May 8, 2014, 10:28:45 AM5/8/14
to clo...@googlegroups.com
Sure, but those return codes correspond to some ending state of your application, which can occur via a normal function return, or through an exception. In both cases, your -main function can map state to exit code.

For instance, let's assume that your application will return or except. In example below I'm using Slingshot to handle the exceptions:

(defn -main [& args]
  (try+
    (apply app args) (System/exit 0)
    (catch [:exit :app/bad-data] _ (System/exit 1))
    (catch [:exit :app/connect-failed] _ (System/exit 2))))

Alternatively, if the app function returns the status rather than excepts:

(defn -main [& args]
  (System/exit
   (case (apply app args)
     :app/ok 0
     :app/bad-data 1
     :app/connect-failed 2)))

- James

Dave Tenny

unread,
May 8, 2014, 11:22:35 AM5/8/14
to clo...@googlegroups.com
James, All well and good, but you still need to know if you're running in a REPL environment, and make sure you do NOT call System/exit for any (typical) reason in that environment.  The idea is to test your (-main) function from the REPL, without having to restart the lisp every time.

Ray Miller

unread,
May 8, 2014, 12:14:12 PM5/8/14
to clo...@googlegroups.com
On 8 May 2014 16:22, Dave Tenny <dave....@gmail.com> wrote:
> James, All well and good, but you still need to know if you're running in a
> REPL environment, and make sure you do NOT call System/exit for any
> (typical) reason in that environment. The idea is to test your (-main)
> function from the REPL, without having to restart the lisp every time.

I think James's point was that, if your -main function is a thin
wrapper that calls (app ...), then it's (app ...) you'd test at the
REPL (apologies if I'm putting words into your mouth, James).

Ray.

Dave Tenny

unread,
May 8, 2014, 12:48:08 PM5/8/14
to clo...@googlegroups.com
Gotcha. I suppose that makes sense as long as I undo some poor design and make sure that the 'exit' wrapper I have never attempts to call System/exit directly and always throws some suitable exception for interpretation at the top level.  Right now my exit wrapper calls exit immediately if we're not in a REPL environment.  Very few applications would care about the distinction
(including mine) between unwinding a thread then calling exit, vs calling exit without unwinding.

Independent of my exit situation, I can still think of situations in which people may want to perform logic only when it was known to be a REPL environment. Of course in the worst case you can always add a -D flag to the JVM on startup indicating some 'interactive" mode desires.  I'm not sure how that goes with repls from cider in emacs and such.

Meanwhile I'm set with a workaround, and the points made on (app) vs. (-main (app)) are good if you always throw and never call exit down the stack.


Reply all
Reply to author
Forward
0 new messages