📦 No More Locked JARs: jarTempFileCaching — BoxLang 1.17.0

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. Now reloadOnChange actually 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.