Created
January 11, 2011 14:20
-
-
Save anonymous/774459 to your computer and use it in GitHub Desktop.
Restartable DevAppServer (Google App Engine for Java)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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