Files
ziqim/zhiqim_httpd/src/org/zhiqim/httpd/HttpServer.java
T
2025-02-20 14:59:35 +08:00

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();
}
}