package com.blogger.tcuri.appserver;
import java.awt.Desktop;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import com.blogger.tcuri.appserver.config.AbstractAppConfig;
import com.blogger.tcuri.appserver.interceptor.Interceptor;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
/**
* アプリケーションサーバの主体となるクラスです
*
* @author tomofumi
*/
public class AppServer {
/**
* 空きポートを探す最大回数
*/
protected static final int SERACH_PORT_TIMES = 10;
/**
* Interceptorのインスタンスのリスト
*/
protected static final List<Interceptor> interceptorList = new ArrayList<Interceptor>();
/**
* AbstractAppConfigのオブエジェクト
*/
protected static AbstractAppConfig appConfig;
/**
* Frameworkクラスのインスタンス
*/
protected static Framework framework;
/**
* Loggerクラスのインスタンス
*/
protected static Logger logger = Logger
.getLogger(Logger.GLOBAL_LOGGER_NAME);
/**
* アプリケーションサーバを起動する
*
* @throws Exception
*/
public static void startup() throws Exception {
framework = new Framework();
appConfig = framework.appConfig;
long startTime = new Date().getTime();
HttpServer server = null;
Integer port = appConfig.getPort();
if (port == null) {
//
// port auto の場合
//
port = 8081;
for (int i = 0; i < SERACH_PORT_TIMES; i++) { // 空きポートを探す回数
port += i;
try {
server = HttpServer.create(new InetSocketAddress(
"localhost", port), 0);
} catch (BindException e) {
continue;
}
break;
}
} else {
server = HttpServer.create(
new InetSocketAddress("localhost", port), 0);
}
server.setExecutor(Executors.newCachedThreadPool());
server.createContext("/", new HttpHandler() {
public void handle(HttpExchange he) throws IOException {
ActionContext context = new ActionContextImpl(he);
String path = he.getRequestURI().getPath();
// 拡張子なしをデフォルトでActionとして扱う。
if (path.indexOf('.') == -1) {
framework.processDynamicResource(context);
} else {
processStaticResource(context);
}
ActionContext.remove();
}
});
server.start();
logger.info("port: " + port);
String browserInitPath = appConfig.getBrowserInitPath();
if (browserInitPath != null) {
String url = "http://localhost:" + port + browserInitPath;
Desktop.getDesktop().browse(new URI(url));
logger.info("browser init path: " + url);
}
logger.info(String.format("Server startup in %d ms",
new Date().getTime() - startTime));
}
/**
* 静的リソースをHTTPレスポンスする
*
* @param context
* {@link ActionContext} クラスのインスタンス
* @throws IOException
*/
private static void processStaticResource(ActionContext context)
throws IOException {
String path = context.getRequestPath();
URL url = AppServer.class.getResource("/documentRoot" + path);
File f = new File(url.getFile());
if (!f.exists()) {
// ファイルがない
context.sendResponseHeaders(404, 0);
context.sendMessage("not found.");
return;
}
if (f.isDirectory()) {
// TODO 通らない。。
File indexFile = new File(f, "index.html");
if (!indexFile.exists()) {
// ディレクトリは見れない
context.sendResponseHeaders(403, 0);
context.sendMessage("fobbiden.");
return;
}
f = indexFile;
}
int idx = path.lastIndexOf(".");
if (idx >= 0) {
// 拡張子からmimeタイプを出力
String ext = path.substring(idx + 1);
String mimetype = appConfig.getMimetype(ext);
if (mimetype != null) {
context.addResponseHeader("Content-Type", mimetype);
}
}
context.sendResponseHeaders(200, f.length());
// ファイル内容を返す
InputStream is = new FileInputStream(f);
OutputStream res = context.getResponseBody();
try {
byte[] buf = new byte[1024];
int len;
while ((len = is.read(buf)) >= 0) {
res.write(buf, 0, len);
}
} finally {
res.close();
is.close();
}
}
/**
* メインメソッドです
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
startup();
}
}