From 3edb255100a1128973c709c42ba022ec655aa448 Mon Sep 17 00:00:00 2001 From: "Flavio S. Glock" Date: Sat, 28 Mar 2026 11:03:58 +0100 Subject: [PATCH] docs: add jcpan blog post for blogs.perl.org Blog post announcing jcpan CPAN client addition to PerlOnJava. Includes long and short versions, example scripts, and benchmark data. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../blogs_perl_org_jcpan_2026/README.md | 38 +++ .../benchmark_results.md | 36 +++ .../blog-post-long.md | 235 ++++++++++++++++++ .../blogs_perl_org_jcpan_2026/blog-post.md | 130 ++++++++++ .../example_datetime.pl | 13 + .../blogs_perl_org_jcpan_2026/example_moo.pl | 18 ++ .../jruby_jython_reference.md | 140 +++++++++++ 7 files changed, 610 insertions(+) create mode 100644 dev/presentations/blogs_perl_org_jcpan_2026/README.md create mode 100644 dev/presentations/blogs_perl_org_jcpan_2026/benchmark_results.md create mode 100644 dev/presentations/blogs_perl_org_jcpan_2026/blog-post-long.md create mode 100644 dev/presentations/blogs_perl_org_jcpan_2026/blog-post.md create mode 100644 dev/presentations/blogs_perl_org_jcpan_2026/example_datetime.pl create mode 100644 dev/presentations/blogs_perl_org_jcpan_2026/example_moo.pl create mode 100644 dev/presentations/blogs_perl_org_jcpan_2026/jruby_jython_reference.md diff --git a/dev/presentations/blogs_perl_org_jcpan_2026/README.md b/dev/presentations/blogs_perl_org_jcpan_2026/README.md new file mode 100644 index 000000000..a9d123272 --- /dev/null +++ b/dev/presentations/blogs_perl_org_jcpan_2026/README.md @@ -0,0 +1,38 @@ +# jcpan Blog Post + +Blog post about the `jcpan` CPAN client for PerlOnJava. + +## Status + +Draft - ready for review + +## Style Notes + +Based on previous posts at https://blogs.perl.org/users/flavio_s_glock/: +- Short, focused (~300-500 words) +- Working code examples with actual output +- Technical but accessible +- Tags: Java, PerlOnJava (previously Perlito) + +Previous posts (2015-2017) covered Perlito5. This is the first post about PerlOnJava with jcpan. + +## Test Results + +- **Moo**: 809/840 subtests passed (96.3%) - failures are due to unimplemented `weaken`/`DESTROY` +- **DateTime**: 3589/3589 subtests passed (100%) + +## Files + +- `blog-post-long.md` - **Main blog post** (~1500 words, polished, shareable) +- `blog-post.md` - Short version (~130 lines, blogs.perl.org style) +- `example_datetime.pl` - Verified DateTime example script +- `example_moo.pl` - Verified Moo example script +- `benchmark_results.md` - Performance benchmark results (PerlOnJava 2.1x faster) +- `jruby_jython_reference.md` - Research on how JRuby/Jython present themselves in blogs +- `README.md` - This file + +## Related Resources + +- [User Guide: Using CPAN Modules](../../../docs/guides/using-cpan-modules.md) +- [Dev: CPAN Client Support](../../modules/cpan_client.md) +- [Dev: DateTime Fixes](../../modules/JCPAN_DATETIME_FIXES.md) diff --git a/dev/presentations/blogs_perl_org_jcpan_2026/benchmark_results.md b/dev/presentations/blogs_perl_org_jcpan_2026/benchmark_results.md new file mode 100644 index 000000000..6a7cbc702 --- /dev/null +++ b/dev/presentations/blogs_perl_org_jcpan_2026/benchmark_results.md @@ -0,0 +1,36 @@ +# Benchmark Results + +## Closure Benchmark (dev/bench/benchmark_closure.pl) + +This benchmark tests closure calls in a loop (20,000 iterations x 5,000 times). + +### System Perl +``` +$ time perl dev/bench/benchmark_closure.pl +timethis 5000: 7 wallclock secs ( 7.49 usr + 0.00 sys = 7.49 CPU) @ 667.56/s (n=5000) +done 1440000 + +real 0m7.532s +user 0m7.520s +sys 0m0.006s +``` + +### PerlOnJava +``` +$ time ./jperl dev/bench/benchmark_closure.pl +timethis 5000: 4 wallclock secs ( 3.48 usr + 0.07 sys = 3.54 CPU) @ 1411.12/s (n=5000) +done 1440000 + +real 0m3.990s +user 0m4.770s +sys 0m0.243s +``` + +### Summary + +| Implementation | CPU Time | Iterations/sec | Speedup | +|----------------|----------|----------------|---------| +| Perl 5 | 7.49s | 667/s | baseline | +| PerlOnJava | 3.54s | 1411/s | **2.1x faster** | + +The JVM's JIT compiler optimizes hot loops after warmup (~10K iterations). diff --git a/dev/presentations/blogs_perl_org_jcpan_2026/blog-post-long.md b/dev/presentations/blogs_perl_org_jcpan_2026/blog-post-long.md new file mode 100644 index 000000000..2b36d3045 --- /dev/null +++ b/dev/presentations/blogs_perl_org_jcpan_2026/blog-post-long.md @@ -0,0 +1,235 @@ +# PerlOnJava Gets a CPAN Client + +If you've ever tried to run Perl code in a Java environment, you know the drill. Rewrite everything in Java (expensive, risky), or maintain two separate runtimes with all the deployment headaches. + +PerlOnJava offers a third path: compile your Perl to JVM bytecode and run it anywhere Java runs. I've been working on Perl-to-JVM compilation, on and off, for longer than I'd like to admit. The latest push has been getting the ecosystem tooling right — and `jcpan` is the result. + +## Why This Matters + +Some scenarios where this pays off: + +**Legacy integration.** You have 50,000 lines of battle-tested Perl that process reports, transform data, or implement business logic. Rewriting it is a multi-year project with uncertain ROI. With PerlOnJava, you can deploy it as a JAR alongside your Java services. + +**JDBC database access.** Perl's DBI works with PerlOnJava's JDBC backend. Connect to PostgreSQL, MySQL, Oracle, or any database with a JDBC driver — no DBD compilation required, no driver version mismatches. + +```perl +use DBI; +my $dbh = DBI->connect("jdbc:postgresql://localhost/mydb", $user, $pass); +my $sth = $dbh->prepare("SELECT * FROM users WHERE active = ?"); +$sth->execute(1); +``` + +**Container deployments.** One Docker image with OpenJDK and your Perl code. You don't need a Perl installation, cpanm in your Dockerfile, or XS modules that compiled fine on your laptop. + +**Embedding in Java applications.** PerlOnJava implements JSR-223, the standard Java scripting API. Your Java application can `eval` Perl code, pass data back and forth, and let users write Perl plugins. + +## The 30-Second Version + +```bash +git clone https://github.com/fglock/PerlOnJava.git +cd PerlOnJava && make + +./jcpan Moo +./jperl -MMoo -e 'print "Moo version: $Moo::VERSION\n"' +``` + +That's it. Moo is installed. No cpanm, no local::lib dance. + +## What Actually Ships in the JAR + +PerlOnJava distributes as a single 23MB JAR file. Inside, you get: + +- **568 Perl modules** — DBI, JSON, YAML, HTTP::Tiny, Test::More, and the rest of the usual suspects +- **Java implementations** of key XS modules — DateTime, Digest::MD5, Compress::Zlib +- **The compiler and runtime** — parse Perl, emit JVM bytecode, execute + +When you run `./jperl script.pl`, there's no second download, no dependency resolution. The standard library is there. + +```perl +# These all work out of the box +use JSON; +use HTTP::Tiny; +use Digest::SHA qw(sha256_hex); +use Archive::Tar; +use DBI; + +my $response = HTTP::Tiny->new->get('https://api.example.com/data'); +my $data = decode_json($response->{content}); +print sha256_hex($data->{token}), "\n"; +``` + +## Installing Additional Modules + +The bundled modules cover common use cases, but CPAN has over 200,000 distributions. For everything else, there's `jcpan`: + +```bash +./jcpan Moo # Install a module +./jcpan -f Some::Module # Force install (skip failing tests) +./jcpan -t DateTime # Run a module's test suite +./jcpan # Interactive CPAN shell +``` + +Modules install to `~/.perlonjava/lib/`, which is automatically in `@INC`. + +### How Installation Works Without Make + +Traditional CPAN installation runs `perl Makefile.PL`, then `make`, then `make install`. This requires a C compiler and the Perl development headers — things that don't exist on the JVM. + +PerlOnJava ships a custom `ExtUtils::MakeMaker` that skips the `make` step entirely. When you run `jperl Makefile.PL`, it: + +1. Parses the distribution metadata +2. Copies `.pm` files directly to the install location +3. Reports any XS files it can't compile (more on that below) + +For pure-Perl modules — which is most of CPAN — this just works. + +### What About XS Modules? + +XS modules contain C code that gets compiled to native machine code. Since PerlOnJava compiles to JVM bytecode, not native code, these need special handling. + +For popular XS modules, PerlOnJava includes **Java implementations** of the XS functions: + +| Module | Implementation | +|--------|----------------| +| DateTime | java.time APIs | +| JSON | fastjson2 library | +| Digest::MD5/SHA | Java MessageDigest | +| DBI | JDBC backend | +| Compress::Zlib | java.util.zip | + +When you `use DateTime`, PerlOnJava's XSLoader detects the Java implementation and loads it automatically. You get the module's full API, backed by Java libraries. + +For XS modules without Java implementations, many have pure-Perl fallbacks that activate automatically. For the rest, installation succeeds (the `.pm` files install), but runtime fails with a clear error message. + +## A Real Example: DateTime + +DateTime is a good stress test. It has a deep dependency tree — Specio, Params::ValidationCompiler, namespace::autoclean, and so on. It also has XS code for performance-critical date math and a comprehensive test suite. + +Here's what happens when you install and test it: + +```bash +$ ./jcpan -t DateTime +... +t/00-report-prereqs.t .... ok +t/00load.t ............... ok +t/01sanity.t ............. ok +... +t/19leap-second.t ........ ok +t/20infinite.t ........... ok +... +All tests successful. +Files=51, Tests=3589, 78 wallclock secs +Result: PASS +``` + +3,589 tests, all passing. The Java XS implementation handles Rata Die conversions (the internal date representation), leap years, leap seconds, and timezone arithmetic. Under the hood, it's using `java.time.JulianFields` — the same code that powers Java's date/time library. + +```perl +use DateTime; + +my $dt = DateTime->new( + year => 2026, + month => 3, + day => 28, + hour => 14, + minute => 30, + time_zone => 'America/New_York' +); + +print $dt->strftime('%Y-%m-%d %H:%M %Z'), "\n"; +# Output: 2026-03-28 14:30 EDT + +$dt->add(months => 1); +print $dt->ymd, "\n"; +# Output: 2026-04-28 +``` + +## The Other Tools + +`jcpan` isn't the only addition. PerlOnJava now includes: + +**jperldoc** — Read module documentation: +```bash +./jperldoc DateTime +./jperldoc Moo::Role +``` + +**jprove** — Run test suites: +```bash +./jprove t/*.t +./jprove -v t/specific_test.t +``` + +These are the standard Perl tools, running on the JVM. + +## Performance + +**Startup is slow.** The JVM needs to load classes and initialize. A "hello world" takes about 250ms versus Perl's 15ms. That's annoying for command-line scripts, irrelevant for services. + +**Short-lived programs don't benefit from JIT compilation.** If your script runs for less than a few seconds, the JVM's just-in-time compiler never kicks in. Test suites, where each `.t` file is a separate process, run slower than native Perl. + +**Long-running programs can be significantly faster.** After warmup (~10,000 iterations through hot code paths), the JIT compiler optimizes aggressively. Here's a real benchmark — closure calls in a tight loop: + +``` +$ time perl dev/bench/benchmark_closure.pl +timethis 5000: 7 wallclock secs ( 7.49 usr ) @ 667/s + +$ time ./jperl dev/bench/benchmark_closure.pl +timethis 5000: 4 wallclock secs ( 3.54 usr ) @ 1411/s +``` + +PerlOnJava runs this benchmark **2.1x faster** than native Perl. The JVM's C2 compiler inlines calls and unrolls loops. + +Bottom line: use it for long-running services and batch jobs. Not for command-line tools that need to start instantly. + +## What Doesn't Work + +Some things just can't work on the JVM: + +- **fork()** — The JVM doesn't do Unix-style forking. Period. For tests that need fork, use native `perl`. + +- **Weak references** — `Scalar::Util::weaken` is a no-op. This breaks some cleanup patterns. + +- **DESTROY** — Object destructors never run. The JVM has garbage collection, not deterministic destruction. If your code depends on DEMOLISH or cleanup in destructors, it won't work. + +- **Some XS modules** — No Java implementation and no pure-Perl fallback means it won't work. + +- **Spurious warnings** — There's currently a bug where test description strings trigger `Argument "..." isn't numeric` warnings. Annoying, being fixed. + +## Cross-Platform, Single Artifact + +PerlOnJava runs on Linux, macOS, and Windows. Same JAR everywhere — the JVM's "write once, run anywhere" actually delivers here. Your deployment is Java 22+, the JAR, and the wrapper scripts. + +The project includes a **Dockerfile** for containerized deployments and a **Debian package recipe** (`make deb`) for system installation. + +## Getting Started + +```bash +# Clone and build +git clone https://github.com/fglock/PerlOnJava.git +cd PerlOnJava +make + +# Run some Perl +./jperl -E 'say "Hello from the JVM"' + +# Install a module +./jcpan Moo + +# Use it +./jperl -MMoo -E ' + package Point { + use Moo; + has x => (is => "ro"); + has y => (is => "ro"); + } + my $p = Point->new(x => 3, y => 4); + say "Point: (", $p->x, ", ", $p->y, ")" +' +``` + +The project is at [github.com/fglock/PerlOnJava](https://github.com/fglock/PerlOnJava), licensed under the Artistic License 2.0. Issues and contributions welcome. + +--- + +*PerlOnJava implements Perl 5.42 semantics and is validated against the Perl test suite. It's been in development since 2024, building on nearly 30 years of prior work on Perl-JVM integration.* diff --git a/dev/presentations/blogs_perl_org_jcpan_2026/blog-post.md b/dev/presentations/blogs_perl_org_jcpan_2026/blog-post.md new file mode 100644 index 000000000..48a16498e --- /dev/null +++ b/dev/presentations/blogs_perl_org_jcpan_2026/blog-post.md @@ -0,0 +1,130 @@ +# PerlOnJava - CPAN client for the JVM + +[PerlOnJava](https://github.com/fglock/PerlOnJava) now includes `jcpan`, a CPAN client that installs pure Perl modules without needing `make` or a C compiler. It runs on Linux, macOS, and Windows - just add Java 22+. + +## Installing modules + +```bash +$ ./jcpan Moo +``` + +Modules are installed to `~/.perlonjava/lib/`, which is automatically in `@INC`. + +The interactive CPAN shell also works: + +``` +$ ./jcpan +cpan shell -- CPAN exploration and target module installation +cpan[1]> install DateTime +``` + +## Other tools + +PerlOnJava also includes: + +- `jperldoc` - documentation viewer +- `jprove` - TAP test harness + +```bash +$ ./jperldoc DateTime +$ ./jprove t/*.t +``` + +## How it works + +PerlOnJava includes a custom `ExtUtils::MakeMaker` that copies `.pm` files directly to the install location. No `make` step, no native toolchain. + +For XS modules (those with C code), PerlOnJava provides Java implementations for popular ones: + +| Module | Java Backend | +|--------|--------------| +| DateTime | java.time with JulianFields | +| JSON | fastjson2 | +| Digest::MD5/SHA | Java MessageDigest | +| DBI | JDBC | +| Compress::Zlib | java.util.zip | + +## Moo example + +Moo is a lightweight object system. After installing with `jcpan Moo`: + +```perl +package Point { + use Moo; + has x => (is => 'ro', default => 0); + has y => (is => 'ro', default => 0); + + sub distance { + my $self = shift; + return sqrt($self->x ** 2 + $self->y ** 2); + } +} + +my $p = Point->new(x => 3, y => 4); +print "Distance from origin: ", $p->distance, "\n"; +``` + +Output: +``` +Distance from origin: 5 +``` + +## DateTime example + +DateTime is one of the more complex CPAN modules. It passes 100% of tests on PerlOnJava: + +```bash +$ ./jcpan DateTime +``` + +```perl +use DateTime; +my $dt = DateTime->new( + year => 2026, + month => 3, + day => 28, + time_zone => 'America/New_York' +); +print $dt->strftime('%Y-%m-%d %Z'), "\n"; +``` + +Output: +``` +2026-03-28 EDT +``` + +The Java XS implementation uses `java.time.JulianFields` for Rata Die conversions and includes full leap second support. + +## What's included + +PerlOnJava ships with 568 Perl modules bundled in the jar — JSON, YAML, HTTP::Tiny, DBI, Archive::Tar, Test::More, Try::Tiny, and more. No installation needed. + +## Performance + +JVM startup is slower than Perl, but long-running programs benefit from JIT compilation. After warmup (~10K iterations), hot loops become significantly faster: + +``` +$ time perl dev/bench/benchmark_closure.pl +timethis 5000: 7 wallclock secs ( 7.49 usr ... ) @ 667/s + +$ time ./jperl dev/bench/benchmark_closure.pl +timethis 5000: 4 wallclock secs ( 3.54 usr ... ) @ 1411/s +``` + +**PerlOnJava: 2.1x faster** for this closure benchmark. + +Short-lived programs (like individual test files) won't see this benefit - the JIT doesn't have time to warm up. Running test suites is slower than native Perl. + +## Current limitations + +- `fork` - not available (use native `perl` for fork-heavy tests) +- `weaken` / `DESTROY` - not implemented +- XS modules without Java implementations or pure Perl fallbacks won't work +- JVM startup overhead - short programs are slower than Perl +- Test suites run slower due to per-file JVM startup +- Some module tests fail due to unimplemented features (Moo's `weaken` tests, for example) +- Spurious warnings: PerlOnJava currently emits `Argument "..." isn't numeric` warnings for test description strings - this will be fixed + +--- + +More info: https://github.com/fglock/PerlOnJava diff --git a/dev/presentations/blogs_perl_org_jcpan_2026/example_datetime.pl b/dev/presentations/blogs_perl_org_jcpan_2026/example_datetime.pl new file mode 100644 index 000000000..d1e916fae --- /dev/null +++ b/dev/presentations/blogs_perl_org_jcpan_2026/example_datetime.pl @@ -0,0 +1,13 @@ +#!/usr/bin/env jperl +use strict; +use warnings; +use DateTime; + +my $dt = DateTime->new( + year => 2026, + month => 3, + day => 28, + time_zone => 'America/New_York' +); + +print $dt->strftime('%Y-%m-%d %Z'), "\n"; diff --git a/dev/presentations/blogs_perl_org_jcpan_2026/example_moo.pl b/dev/presentations/blogs_perl_org_jcpan_2026/example_moo.pl new file mode 100644 index 000000000..a1682e694 --- /dev/null +++ b/dev/presentations/blogs_perl_org_jcpan_2026/example_moo.pl @@ -0,0 +1,18 @@ +#!/usr/bin/env jperl +use strict; +use warnings; + +package Point { + use Moo; + has x => (is => 'ro', default => 0); + has y => (is => 'ro', default => 0); + + sub distance { + my $self = shift; + return sqrt($self->x ** 2 + $self->y ** 2); + } +} + +my $p = Point->new(x => 3, y => 4); +print "Point: (", $p->x, ", ", $p->y, ")\n"; +print "Distance from origin: ", $p->distance, "\n"; diff --git a/dev/presentations/blogs_perl_org_jcpan_2026/jruby_jython_reference.md b/dev/presentations/blogs_perl_org_jcpan_2026/jruby_jython_reference.md new file mode 100644 index 000000000..24685d203 --- /dev/null +++ b/dev/presentations/blogs_perl_org_jcpan_2026/jruby_jython_reference.md @@ -0,0 +1,140 @@ +# JRuby and Jython: Blog and Presentation Patterns + +Research on how JRuby and Jython present themselves, for reference when writing PerlOnJava content. + +## JRuby + +### Homepage Messaging (jruby.org) + +**Tagline:** "The Ruby Programming Language on the JVM" + +**Key selling points (3 pillars):** +1. **The Best of the JVM** - High performance, Real threading, Vast array of libraries +2. **It's Just Ruby** - Ruby 3.4 compatible +3. **Platform Independent** - Easy to install, Easy migration, No hassles + +**Getting started:** Simple 3-step process: +1. Extract JRuby into a directory +2. Add bin to PATH +3. Test: `jruby -v` + +### Blog Post Style (blog.jruby.org) + +**Major release posts (JRuby 10):** +- Long-form, detailed technical content (~2000+ words) +- Clear section headers: "Moving Forward", "Getting Started", "Riding the Rails" +- Performance benchmarks with before/after comparisons +- Code examples showing both Ruby and Java interop +- Discusses JVM features leveraged (invokedynamic, AppCDS, CRaC, Leyden) +- Acknowledges limitations and roadmap + +**Performance claims:** +- Shows actual benchmark output with timing +- Compares JRuby 9.4 vs JRuby 10 +- Discusses JIT warmup explicitly +- Mentions startup time improvements with specific numbers + +**Release notes style:** +- Short intro paragraph with links +- Version compatibility stated clearly (Ruby 3.4) +- Thank contributors by GitHub handle +- List of issues/PRs resolved with links + +### Key Themes + +1. **Compatibility first** - "Ruby 3.4 compatible", test suite numbers +2. **Performance** - JIT optimization, startup improvements, benchmarks +3. **Ecosystem** - Rails support, gem compatibility, Maven integration +4. **Enterprise features** - Threading, JDBC, application servers + +--- + +## Jython + +### Homepage Messaging (jython.org) + +**Opening:** "The Jython project provides implementations of Python in Java" + +**Use cases (3 specific):** +1. **Embedded scripting** - Java programmers add Jython for end-user scripting +2. **Interactive experimentation** - Debug Java systems using Jython +3. **Rapid application development** - "Python programs are typically 2-10x shorter" + +**Code examples:** Shows both directions: +- Java code calling Python +- Python code using Java classes + +**Who uses Jython:** Lists real-world users: +- IBM Websphere, Apache PIG, ImageJ, Robot Framework, etc. + +### News/Blog Style + +**Release announcements:** +- Short, factual paragraphs +- Links to Maven Central for downloads +- Links to NEWS file for details +- Lists notable features as bullet points +- Mentions Java version compatibility (tested against Java 8 and 11) + +**Feature highlights:** +- "slim" JAR for Gradle/Maven +- Elimination of reflective access warnings +- Locale support improvements +- Console logging via java.util.logging + +### Key Themes + +1. **Integration focus** - Embedding, scripting, Java interop +2. **Enterprise adoption** - Lists big-name users +3. **Practical benefits** - "2-10x shorter programs" +4. **Simplicity** - Shows code, not just describes + +--- + +## Lessons for PerlOnJava + +### What works well: + +1. **Clear tagline** - "The X Programming Language on the JVM" +2. **Three-pillar messaging** - Pick 3 key benefits, make them memorable +3. **Show code early** - Both projects show working examples on homepage +4. **Bidirectional examples** - Show Perl→Java AND Java→Perl +5. **Real benchmarks** - Actual output, not just claims +6. **Acknowledge limitations** - Both are honest about what doesn't work +7. **Name real users** - If possible, list who's using it +8. **Simple getting started** - 3 steps or less + +### Blog post patterns: + +1. **Release notes:** Short, link to details, thank contributors +2. **Feature posts:** Longer, technical, show benchmarks +3. **Tutorial posts:** Step-by-step with code + +### Things to emphasize for PerlOnJava: + +| JRuby/Jython Feature | PerlOnJava Equivalent | +|----------------------|----------------------| +| Ruby/Python compatibility | Perl 5.42+ compatibility | +| Threading | JVM threading (Perl threads not implemented) | +| JDBC database access | DBI with JDBC backend | +| Embedding via JSR-223 | Same - ScriptEngine API | +| gem install | jcpan for CPAN modules | +| Rails support | N/A (but CPAN ecosystem) | +| Fast startup (AppCDS/CRaC) | Potential future work | + +### PerlOnJava differentiators: + +1. **Single JAR** - Unlike JRuby which has multiple distribution options +2. **150+ modules bundled** - No separate install needed +3. **Dual backend** - JVM + Internal VM for different use cases +4. **No native toolchain** - jcpan works without make/gcc + +--- + +## Sources + +- https://www.jruby.org/ +- https://blog.jruby.org/ +- https://www.jruby.org/2026/03/04/jruby-10-0-4-0.html +- https://www.jython.org/ +- https://www.jython.org/news