Embed an nREPL server in a Java application to inspect live objects and evaluate Clojure expressions inside its JVM. Register objects directly or access beans from a Spring application context, and control the listener through JMX.
Version 0.1.0 is available on Clojars and requires Java 8 or later. See release notes for changes from 0.0.8, including the move to loopback-only listening.
The library still depends on Clojure 1.6.0 and tools.nrepl 0.2.6 by default. Spring integration is compiled against Spring 3; the host application supplies Spring. Broader dependency upgrades are tracked in the upgrade plan.
Add the library to your Maven dependencies:
<dependency>
<groupId>net.matlux</groupId>
<artifactId>jvm-breakglass</artifactId>
<version>0.1.0</version>
</dependency>
Add Clojars to your project's repositories:
<repositories>
<repository>
<id>clojars</id>
<url>https://repo.clojars.org</url>
</repository>
</repositories>
For Leiningen, add [net.matlux/jvm-breakglass "0.1.0"] to :dependencies.
For Clojure CLI, add net.matlux/jvm-breakglass {:mvn/version "0.1.0"} to :deps.
Add this bean to your Spring configuration:
<bean id="repl" class="net.matlux.NreplServerSpring">
<constructor-arg index="0" value="1112" />
</bean>
1112 is the port number. For lifecycle control and cleanup, see the JMX examples below.
Create a server and register the objects you want to inspect:
import net.matlux.NreplServer;
NreplServer repl = new NreplServer(1112);
repl.put("department", myObject);
// At application shutdown: repl.stop(); repl.unregisterMBean();
Call put for each object you want to expose. NreplServer implements
Map<String, Object>; registered objects can be retrieved from the REPL.
The server exposes a management bean named net.matlux:name=Nrepl, accessible
through a JMX console such as JConsole:
| Type | Name | Meaning |
|---|---|---|
| Attribute | Port | Indicates the port used for the nrepl server (read/write). |
| Attribute | Started | Indicates whether the NreplServer is started or not (read only). |
| Operation | start | Starts the NreplServer. |
| Operation | stop | Stops the NreplServer. |
The one-argument constructor starts the listener and registers this MBean automatically. For an on-demand JMX switch, register the MBean while leaving the listener stopped:
NreplServer repl = new NreplServer(1112, false, true, true, true);
repl.put("department", myDepartment);
// Keep repl for the lifetime of the application.
// During shutdown, even if application work fails:
try {
repl.stop();
} finally {
repl.unregisterMBean();
}
In JConsole, attach to the local application JVM, open MBeans → net.matlux → Nrepl,
then invoke the lowercase start operation. Started becomes true. Connect with
lein repl :connect 127.0.0.1:1112; invoke stop when finished. Change the Port
attribute before the next start to choose another port. Port 0 asks the OS to
choose one on each start; read Port after starting to see the result.
The equivalent Spring bean leaves the REPL stopped until JMX starts it:
<bean id="repl" class="net.matlux.NreplServerSpring" destroy-method="stop">
<constructor-arg index="0" value="1112" />
<constructor-arg index="1" value="false" /> <!-- startOnCreation -->
<constructor-arg index="2" value="true" /> <!-- registerMBeanOnCreation -->
<constructor-arg index="3" value="true" /> <!-- propagateException -->
<constructor-arg index="4" value="true" /> <!-- logExceptionStack -->
</bean>
Closing the Spring context invokes stop. The application must also unregister
its MBean during shutdown (keep a reference to the bean before closing):
NreplServerSpring repl = context.getBean("repl", NreplServerSpring.class);
try {
context.close();
} finally {
repl.unregisterMBean();
}
There is one shared listener and one MBean name per class loader/platform server
respectively. Starting another instance replaces the listener; stopping any
instance stops it. Lifecycle calls are serialized, but the object registry is a
HashMap: coordinate concurrent access in the hosting application.
In 0.1.0, the listener binds to 127.0.0.1. nREPL permits arbitrary code execution in
the host JVM and has no authentication here. For remote access, use an SSH tunnel:
ssh -L 1112:127.0.0.1:1112 user@host, then connect to local port 1112.
This is a deliberate change from the old all-interfaces binding.
With the application running and the listener started on port 1112:
lein repl :connect 127.0.0.1:1112
At the connected prompt, inspect objects registered with put:
(require '[cl-java-introspector.core :as inspect])
(inspect/get-objs)
(def department (inspect/get-obj "department"))
(inspect/methods-info department)
(inspect/fields-info department)
For a Spring application:
(require '[cl-java-introspector.spring :as spring])
(spring/get-beans)
(spring/get-bean "department")
inspect/to-tree recursively exposes an object's fields as Clojure data. It has
no cycle or depth limit, and Java module access rules can prevent access to
private fields. Use it on known, bounded object graphs.
The core module requires JDK 8+ and Maven 3.9+. From the repository root:
mvn -B -f bootloader/pom.xml clean verify
See the module README for test phases, compatibility checks and build output, and the upgrade plan for outstanding work. Maintainers can follow the release procedure.
Copyright (C) 2015–2026 Mathieu Gauthron
Distributed under the Eclipse Public License, the same as Clojure.
Can you improve this documentation? These fine people already did:
Mathieu Gauthron, sesm & Stanislas NanchenEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |