Skip to content

Instantly share code, notes, and snippets.

@henrik42
Last active May 11, 2024 21:40
Show Gist options
  • Save henrik42/3583db3a49d29253d004bae1b5cfc0c5 to your computer and use it in GitHub Desktop.
Save henrik42/3583db3a49d29253d004bae1b5cfc0c5 to your computer and use it in GitHub Desktop.
Mutate Java Environment Variables
;; There are ways to change the environent variables of a running process
;; (https://unix.stackexchange.com/questions/38205/change-environment-of-a-running-process).
;; This is also true for a JVM process.
;;
;; Java has an API to the environment (java.lang.ProcessEnvironment). This class
;; reads the JVM process environment once and stores the key/values in a few
;; maps. From this point on, these maps are Java's view on its environment
;; variables. All access through this API (which your code may use) will read
;; values from these maps.
;;
;; The API gives you only read-access to these maps and will not change them
;; once they are created. So they are effectivly immutable and "fixed". Using
;; reflective access to the static fields holding references to these mutable
;; maps we can access and mutate the maps.
;;
;; In this case we are NOT changing the JVM process environment. We are only
;; changing the view of the API any Java class will be using.
;;
;; This code was inspired by
;; https://blog.sebastian-daschner.com/entries/changing_env_java and
;; https://stackoverflow.com/questions/318239/how-do-i-set-environment-variables-from-java.
;;
;; Notes:
;; * put! and remove! are not thread-safe -- i.e. there is a race between the
;; put/remove and the putAll where users of the API may see inconsistent
;; values.
;; * put! and remove! do not flush to main memory so users from other threads
;; may see inconsistent values.
;; * put! and remove! mutate the maps in-place to that iterators on those maps
;; may brake and throw ConcurrentModificationException.
(defn static-field-value [clazz field-name]
(-> clazz
(.getDeclaredField (name field-name))
(doto (.setAccessible true))
(.get nil)))
(def the-environment
(static-field-value java.lang.ProcessEnvironment :theEnvironment))
(def the-case-insensitive-environment
(static-field-value java.lang.ProcessEnvironment :theCaseInsensitiveEnvironment))
(defn put! [k v]
(.put ^java.util.HashMap the-environment k v)
(.clear the-case-insensitive-environment)
(.putAll the-case-insensitive-environment the-environment))
(defn remove! [k]
(.remove ^java.util.HashMap the-environment k)
(.clear the-case-insensitive-environment)
(.putAll the-case-insensitive-environment the-environment))
(comment
(def the-unmodifiable-environment (static-field-value java.lang.ProcessEnvironment :theUnmodifiableEnvironment))
(defn assert-all-equal [& optional]
(assert (apply =
the-unmodifiable-environment
the-environment
the-case-insensitive-environment
(System/getenv)
(.clone the-environment)
(.clone the-case-insensitive-environment)
optional)))
(put! "FOO" "BAR")
(assert-all-equal)
(assert (= "BAR" (System/getenv "FOO")))
(put! "FOO" "BAZ")
(assert-all-equal)
(assert (= "BAZ" (System/getenv "FOO")))
(put! "FOOBAR" "QUOX")
(assert-all-equal)
(assert (= "QUOX" (System/getenv "FOOBAR")))
(remove! "foobar")
(assert-all-equal)
(remove! "FOOBAR")
(assert-all-equal)
(assert (nil? (System/getenv "FOOBAR")))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment