com.devops4j.embedded.EmbeddedHttpServer Maven / Gradle / Ivy
The newest version!
package com.devops4j.embedded;
import com.devops4j.embedded.handler.DelegateHttpHandler;
import com.devops4j.embedded.handler.StaticHttpHandler;
import com.devops4j.embedded.servlet.EmbeddedServletConfig;
import com.devops4j.embedded.httpserver.HttpServer;
import com.devops4j.embedded.httpserver.HttpsConfigurator;
import com.devops4j.embedded.httpserver.HttpsServer;
import com.devops4j.logtrace4j.ErrorContextFactory;
import com.devops4j.message.MessageFormatter;
import com.devops4j.reflection4j.GlobalSystemMetadata;
import com.devops4j.reflection4j.MetaClass;
import com.devops4j.reflection4j.resource.ClassScanner;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
public class EmbeddedHttpServer {
/**
* 服务器
*/
HttpServer httpServer;
HttpsServer httpsServer;
/**
* 线程池
*/
ExecutorService threadPool;
/**
* 定时器
*/
Timer timer;
final Properties configs = new Properties();
private Logger logger;
final Map servlets = new HashMap();
final Map> urlPatterns = new HashMap();
static EmbeddedHttpServer INSTANCE = new EmbeddedHttpServer();
public EmbeddedHttpServer() {
this.logger = Logger.getLogger(this.getClass().getName());
}
EmbeddedHttpServer registerServlet(String name, Set urlPatterns, final HttpServlet servlet) {
if (servlet == null) {
throw new NullPointerException("servlet '" + name + "' is null");
}
if (this.servlets.containsKey(name)) {
throw ErrorContextFactory.instance().message("exists servlet name '{}'", name).runtimeException();
}
this.urlPatterns.put(name, urlPatterns);
for (String urlPattern : urlPatterns) {
this.servlets.put(urlPattern, servlet);
}
return this;
}
/**
* 注册Servlet
*
* @param name Servlet名字
* @param urlPatterns Servlet
* @param servlet Servlet
* @return 服务器
*/
EmbeddedHttpServer registerServlet(String name, String[] urlPatterns, final HttpServlet servlet) {
Set urlPatterns_0 = new LinkedHashSet();
for (String urlPattern : urlPatterns) {
urlPatterns_0.add(urlPattern);
}
return registerServlet(name, urlPatterns_0, servlet);
}
public static EmbeddedHttpServer getInstance() {
return INSTANCE;
}
void _destroy() {
httpServer = null;
httpsServer = null;
threadPool = null;
}
public static void destroy() {
if (INSTANCE != null) {
INSTANCE._destroy();
INSTANCE = null;
}
}
public void start() throws IOException {
start(Thread.currentThread().getContextClassLoader());
}
void initConfig() {
configs.clear();
Properties systemProp = System.getProperties();
String serverFile = systemProp.getProperty("server.properties");
InputStream is = null;
try {
if(serverFile != null){
File file = new File(serverFile);
is = new FileInputStream(file);
}else{
URL url = this.getClass().getClassLoader().getResource("server.properties");
if (url == null) {
error("not found httpServer.properties");
return;
}
is = url.openStream();
}
configs.load(is);
} catch (IOException e) {
if (is != null) {
try {
is.close();
} catch (IOException e1) {
//nothing
}
}
}
for (Object key : configs.keySet()) {
error("config key {}:'{}'", key, configs.get(key));
}
}
void initServlet() {
//扫描包含Servlet的包路径,多个用英文分号分割
String scanPackages_ = configs.getProperty("server.servlet.packages", "com.devops4j.embedded");
info("scan package'{}'", scanPackages_);
String[] scanPackages = scanPackages_.split(";");
//是否扫描子包
String scanSubPackage_ = configs.getProperty("server.servlet.scan.subpackage", "true");
info("scan subpackage '{}'", scanSubPackage_);
ClassScanner classScanner = new ClassScanner(this.getClass().getClassLoader(), Boolean.valueOf(scanSubPackage_));
for (String scanPackage : scanPackages) {
classScanner.scan(scanPackage, new ClassScanner.AnnotatedWithFilter(WebServlet.class));
}
for (Class servletClass : classScanner.getClasses()) {
WebServlet webServletAnn = (WebServlet) servletClass.getAnnotation(WebServlet.class);
MetaClass metaClass = GlobalSystemMetadata.forClass(servletClass);
HttpServlet servlet = metaClass.newInstance();
EmbeddedServletConfig config = new EmbeddedServletConfig();
//首次启动初始化Servlet
try {
servlet.init(config);
} catch (ServletException e) {
throw new RuntimeException("初始化Servlet发生失败", e);
}
Set urlPatterns_0 = new LinkedHashSet();
for (String urlPattern : webServletAnn.value()) {
urlPatterns_0.add(urlPattern);
}
for (String urlPattern : webServletAnn.urlPatterns()) {
urlPatterns_0.add(urlPattern);
}
registerServlet(webServletAnn.name(), urlPatterns_0, servlet);
}
}
public void start(ClassLoader classLoader) throws IOException {
if (httpServer != null || httpsServer != null) {
stop();
_destroy();
}
initConfig();
if (this.timer == null) {
int autoShutdownSeconds = Integer.valueOf(configs.getProperty("server.shutdown.auto.seconds", "-1"));
if (autoShutdownSeconds > 0) {
info("auto shutdown timer '{}'", autoShutdownSeconds);
this.timer = new Timer("auto shutdown timer", true);
//自动关闭
this.timer.schedule(new TimerTask() {
@Override
public void run() {
info("auto shutdown");
EmbeddedHttpServer.getInstance().stop();
System.exit(0);
}
}, autoShutdownSeconds * 1000);
}
}
initServlet();
this.threadPool = Executors.newCachedThreadPool();
String enableHttp = configs.getProperty("server.http.enable", "true");
String enableHttps = configs.getProperty("server.https.enable", "false");
if(Boolean.valueOf(enableHttp)){
startHttp();
}
if(Boolean.valueOf(enableHttps)) {
startHttps();
}
//执行关机钩子,销毁Servlet
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Execute Hook.....");
for (String name : servlets.keySet()) {
Servlet servlet = servlets.get(name);
servlet.destroy();
}
}
}));
}
public void stop() {
if (this.httpServer != null || this.httpsServer != null) {
System.out.println("Server just now shutdown!");
this.threadPool.shutdown();
if (this.timer != null) {
this.timer.cancel();
}
this.timer = null;
if (this.httpServer != null) {
//3秒后关闭
this.httpServer.stop(3);
}
if (this.httpsServer != null) {
//3秒后关闭
this.httpsServer.stop(3);
}
} else {
return;
}
}
void startHttp() throws IOException {
int port = Integer.valueOf(configs.getProperty("server.http.port","80"));
this.httpServer = HttpServer.create(new InetSocketAddress(port), 0);
this.httpServer.setExecutor(this.threadPool);
boolean forceHttps = Boolean.valueOf(configs.getProperty("server.force.https","false"));
String webapp = configs.getProperty("server.webapp.dir", "webapp");
final DelegateHttpHandler delegateHttpHandler = new DelegateHttpHandler(this.getClass().getClassLoader(), threadPool, servlets, forceHttps);
this.httpServer.createContext("/", new StaticHttpHandler(this.getClass().getClassLoader(), threadPool, forceHttps, webapp));
for (String urlPattern : servlets.keySet()) {
info("context '{}'", urlPattern);
this.httpServer.createContext(urlPattern, delegateHttpHandler);
}
this.httpServer.start();
System.out.println("Server is listening on port " + port);
}
void startHttps() throws IOException {
int port = Integer.valueOf(configs.getProperty("server.https.port","443"));
this.httpsServer = HttpsServer.create(new InetSocketAddress(port), 0);
this.httpsServer.setExecutor(this.threadPool);
SSLContext sslContext = null;
ErrorContextFactory.instance().activity("启动HTTPS服务");
try {
sslContext = loadSslFile(configs.getProperty("server.https.ssl.cert.file", "ssl.cert"), configs.getProperty("server.https.ssl.cert.password",""));
} catch (Exception e) {
throw ErrorContextFactory.instance().message("加载证书发生错误").cause(e).runtimeException();
}finally {
ErrorContextFactory.instance().reset();
}
HttpsConfigurator conf = new HttpsConfigurator(sslContext);
this.httpsServer.setHttpsConfigurator(conf);
boolean forceHttps = Boolean.valueOf(configs.getProperty("server.force.https","false"));
String webapp = configs.getProperty("server.webapp.dir", "webapp");
final DelegateHttpHandler delegateHttpHandler = new DelegateHttpHandler(this.getClass().getClassLoader(), threadPool, servlets, forceHttps);
this.httpsServer.createContext("/", new StaticHttpHandler(this.getClass().getClassLoader(), threadPool, forceHttps, webapp));
for (String urlPattern : servlets.keySet()) {
info("context '{}'", urlPattern);
this.httpsServer.createContext(urlPattern, delegateHttpHandler);
}
this.httpsServer.start();
System.out.println("Server is listening on port " + port);
}
/**
* 记载SSL整数
* @param httpsCertFile 证书文件classpath路径
* @param password 密码
* @return
* @throws KeyStoreException
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws UnrecoverableKeyException
* @throws KeyManagementException
*/
SSLContext loadSslFile(String httpsCertFile, String password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException, URISyntaxException {
KeyStore ks = KeyStore.getInstance("JKS");
URL certURL = getClass().getClassLoader().getResource(httpsCertFile);
if (certURL == null) {
throw ErrorContextFactory.instance().message("https SSL cert '{}' not found", httpsCertFile).runtimeException();
}
InputStream is = null;
try {
is = certURL.openStream();
ks.load(is, password.toCharArray());
} finally {
if (is != null) {
is.close();
}
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password.toCharArray());
SSLContext sslContext = SSLContext.getInstance("SSLv3");
sslContext.init(kmf.getKeyManagers(), null, null);
return sslContext;
}
public void error(String format, Object... args) {
this.logger.log(Level.WARNING, MessageFormatter.format(format, args));
}
public void debug(String format, Object... args) {
this.logger.log(Level.FINER, MessageFormatter.format(format, args));
}
public void info(String format, Object... args) {
this.logger.log(Level.INFO, MessageFormatter.format(format, args));
}
}