Est. 1999 Beginner

BeanShell

A small, embeddable Java source interpreter that runs ordinary Java syntax dynamically and relaxes it with loose types, method closures, and shell-like commands — the scripting language embedded in Ant, JMeter, OpenOffice, and jEdit long before Java had a REPL of its own.

Created by Patrick Niemeyer

Paradigm Multi-paradigm: Object-Oriented, Imperative, Scripting
Typing Optionally typed: loosely typed by default, with full static Java declarations supported and an opt-in strict Java mode
First Appeared 1999
Latest Version 2.1.1, released 2 December 2022; the project describes 3.0 as the next release, and as of mid-2026 it exists only as a snapshot

BeanShell answers a question that sounds trivial and turned out not to be: what if you could just run Java, one line at a time, without a compiler? It is a small interpreter — the JAR is roughly 400 KB — written in Java, that executes standard Java syntax dynamically and then relaxes it. Types become optional. Methods can be declared at the top level. A method can return this and become an object. Shell-like commands (print(), cd(), dir(), source()) sit alongside the Java language, and because the interpreter runs inside your JVM, you can hand it live object references and get live objects back.

For most of the 2000s and 2010s this made BeanShell the default way to make a Java application scriptable. It is embedded in Apache Ant, JMeter, Maven’s plugin tooling, OpenOffice, LibreOffice, jEdit, ImageJ, and a long tail of enterprise products. A great many people have written BeanShell without knowing it — they thought they were writing a JMeter script, or a jEdit macro. Java 9 eventually shipped JShell and made the headline use case redundant, but by then BeanShell had spent eighteen years being the answer.

History and Origins

Patrick Niemeyer came to Java from Tcl/Tk. By his own account he was working at Southwestern Bell Technology Resources in the early 1990s, interested in scripting languages, when a colleague pointed him at a new language called Oak — which became Java. He went on to co-author O’Reilly’s Learning Java, one of the definitive books on the platform — early editions of which carry an appendix on BeanShell and shipped it on the accompanying CD-ROM — and along the way built the thing the platform conspicuously lacked.

The first BeanShell releases, reportedly 0.96 and 1.0, appeared in 1999, distributed from beanshell.org under a dual GNU LGPL and Sun Public License arrangement. The project moved to SourceForge in 2000 and, by the project’s own account, interest grew quickly. The reason is not hard to reconstruct. Java in 1999 had no REPL, no interactive mode, and no standard scripting story; trying an API meant writing a class with a main method, compiling it, and running it. Every other language ecosystem of the era had an interactive prompt. Java’s answer, for a long time, was a third-party JAR maintained by one developer.

The name is a Java pun with a serious point behind it. JavaBeans were the component model of the era, and BeanShell was a shell for beans — you could instantiate a bean, poke at its properties interactively, wire up an event handler, and watch it work. There is no official Docker image for BeanShell, because it was never a platform of its own; it was always something you put inside a Java application.

Adoption arrived through embedding rather than through people choosing BeanShell as their language. Apache Ant 1.5, released on 10 July 2002, supported BeanShell as a Bean Scripting Framework language for its script task. Reachability through IBM’s Bean Scripting Framework, and an embedded copy inside Oracle’s WebLogic Server, followed. Once a build tool that every Java shop used could run BeanShell, the language was effectively everywhere, whether or not anyone put it on a résumé.

Design Philosophy

BeanShell’s central design commitment is that there is no new language to learn. Valid Java is valid BeanShell. From version 2.0 the interpreter handles the entire Java syntax, including class and interface declarations, and scripted classes present themselves to outside Java code as ordinary classes. Everything BeanShell adds is a relaxation of Java rather than a departure from it.

That is a genuinely different bet from the one Groovy, Jython, or JRuby made. Those languages give you a better language on the JVM and ask you to learn it. BeanShell gives you the language you already know and removes the friction: the compile step, the mandatory class wrapper, the type declarations you did not want to write. The trade is deliberate. You get none of Groovy’s expressive machinery — no builders, no meta-object protocol, no elegant collection literals — and in return you get zero learning curve for a Java programmer, and the ability to paste production code straight into a prompt.

The second commitment is embeddability. The interpreter is small, pure Java, and needs no code generation or custom classloader for most of its features, which means it works in security-constrained environments where bytecode-generating engines do not. Wiring it in takes a handful of lines:

1
2
3
4
Interpreter i = new Interpreter();
i.set("server", myServer);            // pass a live object in
i.eval("server.setPort(8080)");        // script it
Object result = i.get("result");       // get a live object back

No serialisation boundary, no IPC, no marshalling — the script and the application share a heap. That is the property that made BeanShell the natural choice for macro languages and extension points, and it is also, as the 2016 CVE showed, the property that makes an embedded interpreter a security-relevant dependency.

The third commitment is a sliding scale of formality. The same file can hold an unstructured script, a loosely typed method, a method closure used as an object, and a fully declared class. You are meant to start sloppy and tighten up as the code earns it, rather than choosing a structure before you know what you are writing.

Key Features

Optionally typed variables. Declare types or don’t:

1
2
3
foo = "Hello";        // untyped — takes whatever you assign
int count = 5;        // typed — enforced like Java
count = "oops";       // error: incompatible types

Untyped variables are dynamically typed in the usual scripting sense; typed ones behave like Java declarations. Setting the interpreter’s strict Java mode turns the loose forms off entirely, which is how you migrate a script toward compilable code.

Top-level scripted methods with optional types.

1
2
3
4
5
add(a, b) {            // no return type, no argument types, no class
    return a + b;
}
print(add(2, 3));      // 5
print(add("a", "b"));  // ab

One declaration, two behaviours, because the operator resolves at run time.

Method closures — scripted objects without classes. This is BeanShell’s most distinctive feature and the one it credits to Perl and JavaScript. A method that returns this hands back its own namespace, methods and fields included:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
counter() {
    count = 0;
    inc() { return ++count; }
    get()  { return count; }
    return this;
}

c = counter();
c.inc(); c.inc();
print(c.get());        // 2

You have an object with encapsulated state and behaviour, and you never wrote a class. Java would not get lambda expressions until version 8, in March 2014.

Scripted interfaces and event handlers. Scripts can implement Java interfaces directly, which is what makes BeanShell usable as a callback and plug-in language:

1
2
3
button.addActionListener(new ActionListener() {
    actionPerformed(e) { print("clicked: " + e); }
});

BeanShell also supports a catch-all invoke(name, args) method, so one script can stand in for an interface whose methods it does not enumerate — a dynamic proxy without the proxy boilerplate.

Scripted classes. From 2.0, class Foo { ... } in a script produces something external Java code treats as a real class while the implementation stays dynamic and reloadable.

Convenience syntax. BeanShell adds shorthands for the tedious corners of the JavaBeans era: JavaBean property access (button.text = "OK" calling setText), map and hashtable indexing, and automatic handling of primitive wrapper types. It also supports auto-allocation of variables so a script can serve as a structured replacement for a properties file.

Commands and a live classpath. An extensible command set covers the shell metaphor (print, source, run, cd, dir, exec, javap, which) and interpreter control (addClassPath, reloadClasses, setAccessibility, frame, desktop, server). Fine-grained class reloading means you can change a class and pick up the new definition without restarting the JVM.

Four ways to run it. The project documents command-line, console (a Swing/AWT desktop with a class browser), applet, and remote session server modes. The remote server — a telnet-style listener and a servlet, the latter shipped as a WAR in the releases — embeds a live shell inside a running application, which was a startlingly effective and startlingly dangerous debugging tool. Do not expose it.

Bean Scripting Framework and JSR-223 integration. Separate bsh-bsf and bsh-engine artifacts let host applications reach BeanShell through IBM’s BSF or the standard javax.script API, which is why so many products list it as one scripting option among several.

Evolution

The 1.x line, through 1.3.0 in August 2003, was a scripting language in the neighbourhood of Java. Version 2.0, whose betas began later in 2003 and reached 2.0b4 in May 2005, was the ambitious rewrite: full Java syntax, scripted classes, and the claim that BeanShell was now a fully Java-compatible scripting language. That claim is the reason JSR 274 looked plausible.

Then the story becomes a case study in what happens when a widely embedded library outlives its maintainer’s attention.

JSR 274 — The BeanShell Scripting Language — cleared its JCP review ballot on 6 June 2005, with Niemeyer as specification lead. Standardisation would have made BeanShell a specified part of the Java ecosystem with a reference implementation. It never happened. The JSR never progressed past expert group formation, the specification was never completed, and in June 2011 the Executive Committee voted to list it as dormant. BeanShell had gotten official blessing and then run out of the one thing it needed, which was someone’s time.

Meanwhile the release train stopped. Version 2.0 stayed in beta permanently — the artifacts on Maven Central are still labelled 2.0b5 and 2.0b6, a beta designation on the code that thousands of production systems depended on. In 2007 the community did what communities do and forked: BeanShell2 on Google Code, created because the original project was no longer being actively maintained. Per the upstream project’s own history, the fork shipped real fixes and updates through 2014.

The reunification took a decade and passed through two hosting collapses. In 2012, Niemeyer granted the 2.0b4 codebase to the Apache Software Foundation and the license changed to Apache 2.0 from 2.0b5 — a significant improvement over LGPL/SPL for the corporate users who had quietly built on it. The code moved to apache-extras.org, a Google Code-hosted home only informally associated with Apache; the project reportedly considered entering the Apache Incubator but never did, and BeanShell is not an Apache project. When Google Code shut down, the repository moved on 23 September 2015 to GitHub, which remains the official home. In August 2017 the maintainers decided to merge the BeanShell2 changes back upstream, and the two lineages became one again.

What followed was a version-numbering mess with real consequences. Fork releases numbered in the 2.1.x range looked newer than the upstream 2.0b6 while carrying a different set of fixes, and a user picking by version number could easily have picked the one missing the security patch. Version 2.1.1, released on 2 December 2022, exists to settle that: per its release notes it merges 2.0b6 with backports from the development version plus contributions from the BeanShell2 fork, and no further work is planned on the 2.x branch. Version 3.0 is the announced next release — the project’s README lists roadmap items such as varargs, try-with-resources, multi-catch, generics parsing, interface default and static methods, and BigInteger/BigDecimal coercion as already done — but as of mid-2026 it is available only as a snapshot, and the README states that master is the only recommended version.

CVE-2016-2510 deserves its own paragraph, because it is the most consequential thing that ever happened to BeanShell. Merely having bsh.jar on the classpath made an application exploitable if some other part of it deserialised untrusted data through Java serialisation or XStream: XThis$Handler was serializable and could be induced to execute arbitrary code. NVD scores it 8.1 (High) on CVSS v3 and lists every version up to and including 2.0b5 as affected. Version 2.0b6 fixed it on 5 February 2016 by removing Serializable from that class. The fallout was disproportionate to the fix. BeanShell was a transitive dependency in a great many Java applications whose developers had never heard of it, and it became a standard gadget in Java deserialisation exploit research. For a good number of teams, the CVE was simultaneously the first and last time they thought about BeanShell — the remediation was often to remove it.

Current Relevance

BeanShell is dormant, and honest about it. The last release was December 2022 and the most recent commit to master, as of mid-2026, was in August 2024. The GitHub project has roughly 930 stars and fewer than a hundred open issues, many of them auto-migrated from SourceForge and, as the README puts it, orphaned without owners. Nothing is obviously broken — the README states that BeanShell requires at least JDK 8 and also works with Java 11, 17, and 21 — but nobody should mistake this for active development.

The reason is straightforward: JShell. Java 9, released in September 2017, gave the platform an official REPL with real language support, and it does the interactive-experimentation job better than an interpreter that has to approximate the language from outside. If you want to try a Java API today, you use jshell. For embedded scripting, javax.script with Groovy or GraalVM’s JavaScript engines is the mainstream choice, and where BeanShell survives as an option — JMeter is the clearest case — the host project’s documentation typically recommends the alternative for performance. It is worth being precise about that recommendation: JMeter’s guidance is that the JSR-223 elements with a compiled Groovy script scale better under load than the interpreted BeanShell components, because BeanShell reinterprets the script per invocation while Groovy can be compiled and cached. That is an architectural difference rather than a published benchmark figure, and anyone who cares about the magnitude should measure their own test plan rather than trust a number from a blog post.

Where BeanShell is still genuinely encountered:

ContextWhy it persists
Legacy JMeter test plansEnormous installed base of BeanShell samplers and assertions; migrating them is work with no visible payoff
jEdit macrosThe macro language is BeanShell; the library is decades deep
Enterprise product extension pointsProducts such as WebLogic, Liferay, OpenKM and DevTest reportedly embedded it or documented it as a scripting option, and extension code written years ago still runs
ImageJ / Micro-Manager scriptsScientific automation scripts have long lifespans and low incentive to rewrite
Transitive dependency auditsSecurity scanners still surface bsh on classpaths, usually pulled in by something older

If you are starting new work, use JShell for exploration and a maintained JSR-223 engine for embedding. If you have inherited BeanShell, ensure you are on 2.1.1 or later and check whether anything in the application deserialises untrusted input.

Why It Matters

BeanShell’s historical significance is that it demonstrated, in 1999, that Java did not have to be a compile-only language — and it demonstrated it convincingly enough that the platform eventually agreed. JShell in Java 9 is not a descendant of BeanShell in any code-lineage sense, and there is no documented design influence to claim. But BeanShell spent eighteen years proving there was demand for exactly the thing JShell delivers, and it is fair to say the platform came around to a position BeanShell had staked out first.

It also made an unfashionable design choice that has aged well as an idea. Every other JVM scripting language of that era offered a better language than Java. BeanShell offered Java, with the ceremony removed. That is why it ended up embedded in dozens of products: a host application adding a scripting engine wants its users to be productive immediately, and for a Java product, the language its users already know beats the language that is objectively nicer. The same reasoning shows up today in TypeScript’s relationship to JavaScript and in Kotlin’s insistence on Java interop — meet people where they are.

And it is a clear-eyed lesson in the economics of infrastructure. BeanShell was in Ant, in JMeter, in OpenOffice, in WebLogic, in Maven’s plugin tooling — critical infrastructure by any reasonable measure — while being maintained by roughly one person, then by a community fork that had to work around him, then by volunteers reconciling two divergent trees across two dead hosting platforms. Version 2.0 never left beta. Its JSR went dormant not because the idea failed but because nobody had time. A security flaw in it forced patching across a swathe of the Java ecosystem by developers who did not know they were using it. Every part of that story has happened again since, to other libraries, for the same reasons.

The technical achievement — an interpreter for a statically compiled language, small enough to embed anywhere, faithful enough that valid Java is valid script, with closures a decade before the host language had them — is one that few people appreciated at the time and fewer remember now. It deserves better than being a line item in a dependency scanner’s output.

Timeline

1999
Patrick Niemeyer releases the first versions of BeanShell — reportedly 0.96 and 1.0 — from beanshell.org, dual-licensed under the GNU LGPL and the Sun Public License
2000
The project moves to SourceForge, where — by the project's own account — interest in the new Java scripting language grows quickly
2002
Apache Ant 1.5 ships on 10 July with support for BeanShell as a Bean Scripting Framework language for its script task — the integration that put BeanShell into mainstream Java build tooling
2003
Version 1.3.0 is released in August, the last widely deployed release of the 1.x line
2005
Version 2.0b4 is released in May, reportedly the last beta posted to the original project site: full Java syntax support, including scripted classes that appear to outside code as ordinary Java classes
2005
JSR 274, The BeanShell Scripting Language, completes its JCP review ballot on 6 June and moves to expert group formation, putting BeanShell on a path to become a standardised part of the Java platform
2007
With upstream development stalled, the community forks the project as BeanShell2 on Google Code; per the project's own history the fork ships fixes and updates through 2014
2011
The JCP Executive Committee votes in June to mark JSR 274 dormant; the specification was never completed
2012
The BeanShell 2.0b4 codebase is donated to the Apache Software Foundation by code grant and relicensed under Apache License 2.0 from 2.0b5 onward; the source moves to apache-extras.org, but the project never enters the Apache Incubator
2015
Google Code shuts down; on 23 September the repository moves to github.com/beanshell/beanshell, which becomes the official project home
2016
Version 2.0b6 is released on 5 February, fixing CVE-2016-2510 — a remote code execution flaw in which having bsh.jar on the classpath made an application deserialising untrusted Java or XStream data exploitable
2017
In August the project decides to merge the BeanShell2 fork back upstream, reuniting the two lineages; the same year, Java 9 ships JShell, giving the platform an official REPL
2022
Version 2.1.1 is released on 2 December, reconciling 2.0b6 with backports from the development branch; the release notes state that no further work will be done on the 2.x branch

Notable Uses & Legacy

Apache JMeter

JMeter has long shipped BeanShell samplers, pre-processors, post-processors, assertions, timers, and listeners, letting load-test authors drop arbitrary Java-flavoured logic into a test plan. It is probably where more people have written BeanShell than anywhere else, usually without realising the language had a name. JMeter's own documentation now steers new work toward the JSR-223 elements with Groovy for performance reasons, but the BeanShell components remain in the product and in a very large amount of existing test plans.

Apache Ant and Apache Maven

Ant's script and scriptdef tasks accept BeanShell as a Bean Scripting Framework language, so a build could carry real logic without writing a custom task in Java. Maven's plugin tooling documents BeanShell mojos, allowing a plugin goal to be written as a script rather than a compiled class. Both are the same idea: a build file that needs to make a decision, without a compile-deploy cycle to get there.

Apache OpenOffice and LibreOffice

Both office suites include a BeanShell scripting provider alongside Basic, JavaScript, and Python, exposing the UNO API to scripts through a bundled editor. It is one of the few places a general-purpose desktop application shipped a Java scripting language to end users rather than only to developers.

jEdit

jEdit's macro system is BeanShell. Macros are recorded and hand-written as BeanShell scripts with the full editor API in scope, and the plugin ecosystem grew on top of that. For a programmer's text editor in the 2000s, this was the Java world's answer to Emacs Lisp — and it is a fair argument that BeanShell's ergonomics are why jEdit's macro library got as large as it did.

ImageJ and Micro-Manager

The scientific imaging community adopted BeanShell as one of the scripting languages for ImageJ and for Micro-Manager's script panel, where researchers automate image analysis and microscope acquisition. Scripting a Java image-processing library from a live interpreter, with real objects passed in and out, is close to the original use case Niemeyer described: interactive experimentation with Java APIs.

Enterprise middleware and platforms

BeanShell was embedded widely as an extension point in commercial and open-source Java platforms. The best-documented cases are Spring's dynamic language support (documented through Spring Framework 3.2 and dropped in 4.0), TestNG's method selectors and group expressions, and Apache Camel's script support, which historically listed BeanShell among its expression languages. Oracle WebLogic Server shipped an embedded copy of BeanShell, according to the BeanShell project's own WebLogic notes from the 6.1 era. Several other products — reportedly including Liferay's script engine, OpenKM, Joget, CA DevTest, and Cisco Prime Network's command builder — also documented BeanShell as a scripting option, though the details vary by version. The pattern repeats — a Java product needing user-supplied logic, unwilling to invent its own language, and reaching for the interpreter that already spoke Java.

Language Influence

Influenced By

Running Today

Run examples using the official Docker image:

docker pull
Last updated: