add tomcat embed

This commit is contained in:
Looly 2024-11-24 18:29:22 +08:00
parent 8bfce0e26e
commit 6ed0b11626
16 changed files with 652 additions and 24 deletions

View File

@ -39,9 +39,10 @@
<okhttp.version>4.12.0</okhttp.version>
<!-- 固定 2.2.x支持到JDK8 -->
<undertow.version>2.2.36.Final</undertow.version>
<!-- 9.x支持到JDK8 -->
<!-- 固定 9.x支持到JDK8 -->
<jetty.version>9.4.56.v20240826</jetty.version>
<tomcat.version>11.0.1</tomcat.version>
<!-- 固定 9.x支持到JDK8 -->
<tomcat.version>9.0.97</tomcat.version>
</properties>
<dependencies>

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.http.server.engine;
import org.dromara.hutool.core.lang.Singleton;
import org.dromara.hutool.core.spi.ServiceLoader;
import org.dromara.hutool.core.spi.SpiUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.server.ServerConfig;
import org.dromara.hutool.log.LogUtil;
/**
* Http服务器引擎工厂类
*
* @author Looly
* @since 6.0.0
*/
public class ServerEngineFactory {
/**
* 获得单例的ServerEngine
*
* @return 单例的ServerEngine
*/
public static ServerEngine getEngine() {
return Singleton.get(ServerEngine.class.getName(), ServerEngineFactory::createEngine);
}
/**
* 根据用户引入的HTTP客户端引擎jar自动创建对应的HTTP服务器引擎对象<br>
* 推荐创建的引擎单例使用此方法每次调用会返回新的引擎<br>
* 对不同引擎个性化配置使用对应的{@link ServerConfig} 子类
*
* <p>
* 如果混用这些配置则个性配置不生效
*
* @param config Http客户端配置
* @return {@code ClientEngine}
*/
@SuppressWarnings("resource")
public static ServerEngine createEngine(final ServerConfig config) {
return createEngine().init(config);
}
/**
* 创建自定义引擎
*
* @param engineName 引擎名称忽略大小写`Undertow``Tomcat``Jetty``SunHttpServer`
* @return 引擎
* @throws HttpException 无对应名称的引擎
*/
public static ServerEngine createEngine(String engineName) throws HttpException {
if (!StrUtil.endWithIgnoreCase(engineName, "Engine")) {
engineName = engineName + "Engine";
}
final ServiceLoader<ServerEngine> list = SpiUtil.loadList(ServerEngine.class);
for (final String serviceName : list.getServiceNames()) {
if (StrUtil.endWithIgnoreCase(serviceName, engineName)) {
return list.getService(serviceName);
}
}
throw new HttpException("No such engine named: " + engineName);
}
/**
* 根据用户引入的HTTP服务器引擎jar自动创建对应的HTTP客服务器引擎对象<br>
* 推荐创建的引擎单例使用此方法每次调用会返回新的引擎
*
* @return {@code ServerEngine}
*/
public static ServerEngine createEngine() {
// SunServerEngine托底始终不空
final ServerEngine engine = SpiUtil.loadFirstAvailable(ServerEngine.class);
LogUtil.debug("Use [{}] Http Engine As Default.", StrUtil.removeSuffix(engine.getClass().getSimpleName(), "Engine"));
return engine;
}
}

View File

@ -17,6 +17,8 @@
package org.dromara.hutool.http.server.engine.jetty;
import org.dromara.hutool.http.server.handler.HttpHandler;
import org.dromara.hutool.http.server.servlet.JavaxServletRequest;
import org.dromara.hutool.http.server.servlet.JavaxServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
@ -44,6 +46,6 @@ public class Jetty9Handler extends AbstractHandler {
@Override
public void handle(final String target, final Request baseRequest,
final HttpServletRequest request, final HttpServletResponse response) {
handler.handle(new Jetty9Request(request), new Jetty9Response(response));
handler.handle(new JavaxServletRequest(request), new JavaxServletResponse(response));
}
}

View File

@ -17,6 +17,7 @@
package org.dromara.hutool.http.server.engine.sun;
import com.sun.net.httpserver.*;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.thread.GlobalThreadPool;
@ -115,7 +116,9 @@ public class SunHttpServerEngine extends AbstractServerEngine {
path = StrUtil.addPrefixIfNot(path, StrUtil.SLASH);
final HttpContext context = this.server.createContext(path, handler);
// 增加整体过滤器
if(CollUtil.isNotEmpty(this.filters)){
context.getFilters().addAll(this.filters);
}
return context;
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.http.server.engine.tomcat;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.valves.ValveBase;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.server.engine.AbstractServerEngine;
/**
* Tomcat引擎实现
*
* @author Looly
* @since 6.0.0
*/
public class TomcatEngine extends AbstractServerEngine {
private Tomcat tomcat;
/**
* 构造
*/
public TomcatEngine() {
// issue#IABWBL JDK8下在IDEA旗舰版加载Spring boot插件时启动应用不会检查字段类是否存在
// 此处构造时调用下这个类以便触发类是否存在的检查
Assert.notNull(Tomcat.class);
// 初始化过程中遇到任何严重错误时会立即退出
System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
}
@Override
public void start() {
initEngine();
try {
this.tomcat.start();
} catch (final LifecycleException e) {
throw new HttpException(e);
}
// 阻塞
tomcat.getServer().await();
}
@Override
public Tomcat getRawEngine() {
return this.tomcat;
}
@Override
protected void reset() {
if (null != this.tomcat) {
try {
this.tomcat.destroy();
} catch (final LifecycleException e) {
throw new HttpException(e);
}
this.tomcat = null;
}
}
@Override
protected void initEngine() {
if (null != this.tomcat) {
return;
}
final Tomcat tomcat = new Tomcat();
tomcat.setHostname(this.config.getHost());
tomcat.setPort(this.config.getPort());
tomcat.setBaseDir(this.config.getRoot());
initContext(tomcat);
this.tomcat = tomcat;
}
/**
* 初始化Context
*
* @param tomcat Tomcat
*/
private void initContext(final Tomcat tomcat) {
final Context context = tomcat.addContext("/", this.config.getRoot());
context.getPipeline().addValve(new ValveBase() {
@Override
public void invoke(final Request request, final Response response) {
handler.handle(new TomcatRequest(request), new TomcatResponse(response));
}
// 此处拦截所有请求无需调用getNext
});
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.http.server.engine.tomcat;
import org.apache.catalina.connector.Request;
import org.dromara.hutool.http.server.handler.ServerRequest;
import java.io.InputStream;
/**
* Tomcat请求对象包装
*
* @author Looly
* @since 6.0.0
*/
public class TomcatRequest implements ServerRequest {
private final Request request;
/**
* 构造
*
* @param request Tomcat请求对象
*/
public TomcatRequest(final Request request) {
this.request = request;
}
@Override
public String getMethod() {
return request.getMethod();
}
@Override
public String getPath() {
return request.getContextPath();
}
@Override
public String getQuery() {
return request.getQueryString();
}
@Override
public String getHeader(final String name) {
return request.getHeader(name);
}
@Override
public InputStream getBodyStream() {
return request.getStream();
}
}

View File

@ -14,43 +14,44 @@
* limitations under the License.
*/
package org.dromara.hutool.http.server.engine.jetty;
package org.dromara.hutool.http.server.engine.tomcat;
import org.apache.catalina.connector.Response;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.http.server.handler.ServerResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* Jetty响应对象包装
* Tomcat响应对象包装
*
* @author Looly
* @since 6.0.0
*/
public class Jetty9Response implements ServerResponse {
public class TomcatResponse implements ServerResponse {
private final HttpServletResponse response;
private final Response response;
private Charset charset;
/**
* 构造
*
* @param response Jetty响应对象
* @param response Tomcat响应对象
*/
public Jetty9Response(final HttpServletResponse response) {
public TomcatResponse(final Response response) {
this.response = response;
}
@Override
public Jetty9Response setStatus(final int statusCode) {
this.response.setStatus(statusCode);
public TomcatResponse setStatus(final int statusCode) {
response.setStatus(statusCode);
return this;
}
@Override
public Jetty9Response setCharset(final Charset charset) {
public TomcatResponse setCharset(final Charset charset) {
this.charset = charset;
return this;
}
@ -61,13 +62,13 @@ public class Jetty9Response implements ServerResponse {
}
@Override
public Jetty9Response addHeader(final String header, final String value) {
public TomcatResponse addHeader(final String header, final String value) {
this.response.addHeader(header, value);
return this;
}
@Override
public Jetty9Response setHeader(final String header, final String value) {
public TomcatResponse setHeader(final String header, final String value) {
this.response.setHeader(header, value);
return this;
}

View File

@ -14,20 +14,22 @@
* limitations under the License.
*/
package org.dromara.hutool.http.server.engine.jetty;
package org.dromara.hutool.http.server.servlet;
import org.dromara.hutool.http.server.handler.ServerRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* Jetty请求对象包装
* Javax Servlet请求对象包装
*
* @author Looly
*/
public class Jetty9Request implements ServerRequest {
public class JavaxServletRequest implements ServerRequest {
private final HttpServletRequest request;
@ -36,7 +38,7 @@ public class Jetty9Request implements ServerRequest {
*
* @param request Jetty请求对象
*/
public Jetty9Request(final HttpServletRequest request) {
public JavaxServletRequest(final HttpServletRequest request) {
this.request = request;
}
@ -55,6 +57,24 @@ public class Jetty9Request implements ServerRequest {
return this.request.getQueryString();
}
/**
* 获取所有请求头
*
* @return 请求头Map
*/
public Map<String, String> getHeaderMap(){
return JavaxServletUtil.getHeaderMap(this.request);
}
/**
* 获取所有请求头包括多个相同名称的请求头
*
* @return 请求头Map
*/
public Map<String, List<String>> getHeadersMap(){
return JavaxServletUtil.getHeadersMap(this.request);
}
@Override
public String getHeader(final String name) {
return this.request.getHeader(name);

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.http.server.servlet;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.http.server.handler.ServerResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* Javax Jetty响应对象包装
*
* @author Looly
*/
public class JavaxServletResponse implements ServerResponse {
private final HttpServletResponse response;
private Charset charset;
/**
* 构造
*
* @param response Jetty响应对象
*/
public JavaxServletResponse(final HttpServletResponse response) {
this.response = response;
}
@Override
public JavaxServletResponse setStatus(final int statusCode) {
this.response.setStatus(statusCode);
return this;
}
@Override
public JavaxServletResponse setCharset(final Charset charset) {
this.charset = charset;
return this;
}
@Override
public Charset getCharset() {
return this.charset;
}
@Override
public JavaxServletResponse addHeader(final String header, final String value) {
this.response.addHeader(header, value);
return this;
}
@Override
public JavaxServletResponse setHeader(final String header, final String value) {
this.response.setHeader(header, value);
return this;
}
/**
* 增加Cookie
*
* @param cookie Cookie对象
* @return this
*/
public JavaxServletResponse addCookie(final Cookie cookie){
JavaxServletUtil.addCookie(this.response, cookie);
return this;
}
@Override
public OutputStream getOutputStream() {
try {
return this.response.getOutputStream();
} catch (final IOException e) {
throw new IORuntimeException(e);
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.http.server.servlet;
import jakarta.servlet.http.HttpServletRequest;
import org.dromara.hutool.http.server.handler.ServerRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* Jakarta Servlet请求对象包装
*
* @author Looly
*/
public class ServletRequest implements ServerRequest {
private final HttpServletRequest request;
/**
* 构造
*
* @param request Jetty请求对象
*/
public ServletRequest(final HttpServletRequest request) {
this.request = request;
}
@Override
public String getMethod() {
return this.request.getMethod();
}
@Override
public String getPath() {
return this.request.getContextPath();
}
@Override
public String getQuery() {
return this.request.getQueryString();
}
/**
* 获取所有请求头
*
* @return 请求头Map
*/
public Map<String, String> getHeaderMap(){
return ServletUtil.getHeaderMap(this.request);
}
/**
* 获取所有请求头包括多个相同名称的请求头
*
* @return 请求头Map
*/
public Map<String, List<String>> getHeadersMap(){
return ServletUtil.getHeadersMap(this.request);
}
@Override
public String getHeader(final String name) {
return this.request.getHeader(name);
}
@Override
public InputStream getBodyStream() {
try {
return this.request.getInputStream();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.http.server.servlet;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.http.server.handler.ServerResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* Jakarta Servlet响应对象包装
*
* @author Looly
*/
public class ServletResponse implements ServerResponse {
private final HttpServletResponse response;
private Charset charset;
/**
* 构造
*
* @param response Jetty响应对象
*/
public ServletResponse(final HttpServletResponse response) {
this.response = response;
}
@Override
public ServletResponse setStatus(final int statusCode) {
this.response.setStatus(statusCode);
return this;
}
@Override
public ServletResponse setCharset(final Charset charset) {
this.charset = charset;
return this;
}
@Override
public Charset getCharset() {
return this.charset;
}
@Override
public ServletResponse addHeader(final String header, final String value) {
this.response.addHeader(header, value);
return this;
}
@Override
public ServletResponse setHeader(final String header, final String value) {
this.response.setHeader(header, value);
return this;
}
/**
* 增加Cookie
*
* @param cookie Cookie对象
* @return this
*/
public ServletResponse addCookie(final Cookie cookie){
ServletUtil.addCookie(this.response, cookie);
return this;
}
@Override
public OutputStream getOutputStream() {
try {
return this.response.getOutputStream();
} catch (final IOException e) {
throw new IORuntimeException(e);
}
}
}

View File

@ -0,0 +1,20 @@
#
# Copyright (c) 2024 Hutool Team and hutool.cn
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
org.dromara.hutool.http.server.engine.undertow.UndertowEngine
org.dromara.hutool.http.server.engine.tomcat.TomcatEngine
org.dromara.hutool.http.server.engine.jetty.JettyEngine
org.dromara.hutool.http.server.engine.sun.SunHttpServerEngine

View File

@ -2,11 +2,10 @@ package org.dromara.hutool.http.server.engine;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.http.server.ServerConfig;
import org.dromara.hutool.http.server.engine.jetty.JettyEngine;
public class JettyTest {
public static void main(String[] args) {
final JettyEngine engine = new JettyEngine();
final ServerEngine engine = ServerEngineFactory.createEngine("jetty");
engine.init(ServerConfig.of());
engine.setHandler((request, response) -> {
Console.log(request.getPath());

View File

@ -0,0 +1,16 @@
package org.dromara.hutool.http.server.engine;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.http.server.ServerConfig;
public class SunServerTest {
public static void main(String[] args) {
final ServerEngine engine = ServerEngineFactory.createEngine("SunHttpServer");
engine.init(ServerConfig.of());
engine.setHandler((request, response) -> {
Console.log(request.getPath());
response.write("Hutool Sun Server response test");
});
engine.start();
}
}

View File

@ -0,0 +1,16 @@
package org.dromara.hutool.http.server.engine;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.http.server.ServerConfig;
public class TomcatTest {
public static void main(String[] args) {
final ServerEngine engine = ServerEngineFactory.createEngine("tomcat");
engine.init(ServerConfig.of());
engine.setHandler((request, response) -> {
Console.log(request.getPath());
response.write("Hutool Tomcat response test");
});
engine.start();
}
}

View File

@ -18,11 +18,10 @@ package org.dromara.hutool.http.server.engine;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.http.server.ServerConfig;
import org.dromara.hutool.http.server.engine.undertow.UndertowEngine;
public class UndertowTest {
public static void main(String[] args) {
final UndertowEngine engine = new UndertowEngine();
final ServerEngine engine = ServerEngineFactory.createEngine("undertow");
engine.init(ServerConfig.of());
engine.setHandler((request, response) -> {
Console.log(request.getPath());