未分類

Javaアプリをwindowsサービス化する方法(令和最新版)

目的

前提条件

  • windows 11環境で確認。
  • Apache DaeamonでJavaアプリをwindows サービス化した。
    • Java8 32bitで確認。Java8 64bitでも動くらしいが、そちらは未確認。
    • ソースコードは以下に格納。動かす際はC直下においてください。
    • MavenとVisual Studio Codeを使用して開発した。
  • JavaアプリはアーカイブしたJARを使用。また、windowsのユーザ権限の問題か、JARの置き場所はユーザのフォルダの配下では動かず、C直下で確認した。

詳細設計

  • パッケージcom.exampleの中にServiceLauncherとServiceの2つのクラスがある。
  • ServiceLauncherはServiceクラスを管理するクラスで、windowsサービスからApache Deamon経由で呼び出されるクラス。public static void startService(String args)およびpublic static void stopService(String args)の2つのメソッドを実装する必要がある。
  • Serviceクラスは実際になんらかの処理を行うクラス。今は無限ループで10秒に一回ログを出力するクラス。

実装

  • ServiceLauncher
package com.example;

import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.apache.commons.daemon.DaemonInitException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
 
/**
 * サービスを起動停止するクラス.
 *
 */
public class ServiceLauncher implements Daemon {
 
    // ランチャー
    private static ServiceLauncher launcher;
 
    // サービス
    private Service service;

    private static final Logger logger = LogManager.getLogger(ServiceLauncher.class);
 
   /** 
    * サービス実行.
    *
    */
    public void run()  {
       
        service = new Service();
        try {
            Thread t = new Thread(service );
            t.setDaemon(true);
            t.start();
            // サービスが停止するまで制御を戻したらダメ
            t.join();
        } catch (InterruptedException e) {
        }
    }
  
   /**
    * サービス停止処理
    */
    public void terminate() {
        if (service != null) {
            service .stop();
        }
    }
 
   /**
    * Windowsサービス開始時にcommons Daemonからコールされる
    *
    */
    public static void startService(String args) {
        logger.info("start startService");
        launcher = new ServiceLauncher();
        launcher.run(); // サービス停止まで制御もどってこないはず
        logger.info("end startService");
    }
 
   /**
    * Windowsサービス停止時にcommons Daemonからコールされる
    *
    * @param args
    */
    public static void stopService(String args) {
        logger.info("start stopService");
        if (launcher != null) {
            launcher.terminate();
        }
        logger.info("stop stopService");
    }
 
   /**
    * Linuxで使用した場合commons Daemonからコールされる
    * 未実装
    */
    @Override
    public void destroy() {
        // Ingore
    }
 
   /**
    * Linuxで使用した場合commons Daemonからコールされる
    * 未実装
    */
    @Override
    public void init(DaemonContext arg0) throws DaemonInitException, Exception {
        // Ingore
    }
 
   /**
    * Linuxで使用した場合commons Daemonからコールされる
    * 未実装
    */
    @Override
    public void start() throws Exception {
        // Ingore
    }
 
   /**
    * Linuxで使用した場合commons Daemonからコールされる
    * 未実装
    */
    @Override
    public void stop() throws Exception {
        // Ingore
    }
 
   /**
    * メイン関数
    * Windowsサービス時には未使用
    * @param args
    */
    public static void main(String[] args) {
        logger.info("start main");
        launcher = new ServiceLauncher();
        launcher.run();
        logger.info("end main");
        return;
    }
}
  • Serviceクラス
package com.example;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * サービスクラス.
 *
 */
public class Service implements Runnable {
    // 実行中フラグ
    private boolean isRunning = false;
     private static final Logger logger = LogManager.getLogger(ServiceLauncher.class);

   /**
    * コンストラクタ.
    */
    public Service () {
    }
  
   /**
    * サービスを停止する。
    *
    */
    public void stop() {
        isRunning = false;
    }
 
   /**
    * スケジューラを開始する。
    *
    */
    @Override
    public void run() {
        isRunning = true;
        logger.info("start run");
 
        while (isRunning) {
            logger.info("isRunning");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        logger.info("end run");
    }
}

バッチ

  • windowsサービスに上記JARを登録するinstall.batと登録解除を行うuninstall.batがある。
  • 起動確認はwindowsサービスを起動してServiceLauncherを確認する。
set EXEC_DIR=%~dp0
echo %EXEC_DIR%
pushd %~dp0..
set PROJECT_ROOT=%CD%
popd
echo %PROJECT_ROOT%

set INSTALL_PATH=%EXEC_DIR%commons-daemon-1.3.4-bin-windows\ServiceLauncher.exe
rem set INSTALL_PATH=%EXEC_DIR%commons-daemon-1.3.4-bin-windows\prunsrv.exe
@REM set CLASSPATH="%PROJECT_ROOT%\target;%PROJECT_ROOT%\target\java_deamon_sample-jar-with-dependencies.jar"
set CLASSPATH="C:\java_deamon_sample-jar-with-dependencies.jar"
rem JVMのパスを指定する。オプションでauto指定もできるがうまくいかない
set JavaHome="C:\Program Files (x86)\Eclipse Adoptium\jdk-8.0.392.8-hotspot\jre"  
set JVM_PATH="C:\Program Files (x86)\Eclipse Adoptium\jdk-8.0.392.8-hotspot\jre\bin\server\jvm.dll"  
rem cd commons-daemon-1.3.4-bin-windows
commons-daemon-1.3.4-bin-windows\ServiceLauncher.exe //IS//ServiceLauncher --DisplayName=ServiceLauncher --Install=%INSTALL_PATH% --Classpath=%CLASSPATH% --Startup=auto --JavaHome=%JavaHome% --Jvm=%JVM_PATH% --StartMode=jvm --StopMode=jvm --StartClass=com.example.ServiceLauncher --StartMethod=startService --StopClass=com.example.ServiceLauncher --StopMethod=stopService --LogPath=%EXEC_DIR% --LogLevel=DEBUG --StdOutput=auto --StdError=auto
rem cd commons-daemon-1.3.4-bin-windows
rem ServiceLauncher.exe //IS//ServiceLauncher --DisplayName=ServiceLauncher --Install=%INSTALL_PATH% --Classpath=%CLASSPATH% --Startup=auto --JavaHome=%JavaHome% --Jvm=%JVM_PATH% --StartMode=jvm --StopMode=jvm --StartClass=com.example.ServiceLauncher --StartMethod=startService --StopClass=com.example.ServiceLauncher --StopMethod=stopService --LogPath=%EXEC_DIR% --LogLevel=DEBUG --StdOutput=auto --StdError=auto
REM ++JvmOptions=-Dlog4j.configurationFile=%PROJECT_ROOT%setting\log4j2.xml
pause
rem commons-daemon-1.3.4-bin-windows\ServiceLauncher.exe //DS//ServiceLauncher 
cd commons-daemon-1.3.4-bin-windows
ServiceLauncher.exe //DS//ServiceLauncher 
  
pause  

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA