/* * 版权所有 (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 . */ 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多例服务,负责启动和关闭:

* 1、初始化AIO/BIO/NIO监听服务
* 2、加载和配置上下文环境
* * @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 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 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(); } }