ããã¯ããªã«ãããããŠæžãããã®ïŒ
åã«ãJakarta RESTful Web Servicesã§Server-Sent Eventsãè©ŠããŠã¿ãŸããã
WildFly 35(RESTEasy)でServer-Sent Events(SSE)を試す - CLOVER🍀
ãã®æã«RESTEasyã®å®è£
ãèŠãŠã¿ããšãJakarta ServletïŒä»¥éServletïŒã®éåæåŠçã䜿ã£ãŠããããšãããããŸããã
ããããã°Servletã®éåæåŠçã䜿ã£ãããšããªãã£ããªãšæã£ãã®ã§ãè©ŠããŠã¿ãããšã«ããŸããã
ä»åã¯WildFly 35.0.1.FinalãšApache Tomcat 10.1.35ã§ç¢ºèªããŠã¿ãããšæããŸãã
Jakarta Servletã®éåæåŠçã«é¢ããå
容ã¯ããã®ãããã«æžãããŠããŸãã
å°ããã€èŠãŠãããŸãããã
ãŸãã¯ãã¡ããããJakarta Servletã®ä»æ§æžã§ã¯ãããã§åããŠéåæåŠçãç»å ŽããŸããæèã¯ãªã¯ãšã¹ãã®åŠçã§ããã
Jakarta Servlet Specification / The Servlet Interface / Servlet Life Cycle / Request Handling / Asynchronous processing
éåæåŠçã䜿ããšããªã¯ãšã¹ããæ±ã£ãã¹ã¬ãããã³ã³ããã«æ»ããå¥ã®ã¹ã¬ããã§ã¬ã¹ãã³ã¹ãçæããããšãã§ããŸãã
éåæåŠçã§ã¯ãAsyncContext#complete
ãåŒã³åºããAsyncContext#dispatch
ã䜿ã£ãŠãã£ã¹ãããããŸãã
The asynchronous processing of requests is introduced to allow the thread to return to the container and perform other tasks. When asynchronous processing begins on the request, another thread or callback may either generate the response and call complete or dispatch the request so that it may run in the context of the container using the AsyncContext.dispatch method.
éåæåŠçã§ã®ã·ãŒã±ã³ã¹ã¯ä»¥äžã«ãªããŸãã
- ãªã¯ãšã¹ããåãä»ãããã£ã«ã¿ãŒãªã©ãéããåŸã«Servletã«æž¡ããã
- Servletã¯ãªã¯ãšã¹ãã®ãã©ã¡ãŒã¿ãŒãã³ã³ãã³ããåŠçããŠãªã¯ãšã¹ãã®æ§è³ªãå€æãã
- Servletã¯ãªãœãŒã¹ãŸãã¯ããŒã¿ãèŠæ±ãã
- ããšãã°ãªã¢ãŒãã®WebãµãŒãã¹ã®åŒã³åºããJDBCæ¥ç¶ãåŸ
æ©ãããã¥ãŒãªã©
- Servletã¯ã¬ã¹ãã³ã¹ãçæããã«ã¡ãœãããçµäºãã
- 3.ã§èŠæ±ããããªãœãŒã¹ãå©çšå¯èœã«ãªããšããã®ã€ãã³ããåŠçããã¹ã¬ããã¯åãã¹ã¬ããããŸãã¯
AsyncContext
ã䜿ã£ãŠã³ã³ãããžãã£ã¹ããããã
éåæåŠçãæ±ãã«ã¯ããã£ã«ã¿ãŒãServletã®ã¢ãããŒã·ã§ã³ã®asyncSupported
å±æ§ãŸãã¯web.xml
ã®async-supported
ã
true
ã«ãªã£ãŠããå¿
èŠããããŸããããã©ã«ãå€ã¯false
ã§ããtrue
ã«ãªã£ãŠããªãå Žåã¯ãéåæåŠçãéå§ã§ããŸããã
asyncSupported=true
ã®ServletããasyncSupported=false
ã®Servletãžã®ãã£ã¹ãããã¯èš±å¯ãããŸããããã®å Žåã¯
éåæããµããŒãããªãServletã®ã¡ãœãããçµäºããæç¹ã§ã¬ã¹ãã³ã¹ã¯ã³ããããããããšã«ãªããŸãã
éåæåŠçã®ã©ã€ããµã€ã¯ã«ã¯ãã¡ãã®å³ã«æžãããŠããŸãã
ServletRequest#startAsync
ãåŒã³åºãããšã§ãéåæåŠçãéå§ãç¶æ
ã¯AsyncStarted
ã«ãªã
AsyncContext#dispatch
ãåŒã³åºãããšã§ãå¥ã®Servletã«ãã£ã¹ãããã§ãã
- ãã ãã1åã®éåæãµã€ã¯ã«ïŒ
ServletRequest#startAsync
ã®åŒã³åºãïŒããšã«ãã£ã¹ãããã§ããåæ°ã¯æ倧1åã®æš¡æ§
AsyncContext#complete
ãåŒã³åºãããšã§ãç¶æ
ã¯Completed
ïŒãŸãã¯Completing
â Completed
ïŒã«ãªãã¬ã¹ãã³ã¹ã¯ã³ãããããã

éåæåŠçã¯ãServletRequest#startAsync
ãåŒã³åºãããšã§ååŸã§ããAsyncContext
ãäžå¿ã«æäœããŸãã
AsyncContext (Jakarta Servlet API documentation)
ãŸããéåæåŠçã®ã€ãã³ãã«å¯ŸããŠãªã¹ããŒïŒAsyncListener
ïŒãèšå®ããããšãã§ããŸãã
AsyncListener (Jakarta Servlet API documentation)
éåæåŠçãéå§ãããã©ããã¯ãServletRequest#isAsyncStarted
ã§å€æã§ããããã§ãã
ServletRequest (Jakarta Servlet API documentation)
ãã¡ãã¯ããªã¯ãšã¹ããè¡šããªããžã§ã¯ãã®ã©ã€ããµã€ã¯ã«ã«ã€ããŠæžãããŠããŸãã
Jakarta Servlet Specification / The Request / Lifetime of the Request Object
ãªã¯ãšã¹ããªããžã§ã¯ãã¯éåžžServletã®service
ã¡ãœããããŸãã¯ãã£ã«ã¿ãŒã®doFilter
ã®éã§æå¹ã§ãã
ServletRequest#startAsync
ã¡ãœãããåŒã³åºãéåæåŠçãéå§ããå Žåã¯ãAsyncContext#comlete
ãåŒã³åºããŸã§
æå¹ã«ãªããŸãã
ãã¡ãã«ã¯ã¬ã¹ãã³ã¹ãè¡šããªããžã§ã¯ãã®ã©ã€ããµã€ã¯ã«ã«ã€ããŠæžãããŠããŸããã¬ã¹ãã³ã¹ã¯ä»¥äžã®å Žåã«
ã¯ããŒãºãããŸãã
- Servletã®
service
ã¡ãœããã®çµäº
setContentLength
ãŸãã¯setContentLengthLong
ã§æå®ããããŒããã倧ããã³ã³ãã³ããã¬ã¹ãã³ã¹ã«æžã蟌ãŸãã
sendError
ã¡ãœãããåŒã³åºããã
sendRedirect
ã¡ãœãããåŒã³åºããã
AsyncContext#complete
ãåŒã³åºããã
Jakarta Servlet Specification / The Response / Lifetime of the Response Object
éåæåŠçãæ±ã£ãŠããå Žåã¯ãAsyncContext#complete
ãåŒã³åºããšãªã¯ãšã¹ããã¬ã¹ãã³ã¹ãã¯ããŒãºãããããš
èŠããŠããã°ããããã§ããã
Jakarta Servlet Specification / Filtering / Main Concepts / Filters and the RequestDispatcher
ãã¡ãã¯ãªã¯ãšã¹ãã®ãã£ã¹ãããã®è©±ã«ã€ããŠãéåæåŠçã䜿ã£ãŠããå Žåã¯ãAsycContext#dispatch
ã§ãªã¯ãšã¹ãã
ãã£ã¹ãããã§ããŸãã
Jakarta Servlet Specification / Dispatching Requests
AsyncContext#dispatch
ã«ã¯åŒæ°ã«ãã¹ãåããã®ãšåããªããã®ããããŸãããã¹ãæå®ããå Žåã¯ServletContext
ããã®
çžå¯Ÿãã¹ã«ãªã/
ããæå®ããŸãããã¹ãæå®ããªãå Žåã¯ãããšããšã®ãªã¯ãšã¹ããš
åããã¹ïŒHttpServletRequest#getRequestURI
ïŒã«å¯ŸããŠãã£ã¹ããããããŸãã
AsyncContext#complete
ãåŒã³åºããåŸã¯ãAsyncContext#dispatch
ãåŒã³åºãããšã¯ã§ããŸãããIllegalStateException
ã
ã¹ããŒãããŸãã
AsyncContext#dispatch
ã䜿çšããŠåŒã³åºããããµãŒãã¬ããã¯ãAsyncContext
ã®è»¢éå
ã«ãªã£ããã©ã¡ãŒã¿ãŒã«ä»¥äžã®
ãªã¯ãšã¹ãã®å±æ§ïŒServletRequest#getAttribute
ïŒã§ã¢ã¯ã»ã¹ã§ããŸãã
ããã¯AsyncContext
ã®å®æ°ãšããŠå®çŸ©ãããŠããŸããã
AsyncContext (Jakarta Servlet API documentation)
ãšã©ãŒãã³ããªã³ã°ã«ã€ããŠã
Jakarta Servlet Specification / Web Applications / Error Handling
Jakarta Servletã§ã¯ãšã©ãŒãçºçããæã«ã©ã®ãããªããŒãžã衚瀺ããããšãããšã©ãŒããŒãžãšããä»çµã¿ããããŸããã
éåæåŠçãæ±ã£ãŠããå Žåã¯ã¢ããªã±ãŒã·ã§ã³ã®è²¬ä»»ã§ãšã©ãŒããã³ããªã³ã°ããªããã°ãªããªãããšãæèšãããŠããŸãã
If the application is using asynchronous operations as described in Section 2.3.3.3, âAsynchronous processingâ, it is the applicationâs responsibility to handle all errors in application created threads. The container MAY take care of the errors from the thread issued via AsyncContext.start.
ãªã¯ãšã¹ãããã£ã¹ãããããå Žåã¯è©±ãå€ããããã§ãã
For handling errors that occur during AsyncContext.dispatch see dispatch error handling.
ã©ããåç
§ããããšãããšãéåæåŠçãç»å Žããæåã®ã»ã¯ã·ã§ã³ã«æ»ã£ãŠããŸãã
Any errors or exceptions that may occur during the execution of the dispatch methods MUST be caught and handled by the container as follows:
Jakarta Servlet Specification / The Servlet Interface / Servlet Life Cycle / Request Handling / Asynchronous processing
éåæåŠçããã£ããããããå
ã§ãšã©ãŒãçºçããå Žåã¯ãã³ã³ããã¯ä»¥äžã®åŠçãè¡ãããã§ãã
- ç»é²ããã
AsyncListener
ã®onError
ã¡ãœãããåŒã³åºã
AsyncContext#complete
ãAsyncContext#dispatch
ãåŒã³åºãããªãã£ãå Žåã¯ãã¹ããŒã¿ã¹ã³ãŒããHttpServletResponse.SC_INTERNAL_SERVER_ERROR
ãšãªããšã©ãŒãã£ã¹ããããå®è¡ããçºçããThrowable
ããªã¯ãšã¹ãã®å±æ§ã«RequestDispatcher.ERROR_EXCEPTION
ãšããŠèšå®ãã
- ã¹ããŒã¿ã¹ã³ãŒããäŸå€ã«å¯Ÿå¿ãããšã©ãŒããŒãžãèŠã€ãããªãå ŽåããŸãã¯
AsynContext#complete
ãAsyncContext#dispatch
ãåŒã³åºãããŠããªãå Žåã¯ãAsyncContext#complete
ãåŒã³åºã
æåŸã¯ã¢ããªã±ãŒã·ã§ã³ã©ã€ããµã€ã¯ã«ã€ãã³ãã§ãããªã¹ããŒã«é¢ãã話ã§ããã
Jakarta Servlet Specification / Application Lifecycle Events
ããã§ã¯AsyncListener
ã®çŽ¹ä»ã«çããããŠããŸãã
AsyncListener (Jakarta Servlet API documentation)
ãã¡ããèŠããšãAsyncListener
ã¯éåæåŠçã®éå§æããšã©ãŒæãã¿ã€ã ã¢ãŠãæãå®äºæã®4ã€ã®ã€ãã³ããæ±ããããã§ãã
Jakarta Servletã®ä»æ§æžã«æžãããŠããéåæåŠçã«é¢ããå
容ã¯ããããªãšããã§ããã
ããšã¯Jakarta EEã®ãã¥ãŒããªã¢ã«ãèªããšãããããããŸããã
Jakarta Servlet / Asynchronous Processing
ãšããããã§ãããã¥ã¡ã³ããèªãã®ã¯ãããããã«ããŠãŸãã¯ç°¡åã«äœ¿ã£ãŠã¿ãŸãããã
ç°å¢
ä»åã®ç°å¢ã¯ãã¡ãã
$ java --version
openjdk 21.0.6 2025-01-21
OpenJDK Runtime Environment (build 21.0.6+7-Ubuntu-124.04.1)
OpenJDK 64-Bit Server VM (build 21.0.6+7-Ubuntu-124.04.1, mixed mode, sharing)
$ mvn --version
Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 21.0.6, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "6.8.0-53-generic", arch: "amd64", family: "unix"
WildFlyã¯35.0.1.FinalãApache Tomcatã¯10.1.36ã䜿ããŸãã
æºå
ãŸãã¯Mavenãããžã§ã¯ãã®æºåãããŸãã
WildFlyãšApache Tomcatã®äž¡æ¹ã§åããã®ã§ãpom.xml
ã¯ãããªæãã«ããŸããã
pom.xml
xml version="1.0" encoding="UTF-8"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlnsxsi="http://www.w3.org/2001/XMLSchema-instance"
xsischemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.littlewings</groupId>
<artifactId>servlet-async-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<mavencompilerrelease>21</mavencompilerrelease>
<projectbuildsourceEncoding>UTF-8</projectbuildsourceEncoding>
<projectreportingoutputEncoding>UTF-8</projectreportingoutputEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-bom</artifactId>
<version>10.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.16</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<finalName>ROOT</finalName>
</build>
<profiles>
<profile>
<id>wildfly</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>5.1.2.Final</version>
<executions>
<execution>
<id>package</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
<configuration>
<overwrite-provisioned-server>true</overwrite-provisioned-server>
<discover-provisioning-info>
<version>35.0.1.Final</version>
<layers-for-jndi>
<layer>ee-concurrency</layer>
</layers-for-jndi>
</discover-provisioning-info>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>tomcat</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven3-plugin</artifactId>
<version>1.10.17</version>
<configuration>
<container>
<containerId>tomcat10x</containerId>
<zipUrlInstaller>
<url>https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.36/bin/apache-tomcat-10.1.36.tar.gz</url>
</zipUrlInstaller>
</container>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
WildFlyã¯WildFly Maven Pluginã䜿ã£ãŠããããžã§ãã³ã°ãApache Tomcatã¯Codehaus Cargo Maven 3 Pluginã䜿ã£ãŠ
ããŠã³ããŒãããããšã«ããŸãã
ããã©ã«ãã¯WildFlyã§ãApache Tomcatã䜿ãæã¯-P tomcat
ã§ãããã¡ã€ã«ãåãæ¿ããŸãã
WildFlyã¯ä»¥äžã®ã³ãã³ãã§ããããžã§ãã³ã°ãšèµ·åãè¡ããŸãã
$ mvn wildfly:run
Apache Tomcatã¯ä»¥äžã®ã³ãã³ãã§ããã±ãŒãžã³ã°ãšèµ·åãè¡ããŸãã
$ mvn -P tomcat package cargo:run
Jakarta Servletã®éåæåŠçã䜿ã£ãŠã¿ã
ããããå
ã¯ãJakarta Servletã®éåæåŠçãåºæ¬çãªäœ¿ãæ¹ãã€ããã€ãã®ããªãšãŒã·ã§ã³ã§è©ŠããŠã¿ãããšæããŸãã
ããªãšãŒã·ã§ã³ãšããã®ã¯ããéåæåŠçãæå¹ã«ããªããšåäœããªãããå«ã¿ãŸãã
éåžžã®Servletã§éåæåŠçã®APIãåŒã³åºã
ãŸãã¯ãéåžžã®Servletã§éåæåŠçã䜿ã£ãŠã¿ãŸãã
src/main/java/org/littlewings/servlet/async/SimpleServlet.java
package org.littlewings.servlet.async;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@WebServlet(urlPatterns = "/sync/simple")
public class SimpleServlet extends HttpServlet {
private Logger logger = LoggerFactory.getLogger(SimpleServlet.class);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync(request, response);
asyncContext.start(() -> {
try {
TimeUnit.SECONDS.sleep(1L);
HttpServletResponse res = (HttpServletResponse) asyncContext.getResponse();
PrintWriter writer = res.getWriter();
for (int i = 0; i < 5; i++) {
int counter = i + 1;
logger.info("[{}] in async{}", Thread.currentThread().getName(), counter);
writer.printf(
"%s [%s] in async%d%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")),
Thread.currentThread().getName(),
counter
);
writer.flush();
TimeUnit.SECONDS.sleep(1L);
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
} finally {
logger.info("[{}] complete async", Thread.currentThread().getName());
asyncContext.complete();
}
});
logger.info("[{}] dispatch async", Thread.currentThread().getName());
response.getWriter().printf(
"%s [%s] dispatch async%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")),
Thread.currentThread().getName()
);
response.getWriter().flush();
}
}
ã³ã¡ã³ãã«ãæžããŠããŸããããã®Servletã¯å®è¡ãããšéåæåŠçã®APIã䜿ã£ãŠããã«ãé¢ããã
@WebServlet
ã¢ãããŒã·ã§ã³ã®asyncSupported
å±æ§ãtrue
ã«ããŠããªãã®ã§å®è¡ã§ããŸããã
@WebServlet(urlPatterns = "/sync/simple")
public class SimpleServlet extends HttpServlet {
éåæåŠçã®APIã¯ãã®åŸã§ã䜿ãã®ã§ãããä»åã¯ããããã€ã³ãã§ããã
AsyncContext asyncContext = request.startAsync(request, response);
ã§ã¯ãå®è¡ãããšã©ããªãã確èªããŠã¿ãŸãããïŒWildFlyããã³Apache Tomcatã®èµ·åã³ãã³ãã¯çç¥ããŸãïŒã
$ curl localhost:8080/sync/simple
çµæã¯ãã©ã¡ããHTTPã¹ããŒã¿ã¹ã³ãŒã500ã«ãªããäŸå€ãæããŠå€±æããŸãã
WildFlyã®ãã°ã
11:07:49,392 ERROR [io.undertow.request] (default task-1) UT005023: Exception handling request to /sync/simple: java.lang.IllegalStateException: UT010026: Async is not supported for this request, as not all filters or Servlets were marked as supporting async
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.spec.HttpServletRequestImpl.startAsync(HttpServletRequestImpl.java:1096)
at deployment.ROOT.war//org.littlewings.servlet.async.SimpleServlet.doGet(SimpleServlet.java:25)
at jakarta.servlet.api@6.0.0//jakarta.servlet.http.HttpServlet.service(HttpServlet.java:527)
at jakarta.servlet.api@6.0.0//jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.security.elytron-web.undertow-server@4.1.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.lambda$handleRequest$1(ElytronRunAsHandler.java:68)
at org.wildfly.security.elytron-base@2.6.0.Final//org.wildfly.security.auth.server.FlexibleIdentityAssociation.runAsFunctionEx(FlexibleIdentityAssociation.java:103)
at org.wildfly.security.elytron-base@2.6.0.Final//org.wildfly.security.auth.server.Scoped.runAsFunctionEx(Scoped.java:161)
at org.wildfly.security.elytron-base@2.6.0.Final//org.wildfly.security.auth.server.Scoped.runAs(Scoped.java:73)
at org.wildfly.security.elytron-web.undertow-server@4.1.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.handleRequest(ElytronRunAsHandler.java:67)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.core@2.3.18.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.core@2.3.18.Final//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.core@2.3.18.Final//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at org.wildfly.security.elytron-web.undertow-server-servlet@4.1.0.Final//org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler.handleRequest(CleanUpHandler.java:38)
at io.undertow.core@2.3.18.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow@35.0.1.Final//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:44)
at io.undertow.core@2.3.18.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow@35.0.1.Final//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:51)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
at io.undertow.core@2.3.18.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:276)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:132)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow@35.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1421)
at org.wildfly.extension.undertow@35.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1421)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:256)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:101)
at io.undertow.core@2.3.18.Final//io.undertow.server.Connectors.executeRootHandler(Connectors.java:395)
at io.undertow.core@2.3.18.Final//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:861)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1348)
at org.jboss.xnio@3.8.16.Final//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282)
at java.base/java.lang.Thread.run(Thread.java:1583)
Apache Tomcatã®ãã°ã
[INFO] 2æ 24, 2025 11:09:30 åå org.apache.catalina.connector.Request startAsync
[INFO] èŠå: åŠçãã§ãŒã³å
ã®æ¬¡ã®ã¯ã©ã¹ãéåæããµããŒãããŠããªããããéåæãéå§ã§ããŸãã [org.littlewings.servlet.async.SimpleServlet]
[INFO] java.lang.IllegalStateException: çŸåšã®ãã§ãŒã³ã®ãã£ã«ã¿ãŸãã¯ãµãŒãã¬ããã¯éåææäœããµããŒãããŠããŸããã
[INFO] at org.apache.catalina.connector.Request.startAsync(Request.java:1510)
[INFO] at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:720)
[INFO] at org.littlewings.servlet.async.SimpleServlet.doGet(SimpleServlet.java:25)
[INFO] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
[INFO] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
[INFO] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
[INFO] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
[INFO] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
[INFO] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
[INFO] at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:663)
[INFO] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
[INFO] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
[INFO] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397)
[INFO] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
[INFO] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905)
[INFO] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743)
[INFO] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
[INFO] at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
[INFO] at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
[INFO] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
[INFO] at java.base/java.lang.Thread.run(Thread.java:1583)
[INFO]
[INFO] 2æ 24, 2025 11:09:30 åå org.apache.catalina.core.StandardWrapperValve invoke
[INFO] é倧: ãµãŒãã¬ãã [org.littlewings.servlet.async.SimpleServlet] ã®Servlet.service()ãäŸå€ãæããŸãã
[INFO] java.lang.IllegalStateException: çŸåšã®ãã§ãŒã³ã®ãã£ã«ã¿ãŸãã¯ãµãŒãã¬ããã¯éåææäœããµããŒãããŠããŸããã
[INFO] at org.apache.catalina.connector.Request.startAsync(Request.java:1510)
[INFO] at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:720)
[INFO] at org.littlewings.servlet.async.SimpleServlet.doGet(SimpleServlet.java:25)
[INFO] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
[INFO] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
[INFO] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
[INFO] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
[INFO] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
[INFO] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
[INFO] at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:663)
[INFO] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
[INFO] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
[INFO] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397)
[INFO] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
[INFO] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905)
[INFO] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743)
[INFO] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
[INFO] at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
[INFO] at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
[INFO] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
[INFO] at java.base/java.lang.Thread.run(Thread.java:1583)
[INFO]
asyncSupportedãæå¹ã«ããServletã䜿ã
次ã¯ãå
ã»ã©ãšã»ãŒåãã³ãŒãã§@WebServlet
ã¢ãããŒã·ã§ã³ã®asyncSupported
å±æ§ãtrue
ã«ããServletã§è©ŠããŠã¿ãŸãã
src/main/java/org/littlewings/servlet/async/SimpleAsyncServlet.java
package org.littlewings.servlet.async;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@WebServlet(urlPatterns = "/async/simple", asyncSupported = true)
public class SimpleAsyncServlet extends HttpServlet {
private Logger logger = LoggerFactory.getLogger(SimpleAsyncServlet.class);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync(request, response);
asyncContext.start(() -> {
try {
TimeUnit.SECONDS.sleep(1L);
HttpServletResponse res = (HttpServletResponse) asyncContext.getResponse();
PrintWriter writer = res.getWriter();
for (int i = 0; i < 5; i++) {
int counter = i + 1;
logger.info("[{}] in async{}", Thread.currentThread().getName(), counter);
writer.printf(
"%s [%s] in async%d%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")),
Thread.currentThread().getName(),
counter
);
writer.flush();
TimeUnit.SECONDS.sleep(1L);
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
} finally {
logger.info("[{}] complete async", Thread.currentThread().getName());
asyncContext.complete();
}
});
logger.info("[{}] dispatch async", Thread.currentThread().getName());
response.getWriter().printf(
"%s [%s] dispatch async%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")),
Thread.currentThread().getName()
);
response.getWriter().flush();
}
}
ä»åã¯ãéåæåŠçã®APIã«ã€ããŠå°ã觊ããŸãããã
ServletRequest#startAsync
ã䜿ã£ãŠãAsyncContext
ãååŸããããšã§éåæåŠçãå®è£
ã§ããããã«ãªããŸãã
AsyncContext asyncContext = request.startAsync(request, response);
å
ã»ã©ã¯asyncSupport
ãæå¹ã«ããŠããªãã£ãã®ã§ããã®åŒã³åºãã«å€±æããŸããã
ServletRequest#startAsync
ã«ã¯åŒæ°ãåããªãããŒãžã§ã³ãããã®ã§ããããã®å Žåã¯ãªãªãžãã«ã®ServletRequest
ãš
ServletResponse
ãæå®ãããããšã«ãªãã¿ããã§ãããã€ãŸããä»åã®å®è£
ã ãšå·®ããããŸããã
ServletRequestWrapper
ãServletResponseWrapper
ã䜿ã£ãããããšãå·®ãåºãã§ããããã
éåæåŠçã1çªç°¡åã«è¡ãã«ã¯ãAsyncContext#start
ã«Runnable
ãæž¡ããšå®è£
ã§ããŸãã
asyncContext.start(() -> {
ãã®äžã®åŠçã¯ãå¥ã¹ã¬ããã§è¡ãããŸãã
ServletRequest
ãšServletResponse
ã¯ãAsyncContext
ããååŸããã®ãããã§ãããã
â»ä»åã¯ServletResponse
ãã䜿ã£ãŠããŸãããâŠ
HttpServletResponse res = (HttpServletResponse) asyncContext.getResponse();
PrintWriter writer = res.getWriter();
ããšã¯ã¹ãªãŒããå
¥ãã€ã€ã§ãããã¬ã¹ãã³ã¹ãæžãåºããŠ
for (int i = 0; i < 5; i++) {
int counter = i + 1;
logger.info("[{}] in async{}", Thread.currentThread().getName(), counter);
writer.printf(
"%s [%s] in async%d%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")),
Thread.currentThread().getName(),
counter
);
writer.flush();
TimeUnit.SECONDS.sleep(1L);
æåŸã«AsyncContext#complete
ãåŒã³åºããŠéåæåŠçãå®äºããŠããŸãã
} finally {
logger.info("[{}] complete async", Thread.currentThread().getName());
asyncContext.complete();
}
ããšãServletããªã¯ãšã¹ããåãä»ããã¹ã¬ããã§ãServletResponse
ãæäœããŠããŸãããæ¬æ¥ã¯ã¹ã¬ããã»ãŒãã§ã¯
ãªãã¯ããªã®ã§ããããã®ã¯Jakarta Servletã®å®è£
ã§ã¯ãããªãããã«æ³šæãããŠããŸãã
logger.info("[{}] dispatch async", Thread.currentThread().getName());
response.getWriter().printf(
"%s [%s] dispatch async%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")),
Thread.currentThread().getName()
);
response.getWriter().flush();
ä»åã¯ãåããèŠãé¢ä¿äžã¡ãã£ãšå
¥ããŠããŸãã
éæã«ã¹ã¬ããåããããããã«ãã°ãã¬ã¹ãã³ã¹ã®å
容ã«å«ããããã«ããŠããŸãã
ã§ã¯ã確èªããŠã¿ãŸãããã
$ curl localhost:8080/async/simple
WildFlyã
ãŸãã¯ã¬ã¹ãã³ã¹ã
2025-02-24 11:24:57 [default task-1] dispatch async
2025-02-24 11:24:58 [default task-2] in async1
2025-02-24 11:24:59 [default task-2] in async2
2025-02-24 11:25:00 [default task-2] in async3
2025-02-24 11:25:01 [default task-2] in async4
2025-02-24 11:25:02 [default task-2] in async5
ãã°ã
11:24:57,117 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-1) [default task-1] dispatch async
11:24:58,117 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async1
11:24:59,120 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async2
11:25:00,122 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async3
11:25:01,123 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async4
11:25:02,125 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async5
11:25:03,127 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] complete async
確ãã«å¥ã¹ã¬ããã«ãªã£ãŠããã®ã§ãããããã¯ã¯ãŒã«ãŒã¹ã¬ããã䜿ã£ãŠãããããªæ°ãããŸãã
次ã¯Apache Tomcatã
ã¬ã¹ãã³ã¹ã
2025-02-24 11:25:58 [http-nio-8080-exec-3] dispatch async
2025-02-24 11:25:59 [http-nio-8080-exec-4] in async1
2025-02-24 11:26:00 [http-nio-8080-exec-4] in async2
2025-02-24 11:26:01 [http-nio-8080-exec-4] in async3
2025-02-24 11:26:02 [http-nio-8080-exec-4] in async4
2025-02-24 11:26:03 [http-nio-8080-exec-4] in async5
ãã°ã
[INFO] [http-nio-8080-exec-3] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-3] dispatch async
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async1
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async2
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async3
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async4
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async5
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] complete async
ãã¡ããåãã§ããã
ãšããããã§ãAsyncContext#start
ã䜿ããšéåæåŠçãå®è£
ã§ããããªãã®ã®ãåãæ¿ãå
ã®ã¹ã¬ããã¯HTTPãªã¯ãšã¹ãã
æ±ããã®ãšåããã®ã䜿ãããå®è£
ãããããã§ããã
ãããªããšéåæåŠçãè¡ã£ãŠããéã¯æ±ããHTTPãªã¯ãšã¹ãæ°ãæžããšããããšã«ãªãã®ã§ããããå«ãªå Žåã¯
å¥ã«ã¹ã¬ããããŒã«ã䜿ããšããããšã«ãªãããã§ãã
éåžžã®Filterãè¿œå ããŠã¿ã
ã¹ã¬ããããŒã«ã䜿ãåã«ãFilterãè¿œå ããŠã¿ãŸãããã
ããããFilterãè¿œå ããã¡ãããéåæåŠçãæå¹ã«ããServletã«å¹æãããããã«èšå®ããŸãã
src/main/java/org/littlewings/servlet/async/SimpleFilter.java
package org.littlewings.servlet.async;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@WebFilter(urlPatterns = "/*")
public class SimpleFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(SimpleFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
logger.info("do filter@{}", getClass().getSimpleName());
chain.doFilter(request, response);
}
}
ã³ã¡ã³ãã«ãæžããŠããŸããã@WebFilter
ã¢ãããŒã·ã§ã³ã®asyncSupported
å±æ§ãtrue
ã«ãªã£ãŠããã®ã§ã
ãã®Filterãé©çšãããåŸã«ã¯éåæåŠçã¯äœ¿ããŸããã
@WebFilter(urlPatterns = "/*")
è©ŠããŠã¿ãŸããã¢ã¯ã»ã¹ããURLã¯ãå
ã»ã©ã®éåæåŠçãæå¹ã«ããServletã§ãã
$ curl localhost:8080/async/simple
WildFlyã
11:30:29,143 INFO [org.littlewings.servlet.async.SimpleFilter] (default task-1) do filter@SimpleFilter
11:30:29,145 ERROR [io.undertow.request] (default task-1) UT005023: Exception handling request to /async/simple: java.lang.IllegalStateException: UT010026: Async is not supported for this request, as not all filters or Servlets were marked as supporting async
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.spec.HttpServletRequestImpl.startAsync(HttpServletRequestImpl.java:1096)
at deployment.ROOT.war//org.littlewings.servlet.async.SimpleAsyncServlet.doGet(SimpleAsyncServlet.java:24)
at jakarta.servlet.api@6.0.0//jakarta.servlet.http.HttpServlet.service(HttpServlet.java:527)
at jakarta.servlet.api@6.0.0//jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at deployment.ROOT.war//org.littlewings.servlet.async.SimpleFilter.doFilter(SimpleFilter.java:23)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.security.elytron-web.undertow-server@4.1.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.lambda$handleRequest$1(ElytronRunAsHandler.java:68)
at org.wildfly.security.elytron-base@2.6.0.Final//org.wildfly.security.auth.server.FlexibleIdentityAssociation.runAsFunctionEx(FlexibleIdentityAssociation.java:103)
at org.wildfly.security.elytron-base@2.6.0.Final//org.wildfly.security.auth.server.Scoped.runAsFunctionEx(Scoped.java:161)
at org.wildfly.security.elytron-base@2.6.0.Final//org.wildfly.security.auth.server.Scoped.runAs(Scoped.java:73)
at org.wildfly.security.elytron-web.undertow-server@4.1.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.handleRequest(ElytronRunAsHandler.java:67)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.core@2.3.18.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.core@2.3.18.Final//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.core@2.3.18.Final//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at org.wildfly.security.elytron-web.undertow-server-servlet@4.1.0.Final//org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler.handleRequest(CleanUpHandler.java:38)
at io.undertow.core@2.3.18.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow@35.0.1.Final//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:44)
at io.undertow.core@2.3.18.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow@35.0.1.Final//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:51)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
at io.undertow.core@2.3.18.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:276)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:132)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow@35.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1421)
at org.wildfly.extension.undertow@35.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1421)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:256)
at io.undertow.servlet@2.3.18.Final//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:101)
at io.undertow.core@2.3.18.Final//io.undertow.server.Connectors.executeRootHandler(Connectors.java:395)
at io.undertow.core@2.3.18.Final//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:861)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1348)
at org.jboss.xnio@3.8.16.Final//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282)
at java.base/java.lang.Thread.run(Thread.java:1583)
å
ã»ã©ã¯æåããŠãããServletRequest#startAsync
ã®åŒã³åºãã倱æããããã«ãªããŸãã
ã¡ãªã¿ã«ãFilterèªäœã¯åããŠããŸãã
11:30:29,143 INFO [org.littlewings.servlet.async.SimpleFilter] (default task-1) do filter@SimpleFilter
Apache Tomcatã
[INFO] [http-nio-8080-exec-3] INFO org.littlewings.servlet.async.SimpleFilter - do filter@SimpleFilter
[INFO] 2æ 24, 2025 11:32:47 åå org.apache.catalina.connector.Request startAsync
[INFO] èŠå: åŠçãã§ãŒã³å
ã®æ¬¡ã®ã¯ã©ã¹ãéåæããµããŒãããŠããªããããéåæãéå§ã§ããŸãã [org.littlewings.servlet.async.SimpleFilter]
[INFO] java.lang.IllegalStateException: çŸåšã®ãã§ãŒã³ã®ãã£ã«ã¿ãŸãã¯ãµãŒãã¬ããã¯éåææäœããµããŒãããŠããŸããã
[INFO] at org.apache.catalina.connector.Request.startAsync(Request.java:1510)
[INFO] at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:720)
[INFO] at org.littlewings.servlet.async.SimpleAsyncServlet.doGet(SimpleAsyncServlet.java:24)
[INFO] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
[INFO] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.littlewings.servlet.async.SimpleFilter.doFilter(SimpleFilter.java:23)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
[INFO] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
[INFO] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
[INFO] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
[INFO] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
[INFO] at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:663)
[INFO] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
[INFO] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
[INFO] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397)
[INFO] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
[INFO] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905)
[INFO] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743)
[INFO] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
[INFO] at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
[INFO] at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
[INFO] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
[INFO] at java.base/java.lang.Thread.run(Thread.java:1583)
[INFO]
[INFO] 2æ 24, 2025 11:32:47 åå org.apache.catalina.core.StandardWrapperValve invoke
[INFO] é倧: ãµãŒãã¬ãã [org.littlewings.servlet.async.SimpleAsyncServlet] ã®Servlet.service()ãäŸå€ãæããŸãã
[INFO] java.lang.IllegalStateException: çŸåšã®ãã§ãŒã³ã®ãã£ã«ã¿ãŸãã¯ãµãŒãã¬ããã¯éåææäœããµããŒãããŠããŸããã
[INFO] at org.apache.catalina.connector.Request.startAsync(Request.java:1510)
[INFO] at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:720)
[INFO] at org.littlewings.servlet.async.SimpleAsyncServlet.doGet(SimpleAsyncServlet.java:24)
[INFO] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
[INFO] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.littlewings.servlet.async.SimpleFilter.doFilter(SimpleFilter.java:23)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
[INFO] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
[INFO] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
[INFO] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
[INFO] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
[INFO] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
[INFO] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
[INFO] at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:663)
[INFO] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
[INFO] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
[INFO] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397)
[INFO] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
[INFO] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905)
[INFO] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743)
[INFO] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
[INFO] at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
[INFO] at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
[INFO] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
[INFO] at java.base/java.lang.Thread.run(Thread.java:1583)
[INFO]
ãã¡ããçµæã¯åãã§ãã
äž¡æ¹ãšãHTTPã¹ããŒã¿ã¹ã³ãŒã500ã«ãªããŸãã
ãšããããã§ããã®Filterã¯ããã§ç¡å¹ã«ããŠãããŸãã
public class SimpleFilter implements Filter {
asyncSupportedãæå¹ã«ããFilterã䜿ã
ã§ã¯ã次ã¯@WebFilter
ã®asyncSupported
ãtrue
ã«ããFilterãé©çšããŠã¿ãŸãã
src/main/java/org/littlewings/servlet/async/SimpleAsyncFilter.java
package org.littlewings.servlet.async;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@WebFilter(urlPatterns = "/*", asyncSupported = true)
public class SimpleAsyncFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(SimpleAsyncFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
logger.info("do filter@{}", getClass().getSimpleName());
chain.doFilter(request, response);
}
}
確èªã
$ curl localhost:8080/async/simple
WildFlyã
ãã°ã
11:35:54,639 INFO [org.littlewings.servlet.async.SimpleAsyncFilter] (default task-1) do filter@SimpleAsyncFilter
11:35:54,640 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-1) [default task-1] dispatch async
11:35:55,641 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async1
11:35:56,644 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async2
11:35:57,646 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async3
11:35:58,647 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async4
11:35:59,649 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] in async5
11:36:00,651 INFO [org.littlewings.servlet.async.SimpleAsyncServlet] (default task-2) [default task-2] complete async
FilterãåäœããåŸã«ãéåæåŠçãåãããã«ãªããŸããã
ã¬ã¹ãã³ã¹ã¯ãªã«ãå€ãããªãã®ã§çç¥ããŸãã
Apache Tomcatã
[INFO] [http-nio-8080-exec-3] INFO org.littlewings.servlet.async.SimpleAsyncFilter - do filter@SimpleAsyncFilter
[INFO] [http-nio-8080-exec-3] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-3] dispatch async
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async1
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async2
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async3
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async4
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] in async5
[INFO] [http-nio-8080-exec-4] INFO org.littlewings.servlet.async.SimpleAsyncServlet - [http-nio-8080-exec-4] complete async
ãã¡ããOKã§ããã
ãã®Filterã¯é©çšãããŸãŸã«ããŸãã
ã¹ã¬ããããŒã«ã䜿ã£ãŠéåæåŠçãè¡ã
æåŸã¯ãã¹ã¬ããããŒã«ã䜿ããŸããå
ã»ã©ã¯éåæåŠçã䜿ã£ãŠã¿ããã®ã®ãAsyncContext#start
ã§ã¯ãªã¯ãšã¹ããæ±ã
ã¹ã¬ãããšåãã¹ã¬ããããŒã«ã®ãã®ã䜿ãããŠãããã ãšãã話ã§ããã
ä»åã¯ãã®ããã«å€æŽã
src/main/java/org/littlewings/servlet/async/SimpleAsyncUseThreadServlet.java
package org.littlewings.servlet.async;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@WebServlet(urlPatterns = "/async/thread", asyncSupported = true)
public class SimpleAsyncUseThreadServlet extends HttpServlet {
private Logger logger = LoggerFactory.getLogger(SimpleAsyncUseThreadServlet.class);
private ExecutorService executorService;
@PostConstruct
void postConstruct() {
try {
executorService = InitialContext.doLookup("java:comp/DefaultManagedExecutorService");
} catch (NamingException e) {
executorService = Executors.newFixedThreadPool(10);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync(request, response);
executorService.execute(() -> {
try {
TimeUnit.SECONDS.sleep(1L);
HttpServletResponse res = (HttpServletResponse) asyncContext.getResponse();
PrintWriter writer = res.getWriter();
for (int i = 0; i < 5; i++) {
int counter = i + 1;
logger.info("[{}] in async{}", Thread.currentThread().getName(), counter);
writer.printf(
"%s [%s] in async%d%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")),
Thread.currentThread().getName(),
counter
);
writer.flush();
TimeUnit.SECONDS.sleep(1L);
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
} finally {
logger.info("[{}] complete async", Thread.currentThread().getName());
asyncContext.complete();
}
});
logger.info("[{}] dispatch async", Thread.currentThread().getName());
response.getWriter().printf(
"%s [%s] dispatch async%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")),
Thread.currentThread().getName()
);
response.getWriter().flush();
}
}
ã¹ã¬ããããŒã«ã¯ãWildFlyã®å Žåã¯Jakarta Concurrentyã®ManagedExecutorService
ãApache Tomcatã®å Žåã¯
Executors#newFixedThreadPool
ã䜿ãããšã«ããŸãã
private ExecutorService executorService;
@PostConstruct
void postConstruct() {
try {
executorService = InitialContext.doLookup("java:comp/DefaultManagedExecutorService");
} catch (NamingException e) {
executorService = Executors.newFixedThreadPool(10);
}
}
ããããã³ãŒãã ãšãWildFly Glowã§ã¯ã¬ã€ã€ãŒãæ€åºã§ããªãã®ã§ee-concurrency
ã¬ã€ã€ãŒãæ瀺çã«è¿œå ããŠããŸãã
<configuration>
<overwrite-provisioned-server>true</overwrite-provisioned-server>
<discover-provisioning-info>
<version>35.0.1.Final</version>
<layers-for-jndi>
<layer>ee-concurrency</layer>
</layers-for-jndi>
</discover-provisioning-info>
</configuration>
å€æŽç¹ã¯ãAsyncContext#start
ã¯äœ¿ããã«ExecutorService#execute
ã«ä»»ããããã«ããã ãã§ãã
executorService.execute(() -> {
try {
TimeUnit.SECONDS.sleep(1L);
HttpServletResponse res = (HttpServletResponse) asyncContext.getResponse();
PrintWriter writer = res.getWriter();
for (int i = 0; i < 5; i++) {
int counter = i + 1;
logger.info("[{}] in async{}", Thread.currentThread().getName(), counter);
writer.printf(
"%s [%s] in async%d%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss")),
Thread.currentThread().getName(),
counter
);
writer.flush();
TimeUnit.SECONDS.sleep(1L);
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
} finally {
logger.info("[{}] complete async", Thread.currentThread().getName());
asyncContext.complete();
}
});
確èªããŠã¿ãŸãã
$ curl localhost:8080/async/thread
WildFlyã
ã¬ã¹ãã³ã¹ã
2025-02-24 11:44:12 [default task-1] dispatch async
2025-02-24 11:44:13 [EE-ManagedExecutorService-default-Thread-1] in async1
2025-02-24 11:44:14 [EE-ManagedExecutorService-default-Thread-1] in async2
2025-02-24 11:44:15 [EE-ManagedExecutorService-default-Thread-1] in async3
2025-02-24 11:44:16 [EE-ManagedExecutorService-default-Thread-1] in async4
2025-02-24 11:44:17 [EE-ManagedExecutorService-default-Thread-1] in async5
ãã°ã
11:44:12,691 INFO [org.littlewings.servlet.async.SimpleAsyncFilter] (default task-1) do filter@SimpleAsyncFilter
11:44:12,695 INFO [org.littlewings.servlet.async.SimpleAsyncUseThreadServlet] (default task-1) [default task-1] dispatch async
11:44:13,697 INFO [org.littlewings.servlet.async.SimpleAsyncUseThreadServlet] (EE-ManagedExecutorService-default-Thread-1) [EE-ManagedExecutorService-default-Thread-1] in async1
11:44:14,699 INFO [org.littlewings.servlet.async.SimpleAsyncUseThreadServlet] (EE-ManagedExecutorService-default-Thread-1) [EE-ManagedExecutorService-default-Thread-1] in async2
11:44:15,701 INFO [org.littlewings.servlet.async.SimpleAsyncUseThreadServlet] (EE-ManagedExecutorService-default-Thread-1) [EE-ManagedExecutorService-default-Thread-1] in async3
11:44:16,703 INFO [org.littlewings.servlet.async.SimpleAsyncUseThreadServlet] (EE-ManagedExecutorService-default-Thread-1) [EE-ManagedExecutorService-default-Thread-1] in async4
11:44:17,706 INFO [org.littlewings.servlet.async.SimpleAsyncUseThreadServlet] (EE-ManagedExecutorService-default-Thread-1) [EE-ManagedExecutorService-default-Thread-1] in async5
11:44:18,707 INFO [org.littlewings.servlet.async.SimpleAsyncUseThreadServlet] (EE-ManagedExecutorService-default-Thread-1) [EE-ManagedExecutorService-default-Thread-1] complete async
åœç¶ãšããã°åœç¶ã§ããã䜿ãããã¹ã¬ãããå€ãããŸããã
Apache Tomcatã
ã¬ã¹ãã³ã¹ã
2025-02-24 11:45:41 [http-nio-8080-exec-3] dispatch async
2025-02-24 11:45:42 [pool-1-thread-1] in async1
2025-02-24 11:45:43 [pool-1-thread-1] in async2
2025-02-24 11:45:44 [pool-1-thread-1] in async3
2025-02-24 11:45:45 [pool-1-thread-1] in async4
2025-02-24 11:45:46 [pool-1-thread-1] in async5
ãã°ã
[INFO] [http-nio-8080-exec-3] INFO org.littlewings.servlet.async.SimpleAsyncFilter - do filter@SimpleAsyncFilter
[INFO] [http-nio-8080-exec-3] INFO org.littlewings.servlet.async.SimpleAsyncUseThreadServlet - [http-nio-8080-exec-3] dispatch async
[INFO] [pool-1-thread-1] INFO org.littlewings.servlet.async.SimpleAsyncUseThreadServlet - [pool-1-thread-1] in async1
[INFO] [pool-1-thread-1] INFO org.littlewings.servlet.async.SimpleAsyncUseThreadServlet - [pool-1-thread-1] in async2
[INFO] [pool-1-thread-1] INFO org.littlewings.servlet.async.SimpleAsyncUseThreadServlet - [pool-1-thread-1] in async3
[INFO] [pool-1-thread-1] INFO org.littlewings.servlet.async.SimpleAsyncUseThreadServlet - [pool-1-thread-1] in async4
[INFO] [pool-1-thread-1] INFO org.littlewings.servlet.async.SimpleAsyncUseThreadServlet - [pool-1-thread-1] in async5
[INFO] [pool-1-thread-1] INFO org.littlewings.servlet.async.SimpleAsyncUseThreadServlet - [pool-1-thread-1] complete async
ãã¡ããåãã§ãã
ä»åã®ç¢ºèªç¯å²ã¯ãããŸã§ã«ããŸãã
AsyncContext#startã§äœ¿ãããã¹ã¬ããã®å®äœã¯ïŒ
ãšããã§ãAsyncContext#start
ã§ã¯Servletã®ãªã¯ãšã¹ããåŠçããã®ãšåãçš®é¡ã®ã¹ã¬ããã䜿ãããŠããããã«èŠããŸããã
æ¬åœã«ãããªã®ããã³ãŒãã§ç¢ºèªããŠã¿ãããšæããŸãã
Undertowã®å Žå
Undertowã®å Žåã¯ããã®ãããã«å®è£
ãããŠããŸãã
以äžã®é ã§éžã¶ããã§ãã
Deployment
ã«éåæçšã®Executor
ãèšå®ãããŠããã°ããã䜿ã
Deployment
ã«Executor
ãèšå®ãããŠããã°ããã䜿ã
- ã¯ãŒã«ãŒã¹ã¬ããïŒServletã®ãªã¯ãšã¹ããåŠçããã®ãšåãã¹ã¬ããããŒã«ïŒã䜿ã
https://github.com/undertow-io/undertow/blob/2.3.18.Final/servlet/src/main/java/io/undertow/servlet/spec/AsyncContextImpl.java#L283-L293
https://github.com/undertow-io/undertow/blob/2.3.18.Final/servlet/src/main/java/io/undertow/servlet/spec/AsyncContextImpl.java#L295-L304
ã§ãWildFlyã§èŠããšããã¯ãŒã«ãŒã¹ã¬ããã䜿ãããŠããŸããã
ã¡ãªã¿ã«ãã¹ã¬ãããèªåã§æ±ã£ãå Žåã§ãAsyncContext#complete
ãåŒã³åºããæã«ã¯å
éšçã«ãã®åŠçãåŒã³åºããã
ããšã«ãªãããã®æã¯ã¯ãŒã«ãŒã¹ã¬ããã䜿ãããããšã«ãªããŸãã
https://github.com/undertow-io/undertow/blob/2.3.18.Final/servlet/src/main/java/io/undertow/servlet/spec/AsyncContextImpl.java#L247
https://github.com/undertow-io/undertow/blob/2.3.18.Final/servlet/src/main/java/io/undertow/servlet/spec/AsyncContextImpl.java#L273-L278
https://github.com/undertow-io/undertow/blob/2.3.18.Final/servlet/src/main/java/io/undertow/servlet/spec/AsyncContextImpl.java#L431-L437
https://github.com/undertow-io/undertow/blob/2.3.18.Final/servlet/src/main/java/io/undertow/servlet/spec/AsyncContextImpl.java#L539
https://github.com/undertow-io/undertow/blob/2.3.18.Final/servlet/src/main/java/io/undertow/servlet/spec/AsyncContextImpl.java#L520
ãã£ã¹ããããè¡ã£ãŠãããã©ããã§æåãéããããªã®ã§ããã£ã¹ããããè¡ããã¿ãŒã³ã¯ãŸã確èªãããã§ããã
Apache Tomcatã§ã®AsyncContext#start
ã®å®è£
ãèŠãŠã¿ãŸãããããã®ãããã§ããã
https://github.com/apache/tomcat/blob/10.1.36/java/org/apache/catalina/core/AsyncContextImpl.java#L234
https://github.com/apache/tomcat/blob/10.1.36/java/org/apache/coyote/AbstractProcessor.java#L559-L561
https://github.com/apache/tomcat/blob/10.1.36/java/org/apache/coyote/AsyncStateMachine.java#L475
ããã§ã®AbstractProcessor
ã®å®è£
ã¯Http11Processor
ãAjpProcessor
ãªã©ãæããŸãã
ã€ãŸããConnectorãšåãã¹ã¬ããããã£ã±ã䜿ãããããã§ããã
AsyncContext#complete
ãåŒã³åºããæã¯ãåŒã³åºãå
ã®ã¹ã¬ãããšåããã®ã䜿ãããŠããŸããã
ãã®ããããå®è£
ãããŠããAsyncStateMachine
ã«ã¯ã以äžã®ããã«éåæåŠçã®ç¶æ
é·ç§»ã«é¢ããã³ã¡ã³ãããã£ãããš
æžããŠãããŸããã
Manages the state transitions for async requests.
<pre>
</pre>
class AsyncStateMachine {
https://github.com/apache/tomcat/blob/10.1.36/java/org/apache/coyote/AsyncStateMachine.java#L30-L129
ãããã«
Jakarta Servletã®éåæåŠçãWildFly 35.0.1.Final.ãApache Tomcat 10.1.36ã§è©ŠããŠã¿ãŸããã
ã»ãŒè§Šããããšããªãã£ãã®ãšãJakarta Servletã®ä»æ§æžããããããšèªãããšã«ãªã£ãã®ã§å匷ã«ãªããŸããã
ãã£ã¹ããããŸããã¯ä»åã¯èŠããªãã£ãã®ã§ããŸãã®æ©äŒã«èŠãŠã¿ããããªãšæããŸãã