diff --git a/README.md b/README.md index 6ac4d41daa78ccb205d165217cdffec6da69292a..033d182df085ecfa7bfb6826b289c7a63d4c6d8c 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ smart-servlet 在 smart-http 的架构之上,通过继承 HttpHandle 实现了 org.smartboot.servlet smart-servlet-maven-plugin - 0.3 + 0.4 8080 @@ -90,7 +90,7 @@ smart-servlet 在 smart-http 的架构之上,通过继承 HttpHandle 实现了 org.smartboot.servlet smart-servlet-spring-boot-starter - 0.3 + 0.4 diff --git a/pom.xml b/pom.xml index 58d74cbcad0477268abeed5bf0caa2828747879c..ee2ce2d8b4dfd7848af8e068b3ee6da04af77d8e 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ 4.0.0 org.smartboot.servlet smart-servlet-parent - 0.3 + 0.4 pom - 1.2.1 - 0.3 + 1.2.7 + 0.4 4.0.1 1.3.2 diff --git a/servlet-core/pom.xml b/servlet-core/pom.xml index 9fb7f51638567c6a87a8aa0c41227079586ea1cc..d4c22ff053c62ddb623030c32a9f8502ca600baf 100644 --- a/servlet-core/pom.xml +++ b/servlet-core/pom.xml @@ -15,7 +15,7 @@ smart-servlet-parent org.smartboot.servlet - 0.3 + 0.4 servlet-core 4.0.0 diff --git a/servlet-core/src/main/java/org/smartboot/servlet/ContainerRuntime.java b/servlet-core/src/main/java/org/smartboot/servlet/ContainerRuntime.java index 997e1594ea820ec415ddc1834a930624f64d8564..ecfbdb19f64fa68727df372ab61b2cddacabbe7d 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/ContainerRuntime.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/ContainerRuntime.java @@ -10,9 +10,6 @@ package org.smartboot.servlet; -import javax.servlet.DispatcherType; -import javax.servlet.ServletContainerInitializer; -import javax.servlet.http.HttpServletResponse; import org.smartboot.http.common.logging.Logger; import org.smartboot.http.common.logging.LoggerFactory; import org.smartboot.http.common.utils.StringUtils; @@ -36,6 +33,9 @@ import org.smartboot.servlet.impl.HttpServletResponseImpl; import org.smartboot.servlet.impl.ServletContextImpl; import org.smartboot.servlet.plugins.Plugin; +import javax.servlet.DispatcherType; +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -64,7 +64,7 @@ public class ContainerRuntime { * Font Name: Puffy */ private static final String BANNER = " _ _ _ \n" + " ( )_ (_ ) ( )_ \n" + " ___ ___ ___ _ _ _ __ | ,_) ___ __ _ __ _ _ | | __ | ,_)\n" + "/',__)/' _ ` _ `\\ /'_` )( '__)| | /',__) /'__`\\( '__)( ) ( ) | | /'__`\\| | \n" + "\\__, \\| ( ) ( ) |( (_| || | | |_ \\__, \\( ___/| | | \\_/ | | | ( ___/| |_ \n" + "(____/(_) (_) (_)`\\__,_)(_) `\\__) (____/`\\____)(_) `\\___/'(___)`\\____)`\\__)"; - public static final String VERSION = "0.3"; + public static final String VERSION = "0.4"; /** * 注册在当前 Servlet 容器中的运行环境 */ @@ -83,7 +83,7 @@ public class ContainerRuntime { */ private HttpServerConfiguration configuration; - public void start(HttpServerConfiguration configuration) { + public void start(HttpServerConfiguration configuration) throws Throwable { if (started) { return; } @@ -98,7 +98,7 @@ public class ContainerRuntime { @Override public void handleRequest(HandlerContext handlerContext) { try { - HttpServletResponse response = handlerContext.getResponse(); + ServletResponse response = handlerContext.getResponse(); response.setContentLength(line.length); response.getOutputStream().write(line); } catch (IOException e) { @@ -112,7 +112,9 @@ public class ContainerRuntime { loadAndInstallPlugins(); //启动运行环境 - runtimes.forEach(ServletContextRuntime::start); + for (ServletContextRuntime runtime : runtimes) { + runtime.start(); + } } @@ -238,9 +240,9 @@ public class ContainerRuntime { try { //识别请求对应的运行时环境,必然不能为null,要求存在contextPath为"/"的container ServletContextRuntime runtime = matchRuntime(request.getRequestURI()); - if (!runtime.isStarted()) { - throw new IllegalStateException("container is not started"); - } +// if (!runtime.isStarted()) { +// throw new IllegalStateException("container is not started"); +// } ServletContextImpl servletContext = runtime.getServletContext(); Thread.currentThread().setContextClassLoader(servletContext.getClassLoader()); @@ -325,6 +327,8 @@ public class ContainerRuntime { //register Servlet into deploymentInfo webAppInfo.getServlets().values().forEach(deploymentInfo::addServlet); + webAppInfo.getErrorPages().forEach(deploymentInfo::addErrorPage); + //register Filter webAppInfo.getFilters().values().forEach(deploymentInfo::addFilter); //register servletContext into deploymentInfo diff --git a/servlet-core/src/main/java/org/smartboot/servlet/HandlerContext.java b/servlet-core/src/main/java/org/smartboot/servlet/HandlerContext.java index 96465a9317ae9acb3a0ad35f9f25ddb250441ea1..02cac9ccb26e0f8c8ad2ef9d9f4d91333ac6ace8 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/HandlerContext.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/HandlerContext.java @@ -13,7 +13,7 @@ package org.smartboot.servlet; import org.smartboot.servlet.conf.ServletInfo; import org.smartboot.servlet.impl.ServletContextImpl; -import javax.servlet.http.HttpServletResponse; +import javax.servlet.ServletResponse; /** * 请求处理上下文对象 @@ -29,7 +29,7 @@ public class HandlerContext { /** * 响应 */ - private final HttpServletResponse response; + private final ServletResponse response; /** * 匹配的Servlet上下文 */ @@ -41,7 +41,7 @@ public class HandlerContext { */ private ServletInfo servletInfo; - public HandlerContext(SmartHttpServletRequest request, HttpServletResponse response, ServletContextImpl servletContext, boolean namedDispatcher) { + public HandlerContext(SmartHttpServletRequest request, ServletResponse response, ServletContextImpl servletContext, boolean namedDispatcher) { this.request = request; this.response = response; this.servletContext = servletContext; @@ -58,7 +58,7 @@ public class HandlerContext { } - public HttpServletResponse getResponse() { + public ServletResponse getResponse() { return response; } diff --git a/servlet-core/src/main/java/org/smartboot/servlet/ServletContextRuntime.java b/servlet-core/src/main/java/org/smartboot/servlet/ServletContextRuntime.java index 79e5531e184ae7d627d3d312e08baa2684c0553d..a275d8ff3cbdd76315fdb8d709da24bff528386b 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/ServletContextRuntime.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/ServletContextRuntime.java @@ -10,6 +10,9 @@ package org.smartboot.servlet; +import org.smartboot.http.common.enums.HttpStatus; +import org.smartboot.http.common.logging.Logger; +import org.smartboot.http.common.logging.LoggerFactory; import org.smartboot.http.common.utils.StringUtils; import org.smartboot.servlet.conf.DeploymentInfo; import org.smartboot.servlet.conf.FilterInfo; @@ -27,13 +30,19 @@ import org.smartboot.servlet.sandbox.SandBox; import javax.servlet.Filter; import javax.servlet.FilterConfig; +import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletException; +import javax.servlet.UnavailableException; import javax.servlet.annotation.WebListener; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -47,6 +56,7 @@ import java.util.List; * @version V1.0 , 2019/12/11 */ public class ServletContextRuntime { + private static final Logger LOGGER = LoggerFactory.getLogger(ServletContextRuntime.class); private String displayName; private String description; /** @@ -121,7 +131,7 @@ public class ServletContextRuntime { /** * 启动容器 */ - public void start() { + public void start() throws Throwable { ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); try { //有些场景下ServletContainerInitializer初始化依赖当前容器的类加载器 @@ -153,6 +163,7 @@ public class ServletContextRuntime { } catch (Exception e) { e.printStackTrace(); plugins.forEach(plugin -> plugin.whenContainerStartError(this, e)); + throw e; } finally { Thread.currentThread().setContextClassLoader(currentClassLoader); } @@ -161,6 +172,9 @@ public class ServletContextRuntime { private void newServletsInstance(DeploymentInfo deploymentInfo) throws InstantiationException, IllegalAccessException, ClassNotFoundException { for (ServletInfo servletInfo : deploymentInfo.getServlets().values()) { if (!servletInfo.isDynamic()) { + if (servletInfo.getJspFile() != null) { + throw new UnsupportedOperationException(); + } Servlet servlet = (Servlet) deploymentInfo.getClassLoader().loadClass(servletInfo.getServletClass()).newInstance(); servletInfo.setServlet(servlet); } @@ -223,7 +237,40 @@ public class ServletContextRuntime { for (ServletInfo servletInfo : servletInfoList) { ServletConfig servletConfig = new ServletConfigImpl(servletInfo, servletContext); - servletInfo.getServlet().init(servletConfig); + try { + servletInfo.getServlet().init(servletConfig); + } catch (UnavailableException e) { + e.printStackTrace(); + //占用该Servlet的URL mappings + servletInfo.setServlet(new HttpServlet() { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) { + resp.setStatus(HttpStatus.NOT_FOUND.value()); + } + }); + } catch (ServletException e) { +// e.printStackTrace(); + String location = deploymentInfo.getErrorPageLocation(e); + if (location == null) { + location = deploymentInfo.getErrorPageLocation(HttpStatus.INTERNAL_SERVER_ERROR.value()); + } + String finalLocation = location; + servletInfo.setServlet(new HttpServlet() { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + req.setAttribute(RequestDispatcher.ERROR_EXCEPTION, e); + req.setAttribute(RequestDispatcher.ERROR_MESSAGE, e.getMessage()); + req.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpStatus.INTERNAL_SERVER_ERROR.value()); + if (finalLocation != null) { + req.getRequestDispatcher(finalLocation).forward(req, resp); + } else { + LOGGER.error("error location is null"); + e.printStackTrace(resp.getWriter()); + } + } + }); + } } } diff --git a/servlet-core/src/main/java/org/smartboot/servlet/WebXmlParseEngine.java b/servlet-core/src/main/java/org/smartboot/servlet/WebXmlParseEngine.java index 690251806226b89fdd8f0dbc5cbe55137c30e08c..127e8180fe23f4272706f81a8a51c508ecad3f17 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/WebXmlParseEngine.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/WebXmlParseEngine.java @@ -129,11 +129,7 @@ class WebXmlParseEngine { List childNodeList = getChildNode(parentElement, "error-page"); for (Node node : childNodeList) { Map nodeData = getNodeValue(node, Arrays.asList("error-code", "location", "exception-type")); - int errorCode = NumberUtils.toInt(nodeData.get("error-code"), -1); - if (errorCode < 0) { - continue; - } - webAppInfo.addErrorPage(new ErrorPageInfo(nodeData.get("location"), errorCode, nodeData.get("exception-type"))); + webAppInfo.addErrorPage(new ErrorPageInfo(nodeData.get("location"), NumberUtils.toInt(nodeData.get("error-code"), -1), nodeData.get("exception-type"))); } } @@ -163,10 +159,11 @@ class WebXmlParseEngine { NodeList rootNodeList = parentElement.getElementsByTagName("servlet"); for (int i = 0; i < rootNodeList.getLength(); i++) { Node node = rootNodeList.item(i); - Map nodeMap = getNodeValue(node, Arrays.asList("servlet-name", "servlet-class", "load-on-startup", "async-supported")); + Map nodeMap = getNodeValue(node, Arrays.asList("servlet-name", "servlet-class", "load-on-startup", "async-supported", "jsp-file")); ServletInfo servletInfo = new ServletInfo(); servletInfo.setServletName(nodeMap.get("servlet-name")); servletInfo.setServletClass(nodeMap.get("servlet-class")); + servletInfo.setJspFile(nodeMap.get("jsp-file")); servletInfo.setLoadOnStartup(NumberUtils.toInt(nodeMap.get("load-on-startup"), 0)); servletInfo.setAsyncSupported(Boolean.parseBoolean(nodeMap.get("async-supported"))); Map initParamMap = parseParam(node); diff --git a/servlet-core/src/main/java/org/smartboot/servlet/conf/DeploymentInfo.java b/servlet-core/src/main/java/org/smartboot/servlet/conf/DeploymentInfo.java index 58e8ac2ab8636bcc6812dc242025af8be98a1c66..ca2c2756fe0b6f6835a6cdb34dee3432fec9eb1a 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/conf/DeploymentInfo.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/conf/DeploymentInfo.java @@ -26,6 +26,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * 运行环境部署配置 @@ -35,6 +37,9 @@ import java.util.Map; */ public class DeploymentInfo { private final Map servlets = new HashMap<>(); + + private final Map errorStatusPages = new HashMap<>(); + private final Map errorPages = new HashMap<>(); private final Map filters = new HashMap<>(); private final List filterMappings = new ArrayList<>(); private final Map initParameters = new HashMap<>(); @@ -58,6 +63,8 @@ public class DeploymentInfo { */ private int sessionTimeout; + private ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + public URL getContextUrl() { return contextUrl; } @@ -105,6 +112,26 @@ public class DeploymentInfo { return servlets; } + public void addErrorPage(final ErrorPageInfo servlet) { + if (servlet.getErrorCode() != null) { + errorStatusPages.put(servlet.getErrorCode(), servlet); + } + if (servlet.getExceptionType() != null) { + errorPages.put(servlet.getExceptionType(), servlet); + } + + } + + public String getErrorPageLocation(int errorCode) { + ErrorPageInfo errorPage = errorStatusPages.get(errorCode); + return errorPage == null ? null : errorPage.getLocation(); + } + + public String getErrorPageLocation(Exception exception) { + ErrorPageInfo errorPage = errorPages.get(exception.getClass().getName()); + return errorPage == null ? null : errorPage.getLocation(); + } + public void addFilter(final FilterInfo filter) { filters.put(filter.getFilterName(), filter); } @@ -235,4 +262,8 @@ public class DeploymentInfo { public void setSessionTimeout(int sessionTimeout) { this.sessionTimeout = sessionTimeout; } + + public ExecutorService getExecutor() { + return executor; + } } diff --git a/servlet-core/src/main/java/org/smartboot/servlet/conf/ErrorPageInfo.java b/servlet-core/src/main/java/org/smartboot/servlet/conf/ErrorPageInfo.java index 57ad16b6ba4be4b8d8c5ac1c7642965db54403ed..6ab3bbbf4da931f4becc9dab6f83207deda036ff 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/conf/ErrorPageInfo.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/conf/ErrorPageInfo.java @@ -15,9 +15,9 @@ package org.smartboot.servlet.conf; * @version V1.0 , 2019/12/13 */ public class ErrorPageInfo { - private String location; - private Integer errorCode; - private String exceptionType; + private final String location; + private final Integer errorCode; + private final String exceptionType; public ErrorPageInfo(String location, Integer errorCode, String exceptionType) { this.location = location; diff --git a/servlet-core/src/main/java/org/smartboot/servlet/conf/ServletInfo.java b/servlet-core/src/main/java/org/smartboot/servlet/conf/ServletInfo.java index 6491bb54257239ec1c89a14e2c0b1be332b09174..85a402d22cd91525e3a83e8e8eba5692ef203310 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/conf/ServletInfo.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/conf/ServletInfo.java @@ -33,6 +33,8 @@ public class ServletInfo { private int loadOnStartup; private Servlet servlet; + private String jspFile; + private boolean dynamic; private MultipartConfigElement multipartConfig; @@ -124,4 +126,12 @@ public class ServletInfo { public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } + + public String getJspFile() { + return jspFile; + } + + public void setJspFile(String jspFile) { + this.jspFile = jspFile; + } } diff --git a/servlet-core/src/main/java/org/smartboot/servlet/conf/WebAppInfo.java b/servlet-core/src/main/java/org/smartboot/servlet/conf/WebAppInfo.java index 8e233a6047096b781347a28e03f32acdecfa3f1e..47fe29f1d21d4f639dd0eed5272be963e855ded2 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/conf/WebAppInfo.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/conf/WebAppInfo.java @@ -44,7 +44,7 @@ public class WebAppInfo { private final Map contextParams = new HashMap<>(); - private final Map errorPages = new HashMap<>(); + private final List errorPages = new ArrayList<>(); private final List welcomeFileList = new ArrayList<>(); @@ -75,7 +75,7 @@ public class WebAppInfo { } public void addErrorPage(ErrorPageInfo errorPageInfo) { - errorPages.put(errorPageInfo.getErrorCode(), errorPageInfo); + errorPages.add(errorPageInfo); } public int getSessionTimeout() { @@ -106,7 +106,7 @@ public class WebAppInfo { return contextParams; } - public Map getErrorPages() { + public List getErrorPages() { return errorPages; } diff --git a/servlet-core/src/main/java/org/smartboot/servlet/handler/ServletServiceHandler.java b/servlet-core/src/main/java/org/smartboot/servlet/handler/ServletServiceHandler.java index 6081c1222fb060041bb2304dc13c9eaa5c2a082f..5a295e7257903cb7d06f853b98f4d2a560760164 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/handler/ServletServiceHandler.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/handler/ServletServiceHandler.java @@ -15,8 +15,8 @@ import org.smartboot.servlet.conf.ServletInfo; import org.smartboot.servlet.exception.WrappedRuntimeException; import javax.servlet.ServletException; +import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** @@ -31,7 +31,7 @@ public class ServletServiceHandler extends Handler { public void handleRequest(HandlerContext handlerContext) { try { HttpServletRequest request = handlerContext.getRequest(); - HttpServletResponse response = handlerContext.getResponse(); + ServletResponse response = handlerContext.getResponse(); //成功匹配到Servlet,直接执行 if (handlerContext.getServletInfo() != null) { handlerContext.getServletInfo().getServlet().service(request, response); diff --git a/servlet-core/src/main/java/org/smartboot/servlet/impl/AsyncContextImpl.java b/servlet-core/src/main/java/org/smartboot/servlet/impl/AsyncContextImpl.java index 9a2ca15b2700f063526091b49d221ce835cc72e6..1c94c42cd9834330162fec9ba861e2dcde1409ae 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/impl/AsyncContextImpl.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/impl/AsyncContextImpl.java @@ -10,80 +10,153 @@ package org.smartboot.servlet.impl; +import org.smartboot.servlet.HandlerContext; +import org.smartboot.servlet.ServletContextRuntime; +import org.smartboot.servlet.plugins.dispatcher.ServletRequestDispatcherWrapper; + import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; +import javax.servlet.DispatcherType; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; /** * @author 三刀(zhengjunweimail@163.com) * @version V1.0 , 2022/11/23 */ public class AsyncContextImpl implements AsyncContext { + private List listeners = new LinkedList<>(); + private HttpServletRequestImpl originalRequest; + private final ServletRequest request; + private final ServletResponse response; + private long timeout = 5000; + private boolean dispatched; + private boolean complete; + private final ServletContextRuntime servletContextRuntime; + + public AsyncContextImpl(ServletContextRuntime deployment, HttpServletRequestImpl originalRequest, ServletRequest request, ServletResponse response) { + this.originalRequest = originalRequest; + this.request = request; + this.response = response; + this.servletContextRuntime = deployment; + } + @Override public ServletRequest getRequest() { - return null; + return request; } @Override public ServletResponse getResponse() { - return null; + return response; } @Override public boolean hasOriginalRequestAndResponse() { - return false; + return request instanceof HttpServletRequestImpl && response instanceof HttpServletResponseImpl; } @Override public void dispatch() { + if (hasOriginalRequestAndResponse()) { + String toDispatch = originalRequest.getRequestURI().substring(request.getServletContext().getContextPath().length()); + String qs = originalRequest.getQueryString(); + if (qs != null && !qs.isEmpty()) { + toDispatch = toDispatch + "?" + qs; + } + dispatch(request.getServletContext(), toDispatch); + } else { + throw new UnsupportedOperationException(); +// dispatch(request.getServletContext(), ""); + } } @Override public void dispatch(String path) { - + dispatch(request.getServletContext(), path); } @Override public void dispatch(ServletContext context, String path) { + if (dispatched) { + throw new IllegalStateException(); + } + dispatched = true; + servletContextRuntime.getDeploymentInfo().getExecutor().execute(new Runnable() { + @Override + public void run() { + ServletRequestDispatcherWrapper wrapper = new ServletRequestDispatcherWrapper(originalRequest, DispatcherType.ASYNC, false); + HandlerContext handlerContext = new HandlerContext(wrapper, response, originalRequest.getServletContext(), false); + servletContextRuntime.getServletContext().getPipeline().handleRequest(handlerContext); + } + }); } @Override public void complete() { - + listeners.forEach(unit -> { + try { + unit.listener.onComplete(new AsyncEvent(this, unit.request, unit.response)); + } catch (IOException e) { + e.printStackTrace(); + } + }); } @Override public void start(Runnable run) { - + servletContextRuntime.getDeploymentInfo().getExecutor().execute(run); } @Override public void addListener(AsyncListener listener) { - + addListener(listener, request, response); } @Override public void addListener(AsyncListener listener, ServletRequest servletRequest, ServletResponse servletResponse) { - + if (dispatched) { + throw new IllegalStateException(); + } + listeners.add(new ListenerUnit(listener, request, response)); } @Override public T createListener(Class clazz) throws ServletException { - return null; + try { + return clazz.newInstance(); + } catch (Exception e) { + throw new ServletException(e); + } } @Override public void setTimeout(long timeout) { - + this.timeout = timeout; } @Override public long getTimeout() { - return 0; + return timeout; + } + + static class ListenerUnit { + AsyncListener listener; + ServletRequest request; + ServletResponse response; + + public ListenerUnit(AsyncListener listener, ServletRequest request, ServletResponse response) { + this.listener = listener; + this.request = request; + this.response = response; + } } } diff --git a/servlet-core/src/main/java/org/smartboot/servlet/impl/HttpServletRequestImpl.java b/servlet-core/src/main/java/org/smartboot/servlet/impl/HttpServletRequestImpl.java index c62f0399d921f2902293d6f8457e1ec2456840fb..88d91afc9d3d0cd737ee2174b5cfe61de0d399f4 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/impl/HttpServletRequestImpl.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/impl/HttpServletRequestImpl.java @@ -97,6 +97,8 @@ public class HttpServletRequestImpl implements SmartHttpServletRequest { */ private ServletInfo servletInfo; + private boolean asyncStarted = false; + public HttpServletRequestImpl(HttpRequest request, ServletContextRuntime runtime, DispatcherType dispatcherType) { this.request = request; this.dispatcherType = dispatcherType; @@ -644,17 +646,24 @@ public class HttpServletRequestImpl implements SmartHttpServletRequest { @Override public AsyncContext startAsync() throws IllegalStateException { - throw new UnsupportedOperationException(); + return startAsync(this, httpServletResponse); } @Override public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { - throw new UnsupportedOperationException(); + if (!isAsyncSupported()) { + throw new IllegalStateException(); + } + if (asyncStarted) { + throw new IllegalStateException(); + } + asyncStarted = true; + return new AsyncContextImpl(runtime, this, servletRequest, servletResponse); } @Override public boolean isAsyncStarted() { - return false; + return asyncStarted; } @Override diff --git a/servlet-core/src/main/java/org/smartboot/servlet/impl/HttpServletResponseImpl.java b/servlet-core/src/main/java/org/smartboot/servlet/impl/HttpServletResponseImpl.java index 8f5c9349b63873d4167d7b6d4afb521bc56d0dd5..8bc5cbb31b1d1373fd1823b0cd66bf4abcde8cdd 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/impl/HttpServletResponseImpl.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/impl/HttpServletResponseImpl.java @@ -10,9 +10,6 @@ package org.smartboot.servlet.impl; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.smartboot.http.common.enums.HeaderNameEnum; import org.smartboot.http.common.enums.HttpStatus; import org.smartboot.http.common.logging.Logger; @@ -22,6 +19,9 @@ import org.smartboot.servlet.ServletContextRuntime; import org.smartboot.servlet.util.DateUtil; import org.smartboot.servlet.util.PathMatcherUtil; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; @@ -56,7 +56,15 @@ public class HttpServletResponseImpl implements HttpServletResponse { @Override public void addCookie(Cookie cookie) { - response.setHeader(HeaderNameEnum.SET_COOKIE.getName(), cookie.toString()); + org.smartboot.http.common.Cookie httpCookie = new org.smartboot.http.common.Cookie(cookie.getName(), cookie.getValue()); + httpCookie.setComment(cookie.getComment()); + httpCookie.setDomain(cookie.getDomain()); + httpCookie.setHttpOnly(cookie.isHttpOnly()); + httpCookie.setPath(cookie.getPath()); + httpCookie.setMaxAge(cookie.getMaxAge()); + httpCookie.setSecure(cookie.getSecure()); + httpCookie.setVersion(cookie.getVersion()); + response.addCookie(httpCookie); } @Override @@ -182,10 +190,10 @@ public class HttpServletResponseImpl implements HttpServletResponse { @Override public String getCharacterEncoding() { - if (charset != null) { - return charset; + if (charset == null) { + setCharacterEncoding(StandardCharsets.ISO_8859_1.name()); } - return StandardCharsets.ISO_8859_1.name(); + return charset; } @Override @@ -211,6 +219,8 @@ public class HttpServletResponseImpl implements HttpServletResponse { @Override public void setContentType(String type) { + //. It does not set the response's character + // encoding if it is called after getWriter has been called or after the response has been committed. if (isCommitted()) { return; } @@ -220,7 +230,11 @@ public class HttpServletResponseImpl implements HttpServletResponse { response.setContentType(type); } else { contentType = type.substring(0, split); - setCharacterEncoding(type.substring(split + 10)); + if (charsetSet) { + response.setContentType(getContentType()); + } else { + setCharacterEncoding(type.substring(split + 9)); + } } } @@ -240,9 +254,14 @@ public class HttpServletResponseImpl implements HttpServletResponse { @Override public PrintWriter getWriter() throws IOException { - if (writer == null) { - writer = new PrintWriter(new ServletPrintWriter(getOutputStream(), getCharacterEncoding())); + if (writer != null) { + return writer; + } + //if the getOutputStream method has already been called for this response object + if (servletOutputStream != null) { + throw new IllegalStateException("getOutputStream has already been called."); } + writer = new PrintWriter(new ServletPrintWriter(getOutputStream(), getCharacterEncoding())); return writer; } @@ -305,9 +324,9 @@ public class HttpServletResponseImpl implements HttpServletResponse { } response.getHeaderNames().forEach(headerName -> response.setHeader(headerName, null)); setContentLength(-1); - setContentType(null); + contentType = null; setCharacterEncoding(null); - response.setHttpStatus(null); + response.setHttpStatus(HttpStatus.OK); writer = null; if (servletOutputStream != null) { servletOutputStream.resetBuffer(); diff --git a/servlet-core/src/main/java/org/smartboot/servlet/impl/ServletContextImpl.java b/servlet-core/src/main/java/org/smartboot/servlet/impl/ServletContextImpl.java index 1b037208df40fb33e89f12c383ebacc786951485..6827246d2f4e72d7b5eefc221365a201c1e7809e 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/impl/ServletContextImpl.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/impl/ServletContextImpl.java @@ -284,14 +284,15 @@ public class ServletContextImpl implements ServletContext { Object oldValue = attributes.put(name, object); List listeners = deploymentInfo.getServletContextAttributeListeners(); - ServletContextAttributeEvent event = listeners.isEmpty() ? null : new ServletContextAttributeEvent(this, name, object); - listeners.forEach(listener -> { + if (!listeners.isEmpty()) { if (oldValue == null) { - listener.attributeAdded(event); + ServletContextAttributeEvent event = new ServletContextAttributeEvent(this, name, object); + listeners.forEach(listener -> listener.attributeAdded(event)); } else { - listener.attributeReplaced(event); + ServletContextAttributeEvent event = new ServletContextAttributeEvent(this, name, oldValue); + listeners.forEach(listener -> listener.attributeReplaced(event)); } - }); + } } @Override @@ -441,18 +442,21 @@ public class ServletContextImpl implements ServletContext { ServletContextEvent event = new ServletContextEvent(this); contextListener.contextInitialized(event); deploymentInfo.addServletContextListener(contextListener); - } else if (ServletRequestListener.class.isAssignableFrom(listener.getClass())) { + } + if (ServletRequestListener.class.isAssignableFrom(listener.getClass())) { deploymentInfo.addServletRequestListener((ServletRequestListener) listener); - } else if (ServletContextAttributeListener.class.isAssignableFrom(listener.getClass())) { + } + if (ServletContextAttributeListener.class.isAssignableFrom(listener.getClass())) { deploymentInfo.addServletContextAttributeListener((ServletContextAttributeListener) listener); - } else if (HttpSessionListener.class.isAssignableFrom(listener.getClass())) { + } + if (HttpSessionListener.class.isAssignableFrom(listener.getClass())) { deploymentInfo.addHttpSessionListener((HttpSessionListener) listener); - } else if (HttpSessionAttributeListener.class.isAssignableFrom(listener.getClass())) { + } + if (HttpSessionAttributeListener.class.isAssignableFrom(listener.getClass())) { deploymentInfo.addSessionAttributeListener((HttpSessionAttributeListener) listener); - } else if (ServletRequestAttributeListener.class.isAssignableFrom(listener.getClass())) { + } + if (ServletRequestAttributeListener.class.isAssignableFrom(listener.getClass())) { deploymentInfo.addRequestAttributeListener((ServletRequestAttributeListener) listener); - } else { - throw new RuntimeException(listener.toString()); } } diff --git a/servlet-core/src/main/java/org/smartboot/servlet/impl/ServletOutputStreamImpl.java b/servlet-core/src/main/java/org/smartboot/servlet/impl/ServletOutputStreamImpl.java index 3e79cd789338b245ed0ee7baf25c98da4f6d777c..9e2e45119570b6bda9e1ea5c3d310c4af384e2db 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/impl/ServletOutputStreamImpl.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/impl/ServletOutputStreamImpl.java @@ -28,6 +28,7 @@ public class ServletOutputStreamImpl extends ServletOutputStream { */ private byte[] buffer; private int count; + private byte[] cacheByte; public ServletOutputStreamImpl(BufferOutputStream outputStream, byte[] buffer) { this.outputStream = outputStream; @@ -46,8 +47,19 @@ public class ServletOutputStreamImpl extends ServletOutputStream { } @Override - public void write(int b) { - throw new UnsupportedOperationException(); + public void write(int v) throws IOException { + initCacheBytes(); + cacheByte[0] = (byte) v; + write(cacheByte, 0, 1); + } + + /** + * 初始化8字节的缓存数值 + */ + private void initCacheBytes() { + if (cacheByte == null) { + cacheByte = new byte[8]; + } } @Override diff --git a/servlet-core/src/main/java/org/smartboot/servlet/plugins/dispatcher/RequestDispatcherImpl.java b/servlet-core/src/main/java/org/smartboot/servlet/plugins/dispatcher/RequestDispatcherImpl.java index 52f1cb920cb2f636ee77b0f3a854d83d8750184b..636c983abefe75d1a2bb8437d5f0a1108a103eab 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/plugins/dispatcher/RequestDispatcherImpl.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/plugins/dispatcher/RequestDispatcherImpl.java @@ -91,14 +91,14 @@ class RequestDispatcherImpl implements RequestDispatcher { requestWrapper.setRequestUri(requestWrapper.getRequest().getRequestURI()); Map parameters = new HashMap<>(); HttpUtils.decodeParamString(requestWrapper.getQueryString(), parameters); - requestWrapper.setParamaters(parameters); + requestWrapper.setParameters(parameters); } else { String[] array = StringUtils.split(dispatcherURL, "?"); requestWrapper.setRequestUri(array[0]); Map parameters = new HashMap<>(); if (array.length > 1) { HttpUtils.decodeParamString(array[1], parameters); - requestWrapper.setParamaters(parameters); + requestWrapper.setParameters(parameters); } } @@ -122,7 +122,7 @@ class RequestDispatcherImpl implements RequestDispatcher { // 如果包含后续请求,那么这些属性 会被后面包含请求的相应属性值替换。 //如果通过 getNamedDispatcher 方法获得包含的 servlet,那么不能设置这些属性。 - Object requestUri = requestImpl.getRequestURI(); + String requestUri = requestImpl.getRequestURI(); Object contextPath = requestImpl.getContextPath(); Object servletPath = requestImpl.getServletPath(); Object pathInfo = requestImpl.getPathInfo(); @@ -143,6 +143,8 @@ class RequestDispatcherImpl implements RequestDispatcher { if (queryString != null) { requestWrapper.setAttribute(INCLUDE_QUERY_STRING, queryString); } + String[] array = StringUtils.split(dispatcherURL, "?"); + requestWrapper.setRequestUri(array[0]); } HandlerContext handlerContext = new HandlerContext(requestWrapper, responseWrapper, servletContext, named); diff --git a/servlet-core/src/main/java/org/smartboot/servlet/plugins/dispatcher/ServletRequestDispatcherWrapper.java b/servlet-core/src/main/java/org/smartboot/servlet/plugins/dispatcher/ServletRequestDispatcherWrapper.java index cc315be0545656d65677a297c9b8416f5ca8684d..552a5dcb121f9f5b04e8db9faf6eef03a03dd328 100644 --- a/servlet-core/src/main/java/org/smartboot/servlet/plugins/dispatcher/ServletRequestDispatcherWrapper.java +++ b/servlet-core/src/main/java/org/smartboot/servlet/plugins/dispatcher/ServletRequestDispatcherWrapper.java @@ -27,7 +27,7 @@ import java.util.Map; * @author 三刀 * @version V1.0 , 2020/11/20 */ -class ServletRequestDispatcherWrapper extends HttpServletRequestWrapper implements SmartHttpServletRequest { +public class ServletRequestDispatcherWrapper extends HttpServletRequestWrapper implements SmartHttpServletRequest { private final HttpServletRequestImpl request; private final DispatcherType dispatcherType; private final boolean named; @@ -39,7 +39,7 @@ class ServletRequestDispatcherWrapper extends HttpServletRequestWrapper implemen private int pathInfoStart; private int pathInfoEnd; private String requestUri; - private Map paramaters; + private Map parameters; public ServletRequestDispatcherWrapper(HttpServletRequestImpl request, DispatcherType dispatcherType, boolean named) { super(request); @@ -50,32 +50,32 @@ class ServletRequestDispatcherWrapper extends HttpServletRequestWrapper implemen @Override public String getParameter(String name) { - if (paramaters == null) { + if (parameters == null) { return null; } - String[] values = paramaters.get(name); + String[] values = parameters.get(name); return values == null || values.length == 0 ? null : values[0]; } @Override public Map getParameterMap() { - return paramaters == null ? Collections.emptyMap() : paramaters; + return parameters == null ? Collections.emptyMap() : parameters; } @Override public Enumeration getParameterNames() { - if (paramaters == null) { + if (parameters == null) { return null; } - return Collections.enumeration(paramaters.keySet()); + return Collections.enumeration(parameters.keySet()); } @Override public String[] getParameterValues(String name) { - if (paramaters == null) { + if (parameters == null) { return null; } - return paramaters.get(name); + return parameters.get(name); } @Override @@ -145,8 +145,8 @@ class ServletRequestDispatcherWrapper extends HttpServletRequestWrapper implemen this.servletPathEnd = end; } - public void setParamaters(Map paramaters) { - this.paramaters = paramaters; + public void setParameters(Map parameters) { + this.parameters = parameters; } @Override diff --git a/smart-servlet-maven-plugin/pom.xml b/smart-servlet-maven-plugin/pom.xml index 4f37cd70e58c897905982f5733bc84950a643524..526f54f2d27c150ccb1b60f13240a682a0c09c3f 100644 --- a/smart-servlet-maven-plugin/pom.xml +++ b/smart-servlet-maven-plugin/pom.xml @@ -15,7 +15,7 @@ smart-servlet-parent org.smartboot.servlet - 0.3 + 0.4 4.0.0 maven-plugin diff --git a/smart-servlet-maven-plugin/src/main/java/org/smartboot/maven/plugin/servlet/Starter.java b/smart-servlet-maven-plugin/src/main/java/org/smartboot/maven/plugin/servlet/Starter.java index 299115c5e494905bde280d48de5632017ec39159..2086bbfd4a225672649f7eaad31bbb5ceaae9cff 100644 --- a/smart-servlet-maven-plugin/src/main/java/org/smartboot/maven/plugin/servlet/Starter.java +++ b/smart-servlet-maven-plugin/src/main/java/org/smartboot/maven/plugin/servlet/Starter.java @@ -27,7 +27,7 @@ import java.io.IOException; */ public class Starter { - public Starter(String path, String contentPath, int port, ClassLoader classLoader) throws Exception { + public Starter(String path, String contentPath, int port, ClassLoader classLoader) throws Throwable { System.out.println("path: " + path); System.out.println("contentPath: " + contentPath); ContainerRuntime containerRuntime = new ContainerRuntime(); diff --git a/spring-boot-starter/pom.xml b/spring-boot-starter/pom.xml index b58207cd74acb24b8eb75cea58c8aa164036d9a8..cd62a3b03efc6d3eed1ccaf3be231dd1c8145771 100644 --- a/spring-boot-starter/pom.xml +++ b/spring-boot-starter/pom.xml @@ -15,7 +15,7 @@ smart-servlet-parent org.smartboot.servlet - 0.3 + 0.4 4.0.0 diff --git a/spring-boot-starter/src/main/java/org/smartboot/springboot/starter/ConfigurableSmartWebServerFactory.java b/spring-boot-starter/src/main/java/org/smartboot/springboot/starter/ConfigurableSmartWebServerFactory.java index 25f1525a8a72c43ab4b69a3e82ccaced5eea7a97..2f5d1472a8a5f8773708245a2bf983abf4d38d2e 100644 --- a/spring-boot-starter/src/main/java/org/smartboot/springboot/starter/ConfigurableSmartWebServerFactory.java +++ b/spring-boot-starter/src/main/java/org/smartboot/springboot/starter/ConfigurableSmartWebServerFactory.java @@ -38,7 +38,11 @@ public class ConfigurableSmartWebServerFactory extends AbstractServletWebServerF deployment.setClassLoader(getServletClassLoader()); deployment.setDisplayName(getDisplayName()); deployment.addServletContainerInitializer(initializer); - return new SmartServletServer(servletRuntime, getPort()); + try { + return new SmartServletServer(servletRuntime, getPort()); + } catch (Throwable e) { + throw new RuntimeException(e); + } } @Override diff --git a/spring-boot-starter/src/main/java/org/smartboot/springboot/starter/SmartServletServer.java b/spring-boot-starter/src/main/java/org/smartboot/springboot/starter/SmartServletServer.java index 00e8c6bf25e2897cd99a2df393d63fd6a519f3a8..0ca2860205fcef2622ea728202b8481fc6682fc0 100644 --- a/spring-boot-starter/src/main/java/org/smartboot/springboot/starter/SmartServletServer.java +++ b/spring-boot-starter/src/main/java/org/smartboot/springboot/starter/SmartServletServer.java @@ -36,7 +36,7 @@ public class SmartServletServer implements WebServer { private final int port; - public SmartServletServer(ServletContextRuntime runtime, int port) { + public SmartServletServer(ServletContextRuntime runtime, int port) throws Throwable { this.port = port; containerRuntime = new ContainerRuntime(); containerRuntime.addRuntime(runtime); diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 791c431920f1939fa0522e31db7cdab88a0dd5d8..651528e67356be4a962650e5011ad71dcaf645c2 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -47,7 +47,7 @@ org.smartboot.servlet servlet-core - 0.3 + 0.4 org.junit diff --git a/testsuite/src/main/java/org/smartboot/servlet/testsuite/JettyEmbeddedContainer.java b/testsuite/src/main/java/org/smartboot/servlet/testsuite/JettyEmbeddedContainer.java index 204ad637cf7018af5af42617989a0aa6f7acea1b..9a5ceb15288ed036873dab3fac0d28e789e6c47a 100644 --- a/testsuite/src/main/java/org/smartboot/servlet/testsuite/JettyEmbeddedContainer.java +++ b/testsuite/src/main/java/org/smartboot/servlet/testsuite/JettyEmbeddedContainer.java @@ -23,7 +23,13 @@ import org.jboss.arquillian.core.api.annotation.Inject; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.descriptor.api.Descriptor; import org.smartboot.http.common.utils.Mimetypes; -import org.smartboot.http.server.*; +import org.smartboot.http.server.HttpBootstrap; +import org.smartboot.http.server.HttpRequest; +import org.smartboot.http.server.HttpResponse; +import org.smartboot.http.server.HttpServerHandler; +import org.smartboot.http.server.WebSocketHandler; +import org.smartboot.http.server.WebSocketRequest; +import org.smartboot.http.server.WebSocketResponse; import org.smartboot.http.server.impl.WebSocketRequestImpl; import org.smartboot.http.server.impl.WebSocketResponseImpl; import org.smartboot.servlet.ContainerRuntime; @@ -103,7 +109,11 @@ public class JettyEmbeddedContainer implements DeployableContainer archive) throws DeploymentException { try { - ServletContextRuntime app = appProvider.createApp(containerRuntime,archive); + ServletContextRuntime app = appProvider.createApp(containerRuntime, archive); app.start(); @@ -163,7 +173,7 @@ public class JettyEmbeddedContainer implements DeployableContainer