Skip to content

Instantly share code, notes, and snippets.

Created January 11, 2011 14:20
Show Gist options
  • Save anonymous/774459 to your computer and use it in GitHub Desktop.
Save anonymous/774459 to your computer and use it in GitHub Desktop.
Restartable DevAppServer (Google App Engine for Java)
package natntech;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import com.google.appengine.tools.development.DevAppServerMain;
/**
* DevAppServerのリスタートを可能にするラッパークラス
*
* <pre>
* license : Apache License 2.0
* url : http://natn-tech-lab.appspot.com/?entry=2011/011
* screenshots : http://picasaweb.google.com/106446354638413583283/RestartableDevAppServerMain
* </pre>
*
* @author natn_tech
* @version 1.0
*/
public class RestartableDevAppServerMain implements Runnable {
/** 終了要求命令 */
public static final int OPERATION_TERMINATE = 254;
// arguments
private String[] args;
/**
* コンストラクタ
*
* @param args dev appserver arguments
*/
public RestartableDevAppServerMain(String[] args) {
this.args = args;
}
/**
* メイン
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// ポート番号
int interruptSocketPortnum = createPortnumFromArgs(args);
// ポート番号でのListenができたかどうか
boolean listenSucceed = false;
// サーバ停止割り込みを待ち受けるサーバソケット
InetSocketAddress interruptSocketAddress = new InetSocketAddress(interruptSocketPortnum);
ServerSocket interruptSocket = null;
// 起動試行ループ
for (int i = 0; i < 3; i++) {
try {
// ポート番号でのListenを試みる
// TODO ServerSocketインスタンスを毎回作り直さないとbindに失敗する。何故?
interruptSocket = new ServerSocket();
interruptSocket.bind(interruptSocketAddress);
// Listen成功
listenSucceed = true;
// ループを抜ける
break;
}
// Listenできなかった場合
catch (Exception e) {
// 他のサーバが起動している
System.out.print("dev appserver already started, try to stop it..");
// interrupuSocketAddressに接続しにいく = 停止要求割り込み
boolean stopped = false;
try {
Socket socket = new Socket();
socket.connect(interruptSocketAddress);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(OPERATION_TERMINATE);
// サーバの終了を待つ
int wait = 0;
while (wait++ < 10) {
System.out.print(".");
// 少し待ってから、書き込みを試みる
try {
Thread.sleep(300);
outputStream.write(1);
}
// 失敗 = ソケットクローズ = 終了とみなす
catch (Exception e1) {
stopped = true;
break;
}
}
}
catch (Exception e1) {
// 何もしない
}
System.out.println(stopped ? "stopped" : "failed");
}
}
// Listenできなかった場合
if (listenSucceed == false) {
System.err.print("fail to start dev appserver!");
}
// Listenできた = サーバを起動して良い
else {
// 何度でもリロードできるように無限ループ
while (true) {
// サーバ起動
System.out.println("starting dev appserver");
Thread appSeverThread = new Thread(new RestartableDevAppServerMain(args));
appSeverThread.start();
// ここでソケットへの接続を待つ
System.out.println("waiting interrupt, portnum=" + interruptSocketPortnum);
Socket acceptedSocket = interruptSocket.accept();
// TODO プロセスが殺された場合、ListenソケットはOSが回収してくれるよう。後始末しないなんて、そんな実装で大丈夫か?
// 要求の取得
int operation = acceptedSocket.getInputStream().read();
try {
// appSeverThreadに対してinterruptすると、ラッキーなことに終わってくれる
System.out.print("stopping dev appserver...");
appSeverThread.interrupt();
System.out.println("stopped");
}
catch (Exception e) {
e.printStackTrace();
}
finally {
// 終了要求
if (operation == OPERATION_TERMINATE) {
// ちょっと待つ
Thread.sleep(300);
// 多分終了できたので、acceptedSocketを閉じる
try {
acceptedSocket.close();
}
catch (Exception e) {
// なにもしない
}
System.out.println("acceptedSocket closed.");
// ここでおしまい
return;
}
// 他はリスタート要求とみなす
// TODO としたいが、DevAppServerMainはinterruptされると内部でSystem.exitを呼ぶらしく、うまくいかない。つかえねぇ。
else {
System.out.println("restarting...");
// ちょっと待つ
Thread.sleep(1000);
}
}
}
}
}
/**
* DevAppServerMainを開始するだけのスレッドメイン。
*/
public void run() {
try {
DevAppServerMain.main(args);
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* プログラム引数からポート番号を作成
*
* @param args
* @return
*/
protected static int createPortnumFromArgs(String[] args) {
// パラメータのポート番号を取得
int paramPortnum = -1;
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (arg.startsWith("--port=")) {
paramPortnum = Integer.parseInt(arg.substring("--port=".length()));
break;
}
}
// このアルゴリズム・・・特に意味はない><
// paramPortnumで一意に決まって、かつwellknownでなくて、かつ2^16を超えなければ何でもいいじゃん?
return 13000 + (paramPortnum + 11231) % 52000;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment