Zhiqim Httpd即知启蒙WEB容器,是Zhiqim Framework面向WEB开发的多例服务,提供更简洁配置、积木式组件模块和天然的模型模板设计。
HttpServer.java9KB
/*
* 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙,邂逅框架梦]
*
* https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
*
* Zhiqim Httpd is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.zhiqim.httpd;
import java.util.ArrayList;
import java.util.HashSet;
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.Z;
import org.zhiqim.kernel.config.Group;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.service.Servicer;
import org.zhiqim.kernel.util.Arrays;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Classes;
import org.zhiqim.kernel.util.Lists;
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 volatile 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 = Z.conf().group(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);
if (!listener.open())
{
return false;
}
//4、初始化上下文环境
String[] contextArr = Arrays.toStringArray(contexts);
for (String contextId : contextArr)
{
Group cGroup = Z.conf().group(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;
addContext(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;
}
synchronized (contextList)
{//删除全部
for (HttpContext context : contextList)
{
if (context.isRunning())
context.destroy();
}
contextList.clear();
}
}
/***************************************************************************************************/
//手动创建服务 设置三个参数 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());
//通过后放置到列表中
synchronized (contextList)
{
contextList.add(context);
}
}
/** 移除上下文环境 */
public void removeContext(HttpContext context)
{
try
{
context.destroy();
}
catch (Exception e)
{
log.error("移除上下文环境时异常", e);
}
finally
{
synchronized (contextList)
{//从列表中删除
contextList.remove(context);
}
}
}
public void removeContext(String contextId)
{
HttpContext context = getContext(contextId);
if (context == null)
return;
removeContext(context);
}
/** 检查域名和路径是否存在相同的配置 */
public void chkContextDomainPath(HashSet<String> domains, String path)
{
List<HttpContext> list = Lists.copy(contextList, true);
if (domains.isEmpty())
{//未配置域名
for (HttpContext ctx : list)
{
if (!ctx.getContextDomains().isEmpty())
continue;
if (!ctx.getContextPath().equals(path))
continue;
throw Asserts.exception("存在相同的上下文环境配置[%s]", ctx.getContextPath());
}
}
else
{//有配置域名
for (HttpContext ctx : list)
{
HashSet<String> ds = ctx.getContextDomains();
if (ds.isEmpty())
continue;
if (!ctx.getContextPath().equals(path))
continue;
for (String d : domains)
{
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)
{
HashSet<String> 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();
}
}