Let us start this chapter with a new set up and configuration for the new ch08 project and with the following steps:
- Add in its pom.xml and all the needed Maven core libraries and dependencies such as the Spring 5, Servlet 3.1, JSP 2.3.1, JSTL 1.2, MySQL Connector 5.1.x, HikariCP 2.5.x, and Log4J 1.2.
- Just like in the previous chapters, create similar empty classes, namely SpringWebinitializer, SpringWebinitializer, SpringContextConfig, and SpringDbConfig. Store them in their respective packages and configure them according to what's been done previously.
- To enable asynchronous request transactions with callbacks, the Spring 5 platform must utilize at least Servlet 3.1 container since this version supports multithreading in web applications. Open SpringWebinitializer and enable <async-supported/> in a JavaConfig manner:
@EnableWebMvc @ComponentScan(basePackages = "org.packt.reactive.codes") @Configuration public class SpringWebinitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext container) throws ServletException { addRootContext(container); addDispatcherContext(container); } // refer to sources private void addDispatcherContext(ServletContext container) { AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext(); dispatcherContext.register( SpringDispatcherConfig.class); ServletRegistration.Dynamic dispatcher = container.addServlet("ch08-servlet", new DispatcherServlet(dispatcherContext)); dispatcher.addMapping("/"); dispatcher.setLoadOnStartup(1); dispatcher.setAsyncSupported(true); } }
The entire book officially uses Apache Tomcat 9, which generates its threads from the thread pool.
- Since we will be generating executor services that will generate threads later, we need to configure server.xml to set the maximum and minimum number of threads in the Tomcat container. Most importantly, there is a need to change our Tomcat's Java connector to org.apache.coyote.http11.Http11NioProtocol for optimal server performance and management of multiple threads during context root execution:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" minProcessors="3" maxProcessors="10" maxThreads="1000" SSLEnabled="true"> <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" /> <SSLHostConfig honorCipherOrder="false"> <Certificate certificateKeyFile="conf/spring5packt.key" certificateFile="conf/spring5packt.crt" keyAlias="spring5server" type="RSA" /> </SSLHostConfig> </Connector>
- Afterwards, configure the thread generation process for the application. Create the context configuration below that implements org.springframework.scheduling.annotation.AsyncConfigurer responsible for injecting org.springframework.core.task.TaskExecutor into the Spring container. The context definition must have a class level annotation @EnableAsync to trigger asynchronous processing anywhere in the platform:
import java.util.concurrent.Executor; import java.util.concurrent.Executors; @EnableAsync @Configuration @ComponentScan(basePackages = {"org.packt.web.reactor"}) public class SpringAsynchConfig implements AsyncConfigurer { /* @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(9); executor.setQueueCapacity(50); executor.setThreadNamePrefix("Ch08Executor-"); executor.setWaitForTasksToCompleteOnShutdown(true); executor.setKeepAliveSeconds(5000); executor.setAwaitTerminationSeconds(1000); executor.initialize(); return executor; } */ /* @Override public Executor getAsyncExecutor () { SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor(); executor.setConcurrencyLimit(100); return executor; } */ @Override public Executor getAsyncExecutor () { ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor( Executors.newFixedThreadPool(100)); executor.setTaskDecorator(new TaskDecorator() { @Override public Runnable decorate (Runnable runnable) { return () -> { long t = System.currentTimeMillis(); runnable.run(); System.out.printf("Thread %s has a processing time: %s%n", Thread.currentThread().getName(), (System.currentTimeMillis() - t)); }; } }); return executor; } }
Preceding there are three executor types, but this recipe uses ConcurrentTaskExecutor.