JRuby on Alpine Linux

Jakub Jirutka edited this page Aug 17, 2016 · 6 revisions
Clone this wiki locally

tl;dr

Install JRuby from the Alpine repository, don’t use Oracle JDK and beware of JARs with bundled native binaries (JNI); check out List of Java libraries with JNI.

echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
apk update
apk add jruby
# install optional subpackages, if you need them
apk add jruby-irb jruby-maven jruby-minitest jruby-rdoc jruby-rake jruby-testunit

Learn about limitations

Alpine Linux doesn’t use GNU C library (glibc) as conventional distributions like Debian, Red Hat or Gentoo. Instead, it uses musl libc, a lightweight, fast, simple and standards-conform C library.

The problem is that musl is binary compatible to glibc only partially. If you download some strange binaries from somewhere, there’s a great chance that they will not work on the Alpine Linux.

This is not just about Alpine Linux, the same applies to all Linux distributions that use any other C library but glibc.

Java Runtime

You cannot run Oracle JDK on the Alpine Linux; it’s built for glibc and incompatible with musl libc. That’s not a problem, just install OpenJDK from the Alpine repository. Don’t worry about compatibility, Oracle JDK 8 is just branded distribution of OpenJDK 8, the code base is nearly identical.

JNI (aka native extensions)

Maybe you’ve never heard about “native extensions” (Java Native Interface) in Java / JVM. They are (fortunately) very rarely used/needed. This also implies that there’s no such good support in tools and knowledge about them like in Ruby, Python or Lua.

When you install a Ruby gem with native extensions (written usually in C or C++), there are compiled and built directly on your machine, against C library you use.

Not so in Java and JRuby. In Java land, “native extensions” are precompiled for various platforms and bundled directly in JARs. And that’s very bad practice. You often even don’t know that there are some native binaries in the JAR. The problem is that most people wrongly assume that Linux == glibc, so they build and test native binaries only against glibc. Sometimes the binaries built for glibc work even on musl, sometimes don’t.

What to do about it?

Well written libraries tries to load their native library from the system first (location is specified by java.library.path, by default it includes LD_LIBRARY_PATH, e.g. /lib, /usr/lib, …) and fallbacks to the bundled binaries. Some libraries (e.g. snappy-java) ignores system-provided libraries by default, but you can change this using an environment property.

Then you can simply compile the native library yourself, install it into /usr/lib and don’t touch the JAR at all.

What to do if the library insists on loading the bundled natives? Well, then you can build the JAR including native code and replace the original one. However, the better way is to patch the library (it’s actually very easy).

Actually, the first thing you should do is to look in the Alpine repository, maybe there’s already a package for it! If it’s not, you can also fill a request or try to ping @JakubJirutka.

How do I know that some JAR contains natives?

Look for files named *.so:

unzip -l some.jar '*.so'

What about JRuby itself?

JRuby itself uses two “native extensions” – jffi and jansi-native. There are packages with correct native binaries for both of them in the Alpine repository. These are automatically installed and used when you install JRuby from the Alpine package. So no problem here, JRuby should work well on the Alpine Linux. If not, please fill a bug.

List of Java libraries with JNI

Name Alpine package Patched JAR [1] Comment

lz4-java

java-lz4

yes :disappointed:

You must use patched JAR installed by the package (PR #83).

jansi-native

java-jansi-native

no :smile_cat:

jffi

java-jffi-native

no :smile_cat:

Neeeds libc6-compat.

jna

java-jna-native

no :smile_cat:

snappy-java

java-snappy-native

no* :smiley_cat:

If you use upstream’s JAR, then loading of system-provided library must be enabled using -Dorg.xerial.snappy.use.systemlib=true. This is set by default in Alpine’s jruby launch script.


1. If the library requires a patched JAR, then you must replace the original (upstream’s) JAR with the one from the Alpine package. Otherwise it’s sufficient to just install the “native extension” from the Alpine package.