293 lines
9.4 KiB
Java
293 lines
9.4 KiB
Java
/*
|
|
* 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙,邂逅框架梦]
|
|
*
|
|
* 知启蒙WEB容器(zhiqim_httpd)在LGPL3.0协议下开源:https://www.zhiqim.com/gitcan/zhiqim/zhiqim_httpd.htm
|
|
*
|
|
* This file is part of [zhiqim_httpd].
|
|
*
|
|
* [zhiqim_httpd] is free software: you can redistribute
|
|
* it and/or modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* [zhiqim_httpd] is distributed in the hope that it will
|
|
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with [zhiqim_httpd].
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.zhiqim.httpd;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import org.zhiqim.httpd.bio.HttpBioListener;
|
|
import org.zhiqim.httpd.bio.HttpBioSslListener;
|
|
import org.zhiqim.httpd.nio.HttpNioListener;
|
|
import org.zhiqim.httpd.nio.HttpNioSslListener;
|
|
import org.zhiqim.kernel.Global;
|
|
import org.zhiqim.kernel.Servicer;
|
|
import org.zhiqim.kernel.config.Group;
|
|
import org.zhiqim.kernel.extend.HashSetS;
|
|
import org.zhiqim.kernel.logging.Log;
|
|
import org.zhiqim.kernel.logging.LogFactory;
|
|
import org.zhiqim.kernel.util.Arrays;
|
|
import org.zhiqim.kernel.util.Asserts;
|
|
import org.zhiqim.kernel.util.Classes;
|
|
import org.zhiqim.kernel.util.Validates;
|
|
|
|
/**
|
|
* HTTP多例服务,负责启动和关闭: <br><br>
|
|
* 1、初始化AIO/BIO/NIO监听服务<br>
|
|
* 2、加载和配置上下文环境 <br>
|
|
*
|
|
* @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
|
|
*/
|
|
public class HttpServer extends Servicer implements HttpdConstants
|
|
{
|
|
private static final Log log = LogFactory.getLog(HttpServer.class);
|
|
|
|
private boolean isRunning; //是否运行
|
|
private HttpListener listener; //HTTP监听器
|
|
private final List<HttpContext> contextList; //HTTP上下文环境表
|
|
|
|
/*********************************************************************************/
|
|
//配置创建HTTP服务
|
|
/*********************************************************************************/
|
|
|
|
public HttpServer()
|
|
{
|
|
this.contextList = new ArrayList<>();
|
|
}
|
|
|
|
public boolean isRunning()
|
|
{
|
|
return isRunning;
|
|
}
|
|
|
|
@Override
|
|
public boolean create() throws Exception
|
|
{
|
|
//1.诊断参数
|
|
Group group = Global.getGroup(id);
|
|
Asserts.as(group != null?null:"HTTP服务[%s]未找到整个配置组", id);
|
|
log.info("初始化[HTTP服务:%s]开始...", id);
|
|
|
|
//2、初始化上下文环境
|
|
String contexts = group.getString(_SERVER_CONTEXT_);
|
|
Asserts.as(Validates.isNotEmptyBlank(contexts)?null:"HTTP服务["+id+"]未找到[context]配置项");
|
|
|
|
//3、初始化监听器
|
|
String scheme = group.getString(_LISTEN_SCHEME_, _HTTP_);
|
|
String io = group.getString(_LISTEN_IO_, _BIO_);
|
|
if (_HTTPS_.equalsIgnoreCase(scheme))
|
|
{
|
|
if (_NIO_.equalsIgnoreCase(io))
|
|
listener = new HttpNioSslListener(this);
|
|
else
|
|
listener = new HttpBioSslListener(this);
|
|
}
|
|
else
|
|
{
|
|
if (_NIO_.equalsIgnoreCase(io))
|
|
listener = new HttpNioListener(this);
|
|
else
|
|
listener = new HttpBioListener(this);
|
|
}
|
|
|
|
listener.setGroup(group);
|
|
listener.open();
|
|
|
|
//4、初始化上下文环境
|
|
String[] contextArr = Arrays.toStringArray(contexts);
|
|
for (String contextId : contextArr)
|
|
{
|
|
Group cGroup = Global.getGroup(contextId);
|
|
Asserts.as(cGroup != null?null:"HTTP服务["+id+"]未找到context["+contextId+"]配置项");
|
|
|
|
String contextClass = cGroup.getString(_CONTEXT_CLASS_, _HTTP_CONTEXT_CLASS_);
|
|
Object obj = Classes.newInstance(contextClass);
|
|
Asserts.as((obj instanceof HttpContext)?null:"HTTP服务["+id+"]context["+contextId+"]的[class]配置项未实现HttpContext接口");
|
|
|
|
HttpContext context = (HttpContext)obj;
|
|
context.setServer(this);
|
|
if (!context.create(cGroup))
|
|
return false;
|
|
|
|
contextList.add(context);
|
|
}
|
|
|
|
isRunning = true;
|
|
|
|
log.info("初始化[HTTP服务:%s]"+this.listener.toString()+"完成!!!%s", id, _BR_);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void destroy() throws Exception
|
|
{
|
|
isRunning = false;
|
|
if (listener != null)
|
|
{
|
|
listener.close();
|
|
listener = null;
|
|
}
|
|
|
|
for (HttpContext context : contextList)
|
|
{
|
|
if (context.isRunning())
|
|
context.destroy();
|
|
}
|
|
}
|
|
|
|
/***************************************************************************************************/
|
|
//手动创建服务 设置三个参数 domain & host & encoding 和 设置 listen & context 再setRunning()
|
|
/***************************************************************************************************/
|
|
|
|
public void setRunning()
|
|
{
|
|
this.isRunning = true;
|
|
}
|
|
|
|
public void addContext(HttpContext context)
|
|
{
|
|
//检查是否有相同的配置
|
|
context.setServer(this);
|
|
chkContextDomainPath(context.getContextDomains(), context.getContextPath());
|
|
|
|
//通过后放置到列表中
|
|
contextList.add(context);
|
|
}
|
|
|
|
public void removeContext(HttpContext context)
|
|
{
|
|
try
|
|
{
|
|
context.destroy();
|
|
contextList.remove(context);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.error("移除上下文环境时异常", e);
|
|
}
|
|
}
|
|
|
|
public void removeContext(String contextId)
|
|
{
|
|
HttpContext context = getContext(contextId);
|
|
if (context == null)
|
|
return;
|
|
|
|
removeContext(context);
|
|
}
|
|
|
|
/** 检查域名和路径是否存在相同的配置 */
|
|
public void chkContextDomainPath(HashSetS domains, String path)
|
|
{
|
|
if (domains.isEmpty())
|
|
{//未配置域名
|
|
for (HttpContext ctx : contextList)
|
|
{
|
|
if (!ctx.getContextDomains().isEmpty())
|
|
continue;
|
|
|
|
if (!ctx.getContextPath().equals(path))
|
|
continue;
|
|
|
|
throw Asserts.exception("存在相同的上下文环境配置[%s]", ctx.getContextPath());
|
|
}
|
|
}
|
|
else
|
|
{//有配置域名
|
|
for (HttpContext ctx : contextList)
|
|
{
|
|
HashSetS ds = ctx.getContextDomains();
|
|
if (ds.isEmpty())
|
|
continue;
|
|
|
|
if (!ctx.getContextPath().equals(path))
|
|
continue;
|
|
|
|
for (String d : domains.instance())
|
|
{
|
|
if (!ds.contains(d))
|
|
continue;
|
|
|
|
//只要有一个成立则表示有相同
|
|
throw Asserts.exception("存在相同的上下文环境配置[%s][%s]", d, ctx.getContextPath());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************/
|
|
//获取 & 配对 listen, context, scheme, port, domain, host, encoding
|
|
/*********************************************************************************/
|
|
|
|
public HttpListener getListener()
|
|
{
|
|
return listener;
|
|
}
|
|
|
|
public List<HttpContext> getContextList()
|
|
{
|
|
return contextList;
|
|
}
|
|
|
|
public HttpContext getContext(String contextId)
|
|
{
|
|
Asserts.as(contextId != null?null:"上下文环境路径不能为空");
|
|
|
|
for (HttpContext context : contextList)
|
|
{
|
|
if (contextId.equals(context.getId()))
|
|
return context;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public HttpContext getContext(String contextDomain, String contextPath)
|
|
{
|
|
Asserts.as(contextPath != null?null:"上下文环境路径不能为空");
|
|
|
|
if (!Validates.isEmptyBlank(contextDomain))
|
|
{//1.域名不为空,优先找匹配上的
|
|
for (HttpContext context : contextList)
|
|
{
|
|
HashSetS domains = context.getContextDomains();
|
|
if (domains.isEmpty())
|
|
continue;
|
|
|
|
if (domains.contains(contextDomain) && contextPath.equals(context.getContextPath()))
|
|
return context;
|
|
}
|
|
}
|
|
|
|
//2.再找缺省未配置域名的,只比较上下文环境路径
|
|
for (HttpContext context : contextList)
|
|
{
|
|
if (!context.getContextDomains().isEmpty())
|
|
continue;
|
|
|
|
if (contextPath.equals(context.getContextPath()))
|
|
return context;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public int getPort()
|
|
{
|
|
return listener.getPort();
|
|
}
|
|
|
|
public String getScheme()
|
|
{
|
|
return listener.getScheme();
|
|
}
|
|
}
|