Jar file as Windows service (+bonus jar to exe)

Alex Kuk
11 min readAug 22, 2019

--

So, your project has lived up to production and you’d like to start it with the OS and you don’t manually rise it every time. But how do we do that with a regular jar-file?

There are a bunch of options:

I) Put the bat-file with java –jar launch or exe-file in Windows startup (after making an exe file from jar).

Windows 7 : C:\users\All Users\Start Menu\Programs\Startup(Admin) or User home directory(%userProfile%)Windows 10 : In Run shell:startup

This will solve the problem, but your app will start in the best case only with the first login of any user (if you throw bat into startup for all users). But, as you know, a good admin doesn’t walk on the server.

II) Make the Windows task, starting the bat-file at system startup.

This option perfectly solves startup problem, but the manipulation with the app ends here, neither start-stop at any time, nor restart due to errors.

III) Run jar as win service with sc.exe (there is still Srvinstw.exe — GUI version, but we are true-programmers and must use the command-line).

This is a little program from the Resource Kit for creating services from exe-files.

But we have a jar-file, right?! No problem! There are options to try:

1) Run java.exe -jar with parameters directly.

2) make exe-file from jar and turn it into a service.

3) Use srvany.exe

a) run java.exe –jar from it

b) write bat-file with launch java -jar and run it from srvany.

IV) Use third-party software.

Let’s rake this pile of options, skipping the first two as the least interesting.

0) Application

First, we’ll write a small application with blackjack and resources, depending on passed parameters, in order to feel maximum pain in the agony of trying to make it work as a service.

The app will:

- swear that there are no parameters

- read certain setting files depending on the passed parameters

- parse these settings by an external library

- pour the same settings in the log every 5 seconds

An app example can be found here:

https://github.com/lkSnatch/tutorials/tree/master/jar-as-os-service

My result will be the executable jar-file, with dependent libraries and resources outside in the separate folder. You can do whatever you want, experiment.

1) Start the service with sc.exe

Well, to read sc help, just enter:

sc –bla_bla

The result will be «Error. Unidentified command» and further help on possible commands.

A) Create the service with java.exe –jar

sc create TestServ4 binPath= “E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe -Xmx400m -jar E:\Java\JavaPrograms\test\withoutSpaces\mainTutorial-1.0-SNAPSHOT.jar amazing prod” type= own start= auto error= normal DisplayName= TestService4

I have a jdk dragging around the app, not to depend on versions in the servers and to give my program solidity of a large size. The full path to java.exe is not necessary if it’s in the environment variables. It’s worth noting one annoying and unobtrusive peculiarity: «binPath= » — after any parameter (here is a binPath) immediately comes the equal sign, and then between it and the parameter value a space is required.

Great, the service is created, run it! Oops..:

(Windows could not start the TestService6 service on Local Computer. Error 1053: the service did not respond to the start or control request in a timely fashion.)

(My TestService will have different numbers in the examples, because the service wasn’t always deleted immediately, sometimes it was just marked for deletion)

Just in case, let’s look if the app log has appeared. But it has! Only a tiny one, containing info from the moment of trying to start the service up until the moment of issuing the error. What happened? Here’s what:

- service started launching java.exe with the required parameters

- our app started its work

- Windows service manager waited some time for a response from java.exe about it successful (or not) launch

- but java.exe returned nothing, because it owes nothing to anyone

- service manager counted down the timeout in seconds and shot off java

Conclusion: not every exe-file is equally useful to run as a service. One does not simply run any exe-file as a service, it must be specially sharpened for Windows service (at least return some data about it launch).

Or maybe try javaw? Try it, but double-V in the name doesn’t mean double victory.

B) Create the service with cmd /c java.exe -jar

What if you run cmd in cmd in cmd and in it run java? Will the whole chain close? Let’s start small — run only java from the cmd, and specify cmd as binpath at service start:

sc create TestServ3 binPath= “cmd /c E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe -Xmx400m -jar E:\Java\JavaPrograms\test\withoutSpaces\mainTutorial-1.0-SNAPSHOT.jar amazing prod” type= own start= auto error= normal DisplayName= TestService3

Likewise, it falls off after the timeout. But only the cmd process ends its worthless life, and our mega java app continues to breathe and delight.

It works? Yes! As a full service? No toothing way!

But put our jar-file in a folder with spaces to (“escape”) enjoy the creation of inclined sticks next to quotes a little more:

sc create TestServ6 binPath= “cmd /c E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe -Xmx400m -jar \”E:\Java\JavaPrograms\test\with Spaces\mainTutorial-1.0-SNAPSHOT.jar\” amazing prod” type= own start= auto error= normal DisplayName= TestService6

Look at the registry:

(DeleteFlag was set by me, so just assume you haven’t seen it here)

Doesn’t look bad, just parameters should be crammed in quotation marks for complete happiness.

Conclusion: tricks are good, but not always 100% useful.

C) Create the service with srvany.exe java.exe –jar

