Java приложение как служба windows

I’ve just inherited a java application that needs to be installed as a service on XP and vista. It’s been about 8 years since I’ve used windows in any form and I’ve never had to create a service, let alone from something like a java app (I’ve got a jar for the app and a single dependency jar — log4j). What is the magic necessary to make this run as a service? I’ve got the source, so code modifications, though preferably avoided, are possible.

3

Apache Commons Daemon is a good alternative. It has Procrun for windows services, and Jsvc for unix daemons. It uses less restrictive Apache license, and Apache Tomcat uses it as a part of itself to run on Windows and Linux! To get it work is a bit tricky, but there is an exhaustive article with working example.

Besides that, you may look at the bin\service.bat in Apache Tomcat to get an idea how to setup the service. In Tomcat they rename the Procrun binaries (prunsrv.exe -> tomcat6.exe, prunmgr.exe -> tomcat6w.exe).

Something I struggled with using Procrun, your start and stop methods must accept the parameters (String[] argv). For example «start(String[] argv)» and «stop(String[] argv)» would work, but «start()» and «stop()» would cause errors. If you can’t modify those calls, consider making a bootstrapper class that can massage those calls to fit your needs.

5

With Apache Commons Daemon you can now have a custom executable name and icon! You can also get a custom Windows tray monitor with your own name and icon!

I now have my service running with my own name and icon (prunsrv.exe), and the system tray monitor (prunmgr.exe) also has my own custom name and icon!

  1. Download the Apache Commons Daemon binaries (you will need prunsrv.exe and prunmgr.exe).

  2. Rename them to be MyServiceName.exe and MyServiceNamew.exe respectively.

  3. Download WinRun4J and use the RCEDIT.exe program that comes with it to modify the Apache executable to embed your own custom icon like this:

    > RCEDIT.exe /I MyServiceName.exe customIcon.ico
    > RCEDIT.exe /I MyServiceNamew.exe customTrayIcon.ico
    
  4. Now install your Windows service like this (see documentation for more details and options):

    > MyServiceName.exe //IS//MyServiceName \
      --Install="C:\path-to\MyServiceName.exe" \
      --Jvm=auto --Startup=auto --StartMode=jvm \
      --Classpath="C:\path-to\MyJarWithClassWithMainMethod.jar" \
      --StartClass=com.mydomain.MyClassWithMainMethod
    
  5. Now you have a Windows service of your Jar that will run with your own icon and name! You can also launch the monitor file and it will run in the system tray with your own icon and name.

    > MyServiceNamew.exe //MS//MyServiceName
    

8

One more option is WinRun4J. This is a configurable java launcher that doubles as a windows service host (both 32 and 64 bit versions). It is open source and there are no restrictions on its use.

(full disclosure: I work on this project).

3

Yet another answer is Yet Another Java Service Wrapper, this seems like a good alternative to Java Service Wrapper as has better licensing. It is also intended to be easy to move from JSW to YAJSW. Certainly for me, brand new to windows servers and trying to get a Java app running as a service, it was very easy to use.

Some others I found, but didn’t end up using:

  • Java Service Launcher I didn’t use this because it looked more complicated to get working than YAJSW. I don’t think this is a wrapper.
  • JSmooth Creating Window’s services isn’t its primary goal, but can be done. I didn’t use this because there’s been no activity since 2007.

2

If you use Gradle Build Tool you can try my windows-service-plugin, which facilitates using of Apache Commons Daemon Procrun.

To create a java windows service application with the plugin you need to go through several simple steps.

  1. Create a main service class with the appropriate method.

    public class MyService {
    
        public static void main(String[] args) {
            String command = "start";
            if (args.length > 0) {
                command = args[0];
            }
            if ("start".equals(command)) {
                // process service start function
            } else {
                // process service stop function
            }
        }
    
    }
    
  2. Include the plugin into your build.gradle file.

    buildscript {
      repositories {
        maven {
          url "https://plugins.gradle.org/m2/"
        }
      }
      dependencies {
        classpath "gradle.plugin.com.github.alexeylisyutenko:windows-service-plugin:1.1.0"
      }
    }
    
    apply plugin: "com.github.alexeylisyutenko.windows-service-plugin"
    

    The same script snippet for new, incubating, plugin mechanism introduced in Gradle 2.1:

    plugins {
      id "com.github.alexeylisyutenko.windows-service-plugin" version "1.1.0"
    }
    
  3. Configure the plugin.

    windowsService {
      architecture = 'amd64'
      displayName = 'TestService'
      description = 'Service generated with using gradle plugin'   
      startClass = 'MyService'
      startMethod = 'main'
      startParams = 'start'
      stopClass = 'MyService'
      stopMethod = 'main'
      stopParams = 'stop'
      startup = 'auto'
    }
    
  4. Run createWindowsService gradle task to create a windows service distribution.

That’s all you need to do to create a simple windows service. The plugin will automatically download Apache Commons Daemon Procrun binaries, extract this binaries to the service distribution directory and create batch files for installation/uninstallation of the service.

In ${project.buildDir}/windows-service directory you will find service executables, batch scripts for installation/uninstallation of the service and all runtime libraries.
To install the service run <project-name>-install.bat and if you want to uninstall the service run <project-name>-uninstall.bat.
To start and stop the service use <project-name>w.exe executable.

Note that the method handling service start should create and start a separate thread to carry out the processing, and then return. The main method is called from different threads when you start and stop the service.

For more information, please read about the plugin and Apache Commons Daemon Procrun.

2

I think the Java Service Wrapper works well. Note that there are three ways to integrate your application. It sounds like option 1 will work best for you given that you don’t want to change the code. The configuration file can get a little crazy, but just remember that (for option 1) the program you’re starting and for which you’ll be specifying arguments, is their helper program, which will then start your program. They have an example configuration file for this.

answered Sep 16, 2008 at 2:34

Ed Thomas's user avatar

Ed ThomasEd Thomas

1,1631 gold badge12 silver badges21 bronze badges

Use «winsw» which was written for Glassfish v3 but works well with Java programs in general.

Require .NET runtime installed.

2

JavaService is LGPL. It is very easy and stable. Highly recommended.

With Java 8 we can handle this scenario without any external tools. javapackager tool coming with java 8 provides an option to create self contained application bundles:

-native type
Generate self-contained application bundles (if possible). Use the -B option to provide arguments to the bundlers being used. If type is specified, then only a bundle of this type is created. If no type is specified, all is used.

The following values are valid for type:

-native type
Generate self-contained application bundles (if possible). Use the -B option to provide arguments to the bundlers being used. If type is specified, then only a bundle of this type is created. If no type is specified, all is used.

The following values are valid for type:

all: Runs all of the installers for the platform on which it is running, and creates a disk image for the application. This value is used if type is not specified.
installer: Runs all of the installers for the platform on which it is running.
image: Creates a disk image for the application. On OS X, the image is the .app file. On Linux, the image is the directory that gets installed.
dmg: Generates a DMG file for OS X.
pkg: Generates a .pkg package for OS X.
mac.appStore: Generates a package for the Mac App Store.
rpm: Generates an RPM package for Linux.
deb: Generates a Debian package for Linux.

In case of windows refer the following doc we can create msi or exe as needed.

exe: Generates a Windows .exe package.
msi: Generates a Windows Installer package.

1

I’ve used JavaService before with good success. It hasn’t been updated in a couple of years, but was pretty rock solid back when I used it.

answered Sep 16, 2008 at 2:20

2

I didn’t like the licensing for the Java Service Wrapper. I went with ActiveState Perl to write a service that does the work.

I thought about writing a service in C#, but my time constraints were too tight.

answered Sep 16, 2008 at 2:27

Hugh Buchanan's user avatar

Hugh BuchananHugh Buchanan

2,0313 gold badges14 silver badges9 bronze badges

1

