DEV Community

Cover image for Java Service Steward, an open-source host for Java Windows services that reads wrapper.conf
Jay Yanez
Jay Yanez

Posted on

Java Service Steward, an open-source host for Java Windows services that reads wrapper.conf

Java Service Steward is a new Windows service host for Java applications. It reads the wrapper.conf format used by the Java Service Wrapper, follows the same command line and log format, and is licensed Apache-2.0 OR MIT. I wrote it because the Community Edition of the Java Service Wrapper has no 64-bit Windows build, and I did not want to buy a license or rewrite the service integration of applications that already had working configuration files.

Repository: https://github.com/jayyanez/java-service-steward

What it is

The distribution is two files, wrapper.exe and wrapper.jar. The executable is written in Rust and does the Windows part: it registers the service, launches the JVM, keeps a control channel to it over a loopback socket, restarts it when it exits unexpectedly or stops answering pings, writes and rotates wrapper.log, and handles Service Control Manager requests (stop, pause, resume, custom control codes). The JAR is compiled for Java 8 and contains the launcher classes and a small API. There is no native DLL and no JNI.

It only runs on 64-bit Windows. There is no Unix version.

What is compatible

  • Configuration. wrapper.conf with #include, #encoding, set.VAR=value, %VAR% expansion and numbered properties such as wrapper.java.additional.<n>. Relative paths resolve from the executable's directory, as before.
  • Command line. -c runs in a console, -i and -r install and remove the service, -t and -p start and stop it, -q queries it, -d requests a thread dump. Property overrides on the command line and -- pass-through of application arguments work the same way.
  • Service registration. An installed service's ImagePath calls wrapper.exe -s <conf>, so an existing registration keeps working.
  • Log format. Records use the same LPTM layout, the same column widths and the same SIZE, WRAPPER and JVM roll modes, so scripts that parse wrapper.log do not need changes.
  • Launchers. A configuration that names the original SimpleApp, StartStopApp or JarApp launcher in wrapper.java.mainclass starts with the equivalent bundled launcher; the log records the substitution.
  • Supervision. Start-up, ping and shutdown timeouts, restart throttling, exit-code actions, output filters (restart, shutdown, dump, GC, pause, resume), pause and resume.
  • Diagnostics. Thread dumps through -d, control code 255 or CTRL_BREAK; if the JVM runs with -Xrs, a jcmd based thread dump instead. On-demand HPROF heap dumps through --heapdump or control code 254. Each mechanism is detected separately; an unavailable tool produces an error message, not a restart.

Unsupported wrapper.* properties are reported by name in the log and ignored only when ignoring them is safe. Properties whose name contains password, secret, token, key, credential or vault are redacted from the log and are not sent to the JVM.

The full property-by-property table is at https://jayyanez.github.io/java-service-steward/compatibility.html.

What is not compatible

  • The original wrapper.jar and wrapper.dll are not loaded. Java Service Steward only works with its own JAR.
  • The original Java API is not implemented. An application that imports it has to switch to the project's Steward and ServiceListener classes (start, stop, restart, log, control events, start wait hints). Applications that use one of the launchers without touching the API need no source change.
  • No JMX, no named-pipe backend, no syslog.
  • Features that exist only in the Standard or Professional editions of the original are not there.

I built the project without reading the original's source code and without copying its documentation. The interface facts come from the public property and command reference, from my own configuration files and logs, and from observing behaviour from the outside.

Migrating

  1. Replace wrapper.exe and wrapper.jar in the installation directory.
  2. Run wrapper.exe -c wrapper.conf once in a console and read the warnings about unsupported properties.
  3. Start the service as usual.

wrapper.conf and the service registration stay as they are. Going back is copying the two old files over again.

How it is built

The supervisor is a single thread running a crossbeam select! loop over the JVM's output, Service Control Manager requests, protocol events from a reader thread and a 50 ms tick for timeouts. Output lines are capped at 64 KiB. The JVM runs in a Job Object with kill-on-close, so if the host process dies, the JVM and its child processes die with it. The control socket is bound to loopback and the JVM has to present a key it receives on its command line before the host accepts lifecycle messages.

CI runs on Windows with Temurin 8, 21 and 25 and starts real JVMs in the integration tests. Releases are built on GitHub Actions and carry a CycloneDX SBOM and Sigstore provenance attestations, which you can check with gh attestation verify. The executable is not Authenticode-signed yet.

Status

The current version is 0.3.2, so this is pre-1.0. The configuration format, command line, log format and supervision behaviour are meant for production; the Rust and Java APIs may still change between minor versions, and every incompatible change is listed in the changelog.

If you have a wrapper.conf that does not behave the same way under Java Service Steward, that is the most useful issue you can open.

Where to get it

Top comments (0)