srvany.exe is also a program tool from the Resource Kit. Its work is simple — run the specified app. I.e. we register srvany.exe as a service with parameters indicating the path to our program. Order: start the service → run srvany → run our app.

sc create TestServ6 binPath= “E:\Java\JavaPrograms\test\with Spaces\srvany.exe” type= own start= auto error= normal DisplayName= TestService6

Add parameters to launch srvany:

reg add “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\TestServ6\Parameters” /v “Application” /d “\”E:\Java\JavaPrograms\jdk-11.0.1\bin\javaw.exe\” -Xmx400m -jar \”E:\Java\JavaPrograms\test\with Spaces\mainTutorial-1.0-SNAPSHOT.jar\” \”amazing\” \”prod\””

Starting.. watching.. waiting.. watching.. it works!! It’s all? This is the end? Or is something missing? Why invented third-party software (except of self-education and advertising purposes)? You can try to kill the process or invoke the exception in the app. And we will smoothly proceed to the next point.

Conclusion: srvany works, but catching errors is lame.

2) Start the service with third-party software NSSM.

Here is a short list of different software:

- procrun

- jsvc (pervert’s version: «It can run on Win32 via the Cygwin emulation layer» (с))

- winsw

- WinRun4J

- Java Service Wrapper

- NSSM (Non-Sucking Service Manager)

We’ll use the last one. For lazy readers repeat the question: why do we need a third-party software? For them, the answer is right from the nssm site:

«srvany and other service helper programs suck because they don’t handle failure of the application running as a service. If you use such a program you may see a service listed as started when in fact the application has died. nssm monitors the running service and will restart it if it dies.»

How does it work? All the same: nssm.exe installing as a service with specified parameters, when the service starts — nssm.exe itself starts, which in turn launches java.exe –jar. Here is a clear run in the task manager for you:

Now in order. Execute «nssm.exe install TestServ» to start the GUI.

(arguments here: -jar “E:\Java\JavaPrograms\test\with Spaces\mainTutorial-1.0-SNAPSHOT.jar” “amazing” “prod”)

We can do the same using the command line:

nssm install TestServ “E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe” “-Xmx400m -jar \”E:\Java\JavaPrograms\test\with Spaces\mainTutorial-1.0-SNAPSHOT.jar\” \”amazing\” \”prod\””nssm set TestServ DisplayName “Test Service”nssm set TestServ AppDirectory “E:\Java\JavaPrograms\test\with Spaces”nssm set TestServ AppRestartDelay 1000nssm set TestServ Description “Start java.exe with params -Xmx400m -jar mainTutorial.jar amazing prod”

it’s important for us to indicate AppDirectory, because the configs and libs used by us lie there, next to our app in the resources and res subfolders.

If you are tired of the service or you screwed up when creating it, then run the command:

nssm remove TestServ

Let’s look back-to-back at the registry entries:

(yes,yes, my folders «with Spaces» and «withoutSpaces» don’t beat in the commands and on screenshots, just consider that you’ve never seen this, for I am too lazy to redo)

All parameters and quotation marks are in place. Look at the service info:

Run service.. Hurray! Everything works, logs are written!

jun. 23, 2019 9:33:29 AM net.snatchTech.tutorials.App mainCONFIG: The app has been started with name: amazing_prodjun. 23, 2019 9:33:30 AM net.snatchTech.tutorials.App lambda$main$3INFO:servicename : serviceid : prodname_id : service_prodoptionsnumber : 1string : smthjun. 23, 2019 9:33:35 AM net.snatchTech.tutorials.App lambda$main$3INFO:servicename : serviceid : prodname_id : service_prodoptionsnumber : 1string : smth

Try to kill java or crash your application from the inside.

Conclusion: not written by our magnificent hands, brains and legs third-party software also has the right to life.

3) Automate it!

It’s worthless to start services on 30 servers by handles, the script is necessary! Omit spreading on machines, at least let’s create a service to correctly run the configuration designated on current server just with one variable name change.

But the script is easy! Sure? And if you add spaces in the names? The pain is hiding in details of fine-tuning quotation marks and their escaping. Suppose we put AppDir into a variable. How should we write it in the params and as the current directory? Where are the quotation marks necessary and where not?

set AppDir=”E:\Java\JavaPrograms\test\with Spaces\” orset AppDir=”E:\Java\JavaPrograms\test\with Spaces\\” orset AppDir=”E:\Java\JavaPrograms\test\with Spaces”

use in parameters:

nssm install %SrvName% %PathToProg% “%ProgParams% -jar %AppDir%%AppName% %AppParams% ornssm install %SrvName% \”%PathToProg%\” “%ProgParams% -jar \”%AppDir%%AppName%\” %AppParams%” ornssm install %SrvName% %PathToProg% “%ProgParams% -jar \”%AppDir%\\%AppName%\” %AppParams%”

as the current directory:

nssm set %SrvName% AppDirectory %AppDir% ornssm set %SrvName% AppDirectory \”%AppDir%\”

If configured incorrectly, you can get such an error when starting the service:

(Windows could not start the TestService on Local Computer. For more information, review the System Event Log. If this is a non-Microsoft service, contact the service vendor, and refer the service-specific error code 3.)

Go to system event log for details:

(Service Control Manager: Error: The service “TestService” stopped due to internal error: System can’t find specified path.)

See what’s wrong with registry paths:

Based on the results we draw conclusions about superfluous/missing quotes or slashes in the right places.

The result script looks like this:

@echo onset WrapperPath=”E:\Java\JavaPrograms\service\nssm.exe”set SrvName=”TestServ”set SrvDispName=”Test Service”set PathToProg=”E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe”set ProgParams=”-Xmx400m”set AppDir=”E:\Java\JavaPrograms\test\with Spaces”set AppName=”mainTutorial-1.0-SNAPSHOT.jar”set AppParams=\”amazing\” \”prod\”%WrapperPath% stop %SrvName%%WrapperPath% remove %SrvName% confirm%WrapperPath% install %SrvName% %PathToProg% “%ProgParams% -jar \”%AppDir%\\%AppName%\” %AppParams%”%WrapperPath% set %SrvName% DisplayName %SrvDispName%%WrapperPath% set %SrvName% AppDirectory %AppDir%%WrapperPath% set %SrvName% AppRestartDelay 1000%WrapperPath% set %SrvName% Description “Start %PathToProg% with params %ProgParams% -jar \”%AppDir%\\%AppName%\” %AppParams%”

And in the params in the registry:

Let’s test restarting. Run app with throwing an exception in a parallel thread, that the app terminates itself after two iterations.

set AppParams=\”amazing\” \”prod\” 2

Watch log files, see their re-creation, which means the service restarts our app upon completion. Great! Even logs are presented in the System Event Journal with a source nssm:

Program E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe for service TestServ exited with return code 0.Killing process tree of process 11196 for service TestServ with exit code 0Service TestServ action for exit code 0 is Restart. Attempting to restart E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe.

Service TestServ ran for less than 1500 milliseconds. Restart will be delayed by 2000 milliseconds.

Conclusion: Automate everything you need, not everything you can, otherwise it may take more time for useless work.

4) Bonus: jar to exe

Now let’s try to convert/wrap/shove our jar-file into exe-file and run it as a service.

Here is a couple of variants:

A) JDK tools, namely JavaPackager (JDK8, JDK10, JDK11).

I haven’t tried it yet, there are a lot of opportunities there, but it’s a bit confusing for our purposes now.

B) compilation from the stackoverflow.

C) Launch4j — standalone program or as a maven plugin.

Use standalone launch4j.

Fill the GUI form and specify path to jar and its params, try to embed JDK inside:

However, it can’t be started, the “start” button is unhighlighted. What’s wrong? Try to save the settings and run it from the cmd:

launch4jc.exe “E:\Java\JavaPrograms\test\with Spaces\exe\mainTutL4j.xml”

run, get:

Error: A JNI error has occurred, please check your installation and try againException in thread “main” java.lang.UnsupportedClassVersionError: net/snatchTech/tutorials/App has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0

Oh yea, it’s not visible on the screenshot, but I played around firstly and indicated the jre max version as 9, but it needs to be 11. Erase jre version, run, get:

some error: This application was configured to use a bundled Java Runtime Environment but the runtime is missing or corrupted.

Erm.. add to the Boundled JRE Path — «\javaw.exe». Doesn’t work. Erase javaw and bin folder. Voila! But wait, now run the exe-file:

E:\Java\JavaPrograms\test\with Spaces\exe>mainTut.exeError: Unable to initialize main class net.snatchTech.tutorials.AppCaused by: java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/type/TypeReference

Aaarrrgh.. it must be a fat jar.. Okay, move the resources folder. Launch.. Yeah it works!

It can be seen from the running processes that in our case mainTut.exe — is just a wrapper, that launches java-exe –jar with params. Moreover, in the “path to jar” argument is mainTut.exe itself! Let’s try to open our exe-file with the archiver:

Actually, here lies the same thing as in our jar-file.

One point — we allegedly have a built-in JRE, although it’s not noticeable by size and the archive inside. Let’s try to rename jdk folder for verification. That is, it doesn’t work. We’ll not be tormented with all the relations, the main thing — here is a working variant and it’s necessary to test it as a service.

sc create TestServ4 binPath= “E:\Java\JavaPrograms\test\with Spaces\exe\mainTut.exe” type= own start= auto error= normal DisplayName= TestService4

but the result is disappointing, like from the cmd — mainTut.exe closes, but java.exe remains. + somehow it’s necessary to throw different arguments to a jar, cause they are sewn into exe-file.

Conclusion: the simple wrapping of your jar-file in exe will not give you any profit to run it as a service. And no profit at all. The wrapping makes sense rather for assembling an installation package.

General conclusion.

Use NSSM to launch jar-file as a service and enjoy the uptime!

Feel free to shake hand on Medium or Telegram or Twitter.

--

--

Alex Kuk

java development, volleyball, performance engineering, kudo, deep learning