BoxLang 1.17.0 fixes a long-standing Windows headache: JAR files loaded via this.javasettings.loadpaths or BoxLang modules (or any DynamicClassLoader for that matter) are now copied to a temp cache first, so the originals aren’t locked and can be updated at runtime.
The Problem
On Windows, when BoxLang loads a JAR into a DynamicClassLoader, the file gets locked by the JVM. That means reloadOnChange for javaSettings was essentially broken — the files literally couldn’t be replaced because they were held open. This also caused frequent issues when updating bx modules while the server was running, which would leave multiple jars on disk since the old ones couldn’t be removed.
If you’ve ever hit “Permission denied” trying to drop a new JAR while BoxLang was running, this was why.
The Solution: Temp File Caching
BoxLang 1.17.0 introduces jarTempFileCaching (enabled by default). When enabled, JARs are copied from their original paths to {java.io.tmpdir}/boxlang-jars/ before being loaded. The originals are never locked.
How It Works
Each temp copy is named:
{originalFilename}-{hashOfPath}-{lastModified}.jar
A sibling .origin sidecar file stores the original source path. This allows BoxLang to track where each temp copy came from and whether it’s still valid.
Naming breakdown
| Part | Meaning |
|---|---|
originalFilename |
The base name of the jar, e.g. mylib.jar |
hashOfPath |
A hash of the full original path so the same file name from different paths doesn’t collide |
lastModified |
The last-modified timestamp of the source, so a new copy is created when the file changes |
Lifecycle
Source JAR → temp copy → loaded by ClassLoader → ClassLoader closed → orphan cleanup
On class loader close: Orphaned temp copies are removed — those tracked by the class loader whose source path no longer exists or has a different last-modified timestamp. Valid files (source stil exists and unchanged) are kept so they can be reused by the next process.
On runtime startup: A one-pass cleanup checks every .jar in {java.io.tmpdir}/boxlang-jars/ against its .origin sidecar. Files whose source path is gone or has been updated are deleted. This catches stale versions left behind from jars that were renamed or deleted.
On GC: When a stale class loader is garbage-collected, such as when you swap out a jar on disk while using this.javaSettings.reloadOnChange, a Cleaner fires that deletes its orphaned temp copies via the same source-path check.
The Setting
In case this feature causes any headaches, or for a production deployment where you don’t want to waste disk space with copies, or you know for certain the jars will never change, we have an escape hatch to opt-out of this behavior. The new setting in boxlang.json:
{
// Copy JAR files to a writable temp cache before loading them. This prevents file locking
// on Windows and lets stale/duplicate versions be cleaned up safly.
// Set to false when the filesystem is read-only, or when JARs never change and locking is
// not a concern. When disabled, JARs are loaded directly from their original paths, no temp
// copies are made, no cleaner is registered, and no startup temp cleanup runs.
"jarTempFileCaching" : false
}
Or, use our env var override convention:
BOXLANG_JARTEMPFILECACHING=false
When disabled, everything works as before — JARs are loaded directly from their original paths, no temp copies are created, no sidecar files exist, no Cleaner is registered, and the startup cleanup is skipped.
Who This Affects
Every dynamic class loader in BoxLang:
- Application
javaSettings/this.javasettings.loadpaths— the most common case. NowreloadOnChangeactually works on Windows. - Runtime class loader — core runtime JARs
- Module class loaders — module JARs
Real-World Impact
| Before | After |
|---|---|
| JARs locked on Windows — can’t replace while running | JARs copied to temp — originals free to update |
reloadOnChange essentially broken on Windows |
reloadOnChange works correctly |
| Manual restart required to pick up new JAR versions | Class loader can be recreated and picks up changes |
| Orphaned temp files accumulate | Startup and GC cleanup remove stale copies |
Quick Start
Nothing to do — this is enabled by default. Drop a new version of a JAR into your javaSettings path and BoxLang picks it up without a restart. The original file is never locked.
Stay tuned for more 1.17.0 feature spotlights.