サービス・アプリケーション
サービス ・ アプリケーションはユーザーインターフェースを持たない常駐型のアプリケーションです。ユーザーがログオンしていなくてもコンピューターの起動時に自動的にプロセスを開始することができます。Windows サービスや NT サービスと呼ばれることもあります。
Web サーバーはサービス ・ アプリケーションの代表例の 1 つです。
オプション -s
を指定するとサービス ・ アプリケーションになります。
サービス・アプリケーションの特徴
ロガー API を通してイベントログへの書き込みができます。
標準出力と標準エラー出力がファイルに書き出されます。
System.out.println()
やe.printStackTrace()
などの出力を確認することができます。拡張フラグNOLOG
を指定するとこのファイル出力機能を無効にすることができます。スレッドが異常終了した場合もアプリケーション ・ プロセスを終了させます。スタックトレースはイベントログに出力されます。マルチスレッド ・ アプリケーションで一部のスレッドが異常終了したまま動作が続行されてしまう危険性がなくなります。拡張フラグ
IGNORE_UNCAUGHT_EXCEPTION
を指定するとこの機能を無効にすることができます。
サービス ・ アプリケーションでは main
メソッドは必要ありません。代わりにメインクラスには以下のシグネチャを持つ start
メソッドと stop
メソッドを定義してください。
public static void start(String[] args)
public static void stop()
これらのメソッドは、 Windows のサービス ・ コントロール ・ マネージャー (SCM) によって呼び出されます。start
メソッドはサービスを開始したときに、 stop
メソッドはサービスを停止したときに呼び出されます。
一般的に start
メソッドはサービス機能を提供している間は終了しないようにループで実装します。stop
メソッドが呼ばれたときに start
メソッドのループを抜けて start
メソッドが終了するようにしてください。
stop
メソッドが呼び出されても start
メソッドが終了しない実装になっていると、 Windows のサービス ・ コントロール ・ マネージャー (SCM) からサービスの停止操作をおこなっても反応しません。
簡単なプログラムを作ってみよう
固定メッセージを返す単純な Web サーバーを作って EXE にしてみましょう。
以下のソースコードを ServiceSample1.java
というファイル名で作成します。
ServiceSample1.javaimport java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicBoolean;
public class ServiceSample1 {
private static AtomicBoolean isStopRequested = new AtomicBoolean(false);
private static ServerSocket serverSocket;
public static void start(String[] args) throws IOException {
serverSocket = new ServerSocket(80, 50, InetAddress.getLoopbackAddress());
for(;;) {
Socket socket;
try {
socket = serverSocket.accept();
} catch(SocketException e) {
if(isStopRequested.get()) {
return;
}
throw e;
}
new Worker(socket).start();
}
}
public static void stop() throws IOException {
isStopRequested.set(true);
if(serverSocket != null) {
serverSocket.close();
}
}
private static class Worker extends Thread {
private Socket socket;
private BufferedReader reader;
private BufferedWriter writer;
public Worker(Socket socket) throws IOException {
this.socket = socket;
this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
}
@Override
public void run() {
try {
for(;;) {
String line = reader.readLine();
if(line == null || line.length() == 0) {
break;
}
}
writer.write("HTTP/1.1 200 OK¥r¥n");
writer.write("Content-Type: text/html; charset=utf-8¥r¥n");
writer.write("¥r¥n");
writer.write("Hello, World!! (");
writer.write(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
writer.write(")¥r¥n");
writer.close();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
}
コマンドプロンプトを起動します。Java コンパイラ (javac) と exewrap が使えるように PATH 環境変数を設定しておいてください。
javac
コマンドでソースコード ServiceSample1.java
をコンパイルすると ServiceSample1.class
と ServiceSample1$Worker.class
ファイルが出来上がります。
コマンドプロンプトC:¥>javac ServiceSample1.java
出力された ServiceSample1.class
と ServiceSample1$Worker.class
ファイルを Java アーカイブ形式 (jar) にまとめます。(対象となるクラスファイルが 2 つあるのでワイルドカードを使って *.class
と指定しています。)
コマンドプロンプトC:¥>jar cf ServiceSample1.jar *.class
最後に exewrap
で ServiceSample1.jar
を実行ファイル (EXE) にします。
このとき、 サービス ・ アプリケーションにするために -s
オプションを指定します。-M
オプションで start
メソッドと stop
メソッドを定義したメインクラスも指定します。(-M
オプションを使わずにメインクラスを指定したマニフェストファイルを jar ファイルに含めておいても構いません。)
コマンドプロンプトC:¥>exewrap -s -M ServiceSample1 ServiceSample1.jar exewrap 1.6.0 for x64 (64-bit) Architecture: x64 (64-bit) Target: Java 5.0 (1.5.0.0) SwingSample1.exe (64-bit) version 0.0.0.0
実行ファイルが出来上がりました。
実行ファイルには製品名や著作権表示、 バージョンなど様々なプロパティ情報を指定することができます。詳細については 「基本オプション」 を参照してください。
コンソールから実行してみる
サービス ・ アプリケーションは通常プロセスとしてコンソールから実行することもできます。まずはインストールせずにコンソールから実行してみましょう。
コマンドプロンプトC:¥>ServiceSample1.exe
プロンプトに復帰せずにキャレットが点滅した状態になれば成功です。プロセスが起動して要求を受け付けられる状態になっています。
この状態でブラウザーを起動して http://localhost/
にアクセスしてみます。
Hello, World!!のメッセージと現在時刻が表示されれば OK です。上手く行きましたね!
サービス ・ アプリケーションをコンソールから起動した場合は Ctrl + C でプロセスを停止できます。
コマンドプロンプトC:¥>ServiceSample1.exe CTRL_C: サービスを停止中です... C:¥>
start
メソッドを実行していたスレッド以外に非デーモンスレッドが存在する場合、 すべての非デーモンスレッドが終了するまでプロセスは終了しません。
さらに Ctrl + C を押すと非デーモンスレッドが残っていても強制的にプロセスを終了します。
コマンドプロンプトC:¥>ServiceSample1.exe CTRL_C: サービスを停止中です... CTRL_C: 強制終了します... C:¥>
サービス ・ アプリケーションをコンソールから起動すると、 標準出力や標準エラー出力の内容や例外発生時のスタックトレースを確認しやすいので開発 ・ デバッグ時にはコンソールからの起動が有効です。
サービスのインストール
作成したサービス ・ アプリケーションに、 引数 -install
を指定して実行するとサービスとして登録されます。ServiceSample1.exe
をサービスとして登録する場合は以下のようにします。(コマンドプロンプトを管理者権限で実行していない場合は UAC 権限昇格の確認画面が表示されます。)
コマンドプロンプトC:¥>ServiceSample1.exe -install ServiceSample1 サービスをインストールしました。
サービス実行時の引数を指定する場合は、 -install
の後に続けて記述してください。サービス実行時の引数は、 start
メソッドの引数 String[] args
として渡されます。
コマンドプロンプトC:¥>ServiceSample1.exe -install foo bar
サービスのインストール時に、 サービスの構成を指定することもできます。サービス構成オプションは -install
よりも前に記述します。たとえば、 サービスの実行アカウントを指定する場合は次のように指定します。詳細は 「サービス構成オプション」 を確認してください。
コマンドプロンプトC:¥>ServiceSample1.exe -u Administrator -p mypassword -install
サービスの開始
コントロール パネル → 管理ツール → サービス から対象のサービスを選択して 開始 します。
サービスが正しく開始されると 「実行中」 と表示されます。
net start
コマンドでを使用してサービスを開始することもできます。net start
コマンドはサービス開始要求を送信した後、 サービスが開始されるまで一定時間待機してくれます。(同期)
コマンドプロンプトC:¥>net start ServiceSample1 ServiceSample1 サービスを開始します. ServiceSample1 サービスは正常に開始されました。 C:¥>
sc start
コマンドを使用してサービスを開始することもできます。sc start
コマンドはサービス開始要求を送信した後、 すぐにプロンプトに復帰します。(非同期)
コマンドプロンプトC:¥>sc start ServiceSample1 SERVICE_NAME: ServiceSample1 TYPE : 10 WIN32_OWN_PROCESS STATE : 2 START_PENDING (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x7d0 PID : 96 FLAGS : C:¥>
サービス開始要求を送信した直後は状態が START_PENDING
となっておりサービスが正しく開始できたかどうか分かりません。
少し待ってから sc query
コマンドで状態を確認してみてください。状態が RUNNING
になっていればサービスは動作しています。
コマンドプロンプトC:¥>sc query ServiceSample1 SERVICE_NAME: ServiceSample1 TYPE : 10 WIN32_OWN_PROCESS STATE : 4 RUNNING (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x0 C:¥>
いずれかの方法でサービスを開始してからブラウザーで http://localhost/
にアクセスしてみます。
サービスとしての動作も問題なしですね!
サービスの停止
コントロール パネル → 管理ツール → サービス から対象のサービスを選択して 停止 します。
サービスが正しく開始されると 「停止」 と表示されます。
net stop
コマンドを使用してサービスを停止することもできます。net stop
コマンドはサービス停止要求を送信した後、 サービスが停止するまで一定時間待機してくれます。(同期)
コマンドプロンプトC:¥>net stop ServiceSample1 ServiceSample1 サービスを停止中です. ServiceSample1 サービスは正常に停止されました。 C:¥>
sc start
コマンドを使用してサービスを停止することもできます。sc start
コマンドはサービス停止要求を送信した後、 すぐにプロンプトに復帰します。
コマンドプロンプトC:¥>sc stop ServiceSample1 SERVICE_NAME: ServiceSample1 TYPE : 10 WIN32_OWN_PROCESS STATE : 3 STOP_PENDING (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x7d0 C:¥>
サービス停止要求を送信した直後は状態が STOP_PENDING
となっておりサービスが正しく停止できたかどうか分かりません。
少し待ってから sc query
コマンドで状態を確認してみてください。状態が STOPPED
になっていればサービスは停止しています。
コマンドプロンプトC:¥>sc query ServiceSample1 SERVICE_NAME: ServiceSample1 TYPE : 10 WIN32_OWN_PROCESS STATE : 1 STOPPED WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x0 C:¥>
サービスのアンインストール
サービスのアンインストールは、 サービスが停止している状態でおこなってください。また、 イベントビューアが開いている状態では、 正しくサービスをアンインストールすることができないので注意してください。
コマンドプロンプトC:¥>ServiceSample1.exe -remove ServiceSample1 サービスを削除しました。
サービスをアンインストール前にサービスの停止を要求することもできます。詳細は 「サービス構成オプション」 を確認してください。
exewrap の配布物に含まれている samples
フォルダーには他にもサンプルコードがあります。ぜひ試してみてください。