I always just use sc.exe (see http://support.microsoft.com/kb/251192). It should be installed on XP from SP1, and if it’s not in your flavor of Vista, you can download load it with the Vista resource kit.

I haven’t done anything too complicated with Java, but using either a fully qualified command line argument (x:\java.exe ….) or creating a script with Ant to include depencies and set parameters works fine for me.

answered Sep 16, 2008 at 2:34

Kevin's user avatar

KevinKevin

611 silver badge6 bronze badges

1

it’s simple as you have to put shortcut in

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

Windows 10 :
In Run shell:startup

in it’s property -> shortcut -> target — > java.exe -jar D:\..\runJar.jar

NOTE: This will run only after you login


With Admin Right

sc create serviceName binpath= "java.exe -jar D:\..\runJar.jar" Will create windows service

if you get timeout use cmd /c D:\JAVA7~1\jdk1.7.0_51\bin\java.exe -jar d:\jenkins\jenkins.war but even with this you’ll get timeout but in background java.exe will be started. Check in task manager

NOTE: This will run at windows logon start-up(before sign-in, Based on service ‘Startup Type‘)

Detailed explanation of creating windows service

1

Another good option is FireDaemon. It’s used by some big shops like NASA, IBM, etc; see their web site for a full list.

answered Sep 16, 2008 at 6:40

Andrew Swan's user avatar

Andrew SwanAndrew Swan

13.5k22 gold badges70 silver badges99 bronze badges

0

I am currently requiring this to run an Eclipse-based application but I need to set some variables first that is local to that application. sc.exe will only allow executables but not scripts so I turned to autoexnt.exe which is part of the Windows 2003 resource kit. It restricts the service to a single batch file but I only need one batch script to be converted into a service.

ciao!

answered Sep 23, 2008 at 0:26

I have been using jar2exe for last few years to run our Java applications as service on Windows. It provides an option to create an exe file which can be installed as Windows service.

It’s possible to implement a Windows service in 100% Java code by combining the use of Foreign Memory and Linker API (previewing from JDK16 upwards) with OpenJDK jextract project to handle the Windows Service callbacks, and then use jpackage to produce a Windows EXE which can then be registered as a Windows Service.

See this example which outlines the work needed to implement a Windows service. All Windows service EXE must provide callbacks for the main entrypoint ServiceMain and Service Control Handler, and use API calls StartServiceCtrlDispatcherW, RegisterServiceCtrlHandlerExW and SetServiceStatus in Advapi.DLL.

The flow of above callbacks in Java with Foreign Memory structures are:

main()
    Must register ServiceMain using StartServiceCtrlDispatcherW
    Above call blocks until ServiceMain exits
        
void ServiceMain(int dwNumServicesArgs, MemoryAddress lpServiceArgVectors)
    Must register SvcCtrlHandler using RegisterServiceCtrlHandlerExW
    Use SetServiceStatus(SERVICE_START_PENDING)
    Initialise app
    Use SetServiceStatus(SERVICE_RUNNING)
    wait for app shutdown notification
    Use SetServiceStatus(SERVICE_STOPPED)

int SvcCtrlHandler(int dwControl, int dwEventType, MemoryAddress lpEventData, MemoryAddress lpContext)
    Must respond to service control events and report back using SetServiceStatus
    On receiving SERVICE_CONTROL_STOP reports SetServiceStatus(SERVICE_STOP_PENDING)
        then set app shutdown notification

Once finished the Java application, jpackage can create runtime+EXE which can then be installed and registered as a Windows Service. Run as Adminstrator (spaces after = are important):

 sc create YourJavaServiceName type= own binpath= "c:\Program Files\Your Release Dir\yourjavaservice.exe"

Введение

В общем, расскажу я вам сказку про Terracota’овский сервер.

В одной далёкой галактике

На одной из наших виртуалок на конторе, жила-была ВебСфера(прожорливая сволочь), которая голодала от недостатка места на жёстом диске и, как результат, приложения живущие на ней начинали сильно лагать, вследствие чего приходилось часто приходилось ребутать вирталку для нормальной жизни приложений на сфере. Но не одиноки мы во вселенной(возможно), тем не менее на Земле так точно не одни, так и на виртуалке кроме сферы жили и другие монстры, куда менее прожорливые, вроде Terracotta сервера и других приложений. Так вот,

вследствие перерождения галактики

перезагрузки виртуалки терракотовский сервак постоянно надо было поднимать, так как наши приложения хранящие данные в кластере не могли запуститься, потому ожидали коннекшена от лежачего сервака. Естественно, кому-то из команды постоянно приходилось запускать Терракотту вручную, что, в общем, то было утомительно:). Мною было принято решение

создать галактический отряд

windows сервис по поднятию сервера после перезагрузки виртуалки, что казалось задачей на 15 минут, и тут я наткнулся на

Дарта Вейдера

подводные камни. Хренова конечно, что из коробки в терракоте нет создания сервиса после установки.

К делу

Посмотрим, что у нас есть:

  1. Windows Server 2008 какой-то edition(с установленным Windows Resource Kit Tools).
  2. Установленная JDK6
  3. Terracotta
  4. Световые мечи

Чего не хватало:

  1. Пива
  2. srvany.exe – утилита, позволяющая запускать любую программу как службу, позволяя тем самым создавать пользовательские службы(странно но у меня на виртуалке этого файла не было). Скачаваем с инета.
  3. Знаний использования реестра.(Этот недостатот быстро отпадает)

Для начала скопируем скачанный srvany.exe в папки /bin с терракотой. В моём случае нехватало батника для создания сервиса, т.к. путь к терракоте содержал
пробелы. Если кто знает как в консоли прописывать пути без пробелов, прошу писать в комменты.
Сообственно createService.bat:
sc.exe create Terracotta binPath=“c:\Program Files\terracotta-3.2.1_1\bin\srvany.exe” type=own start=auto DisplayName=“Terracotta Server 3.2.1_1”
Думаю пояснений не требуется. Windows сервис требует данных о приложении в регистре. Создаём tcservice.reg:

REGEDIT4
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Terracotta\Parameters]
"Application"="\"C:\\Java\\jdk1.6.0_17\\bin\\java.exe\\"
"AppParameters"="-cp c:\\Program Files\\terracotta-3.2.1_1\\lib\\tc.jar -server -Xms4G –Xmx4G -XX:+HeapDumpOnOutOfMemoryError -Dtc.install-root=c:\\Program files\\terracotta-3.2.1_1 com.tc.server.TCServerMain"
"AppDirectory"="c:\\Program Files\\terracotta-3.2.1_1\\"

И запускаем его. Не знаю почему, да и в общем то времени на разборку не было, но скрипт у меня не запускался, а данные в регистр не попадали:(. Поэтому делаем всё ручками. Запускаем regedit, заходим по пути HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Terracotta\Parameters и создаём там строковые параметры типа Application с указанием путей типа С:\\Java\\jdk1.6.0_17\\bin\\java.exe Также можно применить дополнительные параметры, если терракота работает не с дефолтовыми настройками:

"AppParameters"="-cp c:\\ Program Files \\terracotta-3.2.1_1\\lib\\tc.jar -server –Xms512M –Xmx512M -XX:+HeapDumpOnOutOfMemoryError -Dtc.install-root=c:\\terracotta\\terracotta-3.2.1_1 com.tc.server.TCServerMain -f c:\\ Program Files \\my-tc-config.xml -n Server1"

Всё сделали. Тестим заходим в Панель Управления -> Администрирование –> Службы, ищем сервис с названием Terracotta Server 3.2.1_1. Запускаем. Провеяем порты и видим, что приложение стартануло, если нет, проверьте правильность указания путей. Надо бы ещё убедиться что приложение встанет после перезагрузки виртуалки. Значит, что делаем?

Выключаем сервер из розетки

перезагружаем виртуалку – видим, что сервер поднялся автоматически. Ура. Таким макаром можно любое Java приложение сделать как windows сервис.

I would like to run a Java application as a service. Unfortunately, I am limited in that I can’t use something like the Java Service Wrapper (which does appear to be an excellent tool).

Is there any way of running an executable JAR, as a service, without relying on external applications? I currently have the service installed, but it fails to start. This is where I am getting stuck and I haven’t been able to find anything on Google other than information about the JSW.

palacsint's user avatar

palacsint

28.4k11 gold badges82 silver badges109 bronze badges

asked Sep 10, 2009 at 16:44

JasCav's user avatar

6

There’s an LGPL clone of the Java Service Wrapper: http://yajsw.sourceforge.net

BTW, IANAL, but I suspect that JSW people are spreading FUD, and their software can be used to service-enable commercial applications under GPL license, just like one can gzip a commercial app for redistribution. I could be completely wrong about this, though :)

answered Sep 10, 2009 at 18:16

ykaganovich's user avatar

ykaganovichykaganovich

14.7k8 gold badges60 silver badges96 bronze badges

3

A program that should run as windows service must provide certain functions that the windows service manager uses to communicate with that service.

As long as there is no JVM that implements this functions directly (and I know of none) you will need some kind of wrapper.

I have successfully used srvany for a java based windows service (Basically it allows to run any program as windows service and it works fine with java)

answered Sep 10, 2009 at 17:56

Wolfgang's user avatar

WolfgangWolfgang

3,4603 gold badges28 silver badges38 bronze badges

1

You can use NSSM like this:

nssm install MyService "%JAVA_HOME%\bin\java.exe" -jar "path\to\the\file.jar"

answered May 24, 2016 at 14:15

Anthony O.'s user avatar

Anthony O.Anthony O.

22.3k18 gold badges108 silver badges163 bronze badges

I haven’t tried it (yet), but Launch4j looks like it could suit your needs.

answered Sep 10, 2009 at 16:58

dustmachine's user avatar

dustmachinedustmachine

10.6k5 gold badges26 silver badges28 bronze badges

1

one more option winrun4j. the license is eclipse’s CPL.

answered Sep 10, 2009 at 20:09

Peter Smith's user avatar

Peter SmithPeter Smith

7536 silver badges7 bronze badges

1

The most simple way I found was RunAsService.

A co-worker recommended a tool called SC, but I did not try it.

answered Jun 18, 2010 at 9:53

toba303's user avatar

toba303toba303

931 silver badge5 bronze badges

JSmooth can do it, and it is scriptable with ant.

answered Oct 11, 2010 at 8:18

Thorbjørn Ravn Andersen's user avatar

1

In this blog post, I show you how to install a Java application with WinSW as a Windows service. WinSW is an executable binary, which can be used to wrap and manage any custom process as a Windows service.

Demo

First, we need a demo application. I use a simple Quarkus application for this purpose. You can install any Java application as a service. Usually, you install applications that run forever like an HTTP server in this case.

To bootstrap a Quarkus application run this command

mvn io.quarkus:quarkus-maven-plugin:2.5.1.Final:create -DprojectGroupId=com.testlab -DprojectArtifactId=testservice -DclassName="com.testlab.testservice.GreetingResource" -Dpath="/hello"
cd testservice

We use the application as is with just a small change. I want to create a fat jar. A jar with all dependent libraries included.
You can package your application in any way you want. In this case, I prefer the fat jar installation because I only have to copy one file.

Open src\main\resources\application.properties and insert the following property

quarkus.package.type=uber-jar

Now you can package the application with .\mvnw.cmd package. You find the jar in the target folder: testservice-1.0.0-SNAPSHOT-runner.jar

Preparation

You can proceed in different ways. I usually create a new folder where I copy all the files into it that are needed to run the application as a service. Copy the jar from the target folder into this directory.

Java

Next, I download the Java Runtime and also copy it into this folder. This is optional. Your service can access a globally installed Java. But I like to have everything contained in one folder and don’t have to worry about somebody updating or removing the globally installed Java.

I usually download Java from Adoptium. On the release page, you find the ZIP file with the JDK for Windows.

Download the ZIP file and unzip it into the install folder. I unzip it into the java subfolder, so the java.exe is accessible with this path java\bin\java.exe

WinSW

Open the release page of the WinSW project and download the executable suitable for your platform.
https://github.com/winsw/winsw/releases

For this demo installation, I download WinSW.NET461.exe. WinSW runs on Windows with .NET Framework 2.0, 4.0 or 4.6.1. If you need to install the service on a Windows without .NET framework, download the core variant WinSW-x64.exe for 64-bit or WinSW-x86.exe` for 32-bit systems.

Copy the executable also into the folder where your jar resided. Rename the WinSW executable to any name you like. For this example, I rename it to testservice.exe. Create an XML with the same base name: testservice.xml. Make sure that the exe and XML file are located in the same folder.

Open the XML file and paste the following code into it.

<service>
  <id>testservice</id>
  <name>Test Service</name>
  <description>This is a test service.</description>
  <executable>"%BASE%\java\bin\java"</executable>
  <arguments>-jar "%BASE%\testservice-1.0.0-SNAPSHOT-runner.jar"</arguments>
  <logmode>rotate</logmode>
  <stopparentprocessfirst>true</stopparentprocessfirst>
</service>

Check the executables path and arguments. %BASE% is an environment variable that points to the directory where the WinSW executable is located. If you want to start the application with a Java that is on the %PATH%, just use <executable>java</executable>.

Relevant here is <stopparentprocessfirst>. This tells WinSW to shutdown the parent process first. In our case this is useful because the main process opens a console (java), which can respond to Ctrl+C and will gracefully shutdown the child process (the Java application).

Check out this wiki page to learn more about all the supported options:
https://github.com/winsw/winsw/blob/master/doc/xmlConfigFile.md

Directory structure

The directory structure of my install folder.

testservice.exe
testservice.xml
testservice-1.0.0-SNAPSHOT-runner.jar
java
  bin
    java.exe
    ...
  conf
  legal
  lib
  ...

Installation

With everything in place you install the Windows service with this command

testservice.exe install

The WinSW command supports the following options:

  • install: Install the service
  • uninstall: Uninstall the service
  • start: Start the installed service
  • stop: Stop the service
  • restart: Restart the service
  • status: Show the current service status (NonExistent, Started, Stopped)

The service by default is installed with start mode Automatic. That means Windows starts the service when it boots up. You can change that with the <startmode> configuration in the XML file.

  <startmode>Manual</startmode>

To test the installation, open http://localhost:8080 in a browser. You should see the default start page of Quarkus. To test if the graceful shutdown works, stop the service

testservice stop

Open the file testservice.out.log.

2020-05-06 05:30:52,501 INFO  [io.quarkus] (main) Profile prod activated. 
2020-05-06 05:30:52,501 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
2020-05-06 05:30:54,801 INFO  [io.quarkus] (main) testservice stopped in 0.032s

We see that the application shutdown gracefully because of the testservice stopped log entry.

Note that WinSW creates by default three logs files:

  • <service>.out.log: Console output from the application (System.out.println)
  • <service>.err.log: Error output from the application (System.err.println)
  • <service>.wrapper.log: Log output from WinSW itself

That concludes this tutorial about installing any Java applications as Windows Service. Optionally you could try and create a native image of your application with GraalVM. The service installation does not change a lot. You don’t need the JRE anymore and have to change <executable> accordingly.

Введение

В рамках одного из проектов требовалось разработать Windows службу, которая могла бы выполнять ряд действий с помощью Windows API, Websocket и стандартных средств Java. Далее в статье будут описаны шаги, которые были сделаны для создания такой службы.
Потребность в Windows службе возникла из-за необходимости иметь программу со следующими возможностями:

  • она должна быть постоянно запущена,
  • выполнялась от системного пользователя,
  • автоматически запускалась при старте системы,
  • чтобы её сложно было остановить обычному пользователю.

Создание минимизированной версии JRE

Так как GraalVM всё ещё не поддерживает создание исполняемых файлов под Windows, было решено воспользоваться другими возможностями, которые предоставляет экосистема Java, а именно создание минимизированной версии JRE.

Для того, чтобы создать минимизированную версию JRE, для начала необходимо узнать зависимости на определенные пакеты, которые будут включены в JRE. 

В первую очередь необходимо собрать jar-файл “fat jar” со всеми зависимостями.

Затем выполнить команду jdeps -s <путь к jar-файлу>, чтобы получить список всех зависимостей. Например:

jdeps -s application.jar
application.jar -> java.base
application.jar -> java.datatransfer
application.jar -> java.desktop
application.jar -> java.logging
application.jar -> java.net.http
application.jar -> java.sql
application.jar -> java.xml
application.jar -> jdk.unsupported
application.jar -> not found

Далее создаём нашу версию JRE с данными зависимостями:

jlink —module-path <путь к папке jmods, которая находится в jdk> —add-modules

java.base,java.datatransfer,java.desktop,java.logging,java.net.http,java.sql,java.xml,jdk.unsupported —strip-debug —compress 2 —no-header-files —no-man-pages —output <имя папки, которая будет содержать сгенерированную JRE>

Обратите внимание, что перечисление пакетов для опции —add-modules необходимо разделять запятой и не ставить между ними пробелов. Остальные опции отвечают за сжатие и убирание файлов и другой информации, которая не пригодится для выполнения программы.

После выполнения этих действий JRE будет занимать порядка 30 mb, вместо сотен.

Создание Windows службы из любого приложения

Java не имеет стандартных средств по созданию служб, поэтому были изучены сторонние инструменты и был выбран WinSW в силу его бесплатности и простоты использования.

WinSW

WinSW — это утилита, которая позволяет запустить и обернуть любой процесс как Windows службу. Для того, чтобы начать с ней работать, необходимо скачать исполняемый и конфигурационный файлы по этой ссылке https://github.com/kohsuke/winsw/releases.

Необходимо поместить эти два файла в директорию. Переименовать исполняемый файл на своё усмотрение и дать такое же название файлу конфигурации, затем поместить в эту директорию jar-файл приложения и созданную JRE.

В конфигурационном файле необходимо прописать минимальную конфигурацию:

<configuration>
  <!-- ID of the service. It should be unique across the Windows system-->
  <id>идентификатор службы</id>
  <!-- Display name of the service -->
  <name>имя</name>
  <!-- Service description -->
  <description>Описание</description>
  <!-- Path to the executable, which should be started -->
  <executable>jrebinjava.exe</executable>
  <arguments>-jar application.jar</arguments>
</configuration>

jrebinjava.exe — относительный путь внутри нашей папки к исполняемому файлу нашей JRE.

После этих действий можно установить службу, для этого необходимо выполнить команду от имени администратора:

winsw.exe install

Список команд можно посмотреть здесь.

Взаимодействие Java и Windows API

Для использования функций Windows (таких как создание нового процесса или добавление ключей реестра) в нашем приложении был использован JNA.
JNA (Java Native Access) предоставляет Java-программам легкий доступ к библиотекам, написанным на другом языке, без написания чего-либо, кроме кода Java. JNA позволяет напрямую вызывать нативные функции, используя обычный вызов метода Java. Большинство методов не требуют специальной обработки или конфигурации; не требуется шаблон или сгенерированный код.
Подключить и работать с JNA очень просто, для этого необходимо скачать jar-файл или подключить зависимость в сборщик проекта — в нашем случает Maven:

<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.0.0</version>
</dependency>

В нашем проекте мы использовали JNA для достижения следующих целей: заблокировать и сделать вновь доступным диспетчер задач 1) по комбинации Ctrl+Shift+Esc и 2) в меню, доступном по комбинации Ctrl+Alt+Del.

Для достижения этого были использованы класс Advapi32Util (удобная обёртка над библиотекой advapi32.dll) и интерфейс WinReg с полезными константами, которые предоставляют функциональность для внесения изменений в реестр Windows (Рисунок 1. Класс TaskManager с методами enable() и disable() для изменения ключей реестра диспетчера задач).

Рисунок 1. Класс TaskManager с методами enable() и disable() для изменения ключей реестра диспетчера задач.

  • Создать новый процесс от имени определённого пользователя Windows.  Для этого мы использовали метод CreateProcessAsUser()интерфейса Advapi32. В метод необходимо передать следующие параметры:
    • hToken — дескриптор токена пользователя, для которого мы запускаем процесс.
    • lpApplicationName — имя модуля, который должен быть выполнен.
    • lpCommandLine — командная строка для выполнения.
    • lpProcessAttributes — указатель на структуру SECURITY_ATTRIBUTES, которая определяет дескриптор безопасности для нового объекта процесса и определяет, могут ли дочерние процессы наследовать возвращенный дескриптор процесса.
    • lpThreadAttributes — указатель на структуру SECURITY_ATTRIBUTES, который определяет дескриптор безопасности для нового объекта потока и определяет, могут ли дочерние процессы наследовать возвращенный дескриптор потока.Создать новый процесс от имени определённого пользователя Windows. Для этого мы использовали метод CreateProcessAsUser() интерфейса Advapi32. В метод необходимо передать следующие параметры:
    • bInheritHandles — если этот параметр TRUE, каждый наследуемый дескриптор вызывающего процесса наследуется новым процессом. Если параметр FALSE, процессы не наследуются.
    • dwCreationFlags — флаги, которые контролируют класс приоритета и создают процесс.
    • lpEnvironment — указатель на блок среды для нового процесса. Если этот параметр равен NULL, новый процесс использует среду вызывающего процесса. Блок среды состоит из блока с нулевым завершением строк с нулевым завершением. Каждая строка имеет следующий вид: name = value 0.
    • lpCurrentDirectory — полный путь к текущему каталогу для процесса. Строка также может указывать путь UNC (universal naming convention).
    • lpStartupInfo — указатель на структуру STARTUPINFO или STARTUPINFOEX.lpProcessInformation — указатель на структуру PROCESS_INFORMATION, которая получает идентификационную информацию о новом процессе.

Рисунок 2. Метод для создания нового процесса для определённого пользователя Windows.

  • Получить токен активного пользователя, т.к. он необходим для создания процесса от определённого пользователя.
private static final Function activeSessionFunc = Function.getFunction("kernel32", "WTSGetActiveConsoleSessionId");
private static final Function userTokenFunc = Function.getFunction("wtsapi32", "WTSQueryUserToken");
public static WinNT.HANDLE getActiveUserToken() {
        PointerByReference pointer = new PointerByReference();
        WinDef.ULONG id = (WinDef.ULONG) activeSessionFunc.invoke(WinDef.ULONG.class, null);
        userTokenFunc.invoke(WinDef.BOOL.class, new Object[]{id, pointer});
        return new WinNT.HANDLE(pointer.getValue());
    }

Работа с процессами

Для работы и слежения за процессами в Windows был использован, добавленный в Java 9, класс ProcessHandle. ProcessHandle позволяет получать и производить различные манипуляции с процессами. В частности, при решении задачи, требовалось собирать PID процессов, фильтровать процессы на основе имени и принудительно завершать необходимые процессы.

Рисунок 3. Класс ProcessHandler с методами takeSnapshot() для создания снимка текущих процессов и closeNewProcesses() для завершения процессов, отличных от снимка.

Взаимодействие с другими компонентами системы

WebSocket

Для Java существует стандартизированный API для работы с WebSocket.

<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>

Но одного API недостаточно, поэтому для запуска кода была выбрана одна из его реализаций — Tyrus.

<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-server</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
 <artifactId>tyrus-container-grizzly-server</artifactId>
<version>1.14</version>
</dependency>

Далее можно создать минималистичный сервер и указать обработчики (EndPoints).

 var server = new Server(
                "localhost",
                8080,
                "/endpoints",
                null,
                EndPoint1.class,
    EndPoint2.class,
    ...);
        try {
            server.start();
            Thread.currentThread().join();
        } catch (Exception e) {
            log.error("Ooops! ", e);
        } finally {
            server.stop();
            log.info("Finally...");
        }

Заготовка обработчика выглядит следующим образом:

@ServerEndpoint(value = "endpoint/url")
public class SampleEndpoint {
 
    @OnOpen
    public void onOpen(Session session) throws IOException {
        // Вызывается при инициализации нового соединения
    }
 
    @OnMessage
    public void onMessage(Session session, Message message) throws IOException {
        // Вызывается при получении сообщения от клиента
    }
 
    @OnClose
    public void onClose(Session session) throws IOException {
        // Вызывается при закрытии соединения
    }
 
    @OnError
    public void onError(Session session, Throwable throwable) {
        // Вызывается при возникновении ошибок
    }
}

HTTP-клиент

С выпуском 11-ой версии Java в ней появился удобный HTTP-клиент, поэтому потребность в сторонних клиентах исчезла.

Для создания экземпляра клиента необходимо воспользоваться билдером. В простейшем случае:

var client = HttpClient .newBuilder() .build()

Далее необходимо создать запрос(request), например:

var request = HttpRequest.newBuilder()
                .uri(URI.create("https://myserver.com"))
                .timeout(Duration.ofMillis(1000))
                .header("Content-Type", "application/json")
                .POST(bean.toJSON())
                .build();

Затем этот запрос можно использовать для отправки на сервер:

var response = client.send(closeSession(sessionId, token), HttpResponse.BodyHandlers.ofString());

Заключение

Благодаря модульной организации версий Java 9 и выше, утилите WinSW, обновлённому Process API для взаимодействия с процессами операционной системы и библиотеки JNA (Java Native Access), которая предоставляет программам Java простой доступ к нативным библиотекам, мы смогли создать Windows службу с использованием языка Java, на котором была реализована и серверная часть. Что в итоге позволило не вводить в процесс разработки новый язык.

  • Java virtual machine launcher error unable to access jarfile c windows syswow64
  • Java не является приложением win32 windows xp
  • Java virtual machine launcher a java exception has occurred windows 10
  • Java запуск из командной строки windows
  • Java virtual machine for windows