プログラマメモ2 - programmer no memo2

[java]サーバープログラムをソケット使用して停止させる。 2006/07/13
2006/11/26

サーバプログラム(デーモン)を作成する際に、どうやって停止させようかと悩んだ。



ソケットを通して停止させるパターンのサンプルを書いてみた。



もとネタは、軽量HTTPサーバのJettyです。



jettyのorg.mortbay.startパッケージ、org.mortbay.stopパッケージを参考にしています。



まずmonitorを作成します。monitorはソケットを通して命令を受付て、System.exitを行います。



package scoketdecontrol.start;



import java.io.InputStreamReader;

import java.io.LineNumberReader;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;



public class Monitor extends Thread {



static public String STOP_KEY = "stop.key";

static public int STOP_PORT = 8079;



ServerSocket serverSocket;



private Monitor() {



try {



// daemon thread

setDaemon(true);

serverSocket = new ServerSocket(STOP_PORT, 1, InetAddress

.getByName("127.0.0.1"));

if (STOP_PORT == 0) {

STOP_PORT = serverSocket.getLocalPort();

System.out.println(STOP_PORT);

}



} catch (Exception e) {



e.printStackTrace();



}



if (serverSocket != null) {

this.start();

} else {

System.err.println("WARN: Not listening on monitor port: "

+ STOP_PORT);

}

}



public void run() {

while (true) {

Socket socket = null;

try {

socket = serverSocket.accept();



LineNumberReader lin = new LineNumberReader(

new InputStreamReader(socket.getInputStream()));

String key = lin.readLine();



if (!eq(key, STOP_KEY))

continue;



String cmd = lin.readLine();



System.err.println("received command=[" + cmd + "]");



if (eq(cmd, "stop")) {

try {

socket.close();

} catch (Exception e) {

e.printStackTrace();

}

try {

serverSocket.close();

} catch (Exception e) {

e.printStackTrace();

}

System.exit(0);

} else if (eq(cmd, "status")) {

socket.getOutputStream().write("OK\r\n".getBytes());

socket.getOutputStream().flush();

}

} catch (Exception e) {

e.printStackTrace();

} finally {

if (socket != null) {

try {

socket.close();

} catch (Exception e) {

}

}

socket = null;

}

}

}



protected static boolean eq(Object object, Object object2) {

return object == null ? object2 == null : object.equals(object2);

}



public static void monitor() {

new Monitor();

}



public static void main(String[] args) {

Monitor.monitor();

}

}







つぎにmonitorを利用するメインのスレッドが動いているアプリケーションのサンプルです。

GUIをもつFrameにしてみました。



mainでmonitorを起動しています。



package scoketdecontrol.start;



import javax.swing.SwingUtilities;

import java.awt.BorderLayout;

import javax.swing.JPanel;

import javax.swing.JFrame;



public class TestMainForMonitor extends JFrame {



private static final long serialVersionUID = 1L;

private JPanel jContentPane = null;



public static void main(String[] args) {



Monitor.monitor();



SwingUtilities.invokeLater(new Runnable() {

public void run() {

TestMainForMonitor thisClass = new TestMainForMonitor();

thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

thisClass.setVisible(true);

}

});

}





public TestMainForMonitor() {

super();

initialize();

}



private void initialize() {

this.setSize(300, 200);

this.setContentPane(getJContentPane());

this.setTitle("JFrame");

}



private JPanel getJContentPane() {

if (jContentPane == null) {

jContentPane = new JPanel();

jContentPane.setLayout(new BorderLayout());

}

return jContentPane;

}



}







つぎに、停止命令を発行する別アプリケーションです。





package scoketdecontrol.stop;



import java.io.OutputStream;

import java.net.InetAddress;

import java.net.Socket;



import scoketdecontrol.start.Monitor;



public class Main {



int port = Monitor.STOP_PORT;;

String key = Monitor.STOP_KEY;



public static void main(String[] args) {

new Main().stop();

}



void stop() {

final String STOP_COMMAND = "stop";

try {

Socket s = new Socket(InetAddress.getByName("127.0.0.1"), port);

OutputStream out = s.getOutputStream();

out.write((key + "\r\n" + STOP_COMMAND + "\r\n").getBytes());

out.flush();

s.shutdownOutput();

s.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}







TestMainForMonitorを起動して、stopを起動するとFrameが終了します。



上記のサンプルでは、使用するポートを固定し、識別のためのkeyを固定にしてますが、これを可変にした場合、どこにその値をもたせるか悩みます。



jetty-5.1.1のソースでは、Syste.properyを使用していましたが、いまいちきめてにかけるような気がします。

: