[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を使用していましたが、いまいちきめてにかけるような気がします。
ソケットを通して停止させるパターンのサンプルを書いてみた。
もとネタは、軽量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を使用していましたが、いまいちきめてにかけるような気がします。
: