Reputation: 140
Tomcat architecture is comprised of the following elements: Server => Service => Engine => Host => Context
When configuring a standard Tomcat server, we can configure a custom thread pool by specifying the following in our server.xml file: (below is pseudo-code)
<Server>
<Service name="Catalina">
<Connector port="8080"/>
<Executor name="custom-pool" className="my.package.poolImplementation" />
<Engine name="Catalina" defaultHost="localhost">
<Here be more elements />
</Engine>
</Service>
</Server>
(specifically, the Executor name="custom-pool" className="my.package.poolImplementation")
How do I configure Spring Boot to allow the same behaviour programmatically ?
(WITHOUT using Spring configuration files)
No matter where i searched, or how hard I tried, I couldn't find any answer or example.
Thanks in advance
Upvotes: 1
Views: 5075
Reputation: 140
Considering two and a half years have passed since I originally asked this question, I think it is time that I shared our solution for the benefit of anyone that might read this in the future.
We ended up writing a custom component that implements WebServerFactoryCustomizer. Spring Boot will scan for all beans before starting its embedded Tomcat server. If it detects a bean that implements this interface, it will invoke the customize()
method and pass the server factory as an argument to the function.
From there, it was straightforward:
package your.pack.age.name;
import org.apache.catalina.core.StandardThreadExecutor;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class TomcatServerConfig implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
private final StandardThreadExecutor customExecutor;
public TomcatServerConfig() {
this.customExecutor = YourExecutorImplementation();
}
@Override
public void customize(TomcatServletWebServerFactory factory) {
/*This web server is the Tomcat server embedded in Spring Boot*/
TomcatWebServer webServer = (TomcatWebServer)factory.getWebServer()
webServer.getTomcat().getService().addExecutor(this.customExecutor);
}
}
(The actual code we used was simplified here, for the sake of a clear answer)
It is also worth noting that similar code needs to be written for the Tomcat Connectors, using TomcatConnectorCustomizer:
package your.pack.age.name;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.stereotype.Component;
@Component
public class TomcatConnectorConfig implements TomcatConnectorCustomizer {
private final StandardThreadExecutor customExecutor;
public TomcatConnectorConfig() {
this.customExecutor = YourExecutorImplementation();
}
@Override
public void customize(Connector connector) {
connector.getProtocolHandler().setExecutor(this.customExecutor);
}
}
For convenience, I am adding a skeletal custom implementation of a thread executor:
import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.StandardThreadExecutor;
public class HTTPThreadExecutor extends StandardThreadExecutor {
public HTTPThreadExecutor() {
super();
/* Custom constructor code here */
}
@Override
protected void startInternal() throws LifecycleException {
super.startInternal();
/* Any work you need done upon startup */
}
}
Upvotes: 2
Reputation: 157
I needed to customize Tomcat also. I ended up with a code like this:
@Component
public class TomcatCustomizer extends TomcatServletWebServerFactory {
@Override
protected void postProcessContext(Context context) {
Engine engine = (Engine) context.getParent().getParent();
Service service = engine.getService();
Server server = service.getServer();
Connector connector = service.findConnectors()[0];
}
}
You can then set different properties of the server, service, engine, connector. From the object service you can also access the executor and change it. This part I never tried. Whatever you change it will override and complete the spring-boot configuration, you will not loose the spring-boot config.
As Yonatan mentioned one can add an executor with service.addExecutor(...) there is also a method from removing an executor.
I needed this kind of detailed access to the configuration because I needed to configure the server.getGlobalNamingResources(). Also to add a JAASRealm and a Valve. I do not see how one could achieve complete config access with just the customize method.
Upvotes: 0
Reputation: 160
I looked up some source code (see TomcatServletWebServerFactory.java
/ServletWebServerFactoryConfiguration.java
) and found a way to do that.
@Bean
public TomcatProtocolHandlerCustomizer<?> tomcatProtocolHandlerCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(...);
};
}
Upvotes: 4