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
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(...);
};
}
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.
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);
}
}
Related
I am trying to use Apache Camel File-Watch feature.
However I notice all example are used along with Spring.
Is there anyway to make use of this feature without Spring ?
Thank you for any potential input.
Best regards.
Apache Camel is a standalone integration framework to easily integrate different systems using well know and established EIP (Enterprise Integration Patterns).
It is not tied to Spring in any way at its core and has different deployment / integration models out of which is Spring / Spring Boot for the sake of easing its adoption and configuration for Spring framework users.
Out of the runtime contexts, you can use for example Camel Main component to run your application having a File Watch component setup to watch the /tmp/ directory change events:
The main application would look like the following:
public class FileWatchApplication {
private FileWatchApplication() {
}
public static void main(String[] args) throws Exception {
// use Camels Main class
Main main = new Main(FileWatchApplication.class);
// now keep the application running until the JVM is terminated (ctrl + c or sigterm)
main.run(args);
}
}
A simple RouteBuilder setting up your File Watch component would look like the following (note that it must be within the same (or child) package of the FileWatchApplication class):
public class FileWatchRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
// snippet configuration from the official documentation
from("file-watch:///tmp/")
.log("File event: ${header.CamelFileEventType} occurred on file ${header.CamelFileName} at ${header.CamelFileLastModified}");
}
}
Camel is based on Spring. In fact, the Camel Context is an extension of the Spring Application Context. So if you have Camel, you also must have Spring.
I have TennisCoach class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
#Component
public class TennisCoach implements Coach {
#Autowired
#Qualifier("randomFortuneService")
private FortuneService fortuneService;
public TennisCoach() {
System.out.println("Inside default constructor");
}
#PostConstruct
public void doMyStartupStuff() {
System.out.println("Inside postconstructor");
}
#PreDestroy
public void doMyFinalStuff() {
System.out.println("Inside predestroyer");
}
#Override
public String getDailyFortune() {
return fortuneService.getFortune();
}
#Override
public String getDailyWorkout() {
return "Practice your backhand volley";
}
}
And such main class:
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationBeanScopeDemoApp {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
Coach theCoach = context.getBean("tennisCoach", Coach.class);
System.out.println(theCoach.getDailyFortune());
System.out.println(theCoach.getDailyWorkout());
context.close();
}
}
Expected output is:
Inside default constructor
Inside postconstructor
DailyFortune
Practice your backhand volley
Inside predestroyer
But I get this:
Inside default constructor
DailyFortune
Practice your backhand volley
It seems like #PostContruct and #PreDestroy annotations do not work correctly. I cannot find the reason of my problem.
Spring might not handle JSR-250 lifecycle annotations by default. Check your applicationContext.xml that it has element:
<beans ...
<context:annotation-config/>
....
</beans>
that configures Spring to be aware of #PostConstruct & #PrePersist.
Fo more info see this article.
From the Spring Core documentation, paragraph 1.9.8 - Using #PostConstruct and #PreDestroy
Provided that the CommonAnnotationBeanPostProcessor is registered
within the Spring ApplicationContext, a method carrying one of these
annotations is invoked at the same point in the lifecycle as the
corresponding Spring lifecycle interface method or explicitly declared
callback method.
Declaring
<context:annotation-config/>
inside you applicationContext.xml file, implicitly register a CommonAnnotationBeanPostProcessor.
Otherwise, you need to manually register it, using (docs)
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
If you use java 9+ then add dependency javax.annotation:javax.annotation-api (or use java 8).
Or use spring-boot 2 if you using spring-boot 1.
Your Spring don't know about annotation's #PostContruct and #PreDestroy.
Add this code in your applicationContext.xml
<bean class = "org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
Or implements InitializingBean,DisposableBean interfaces in your TenisCoach class.
Eclipse is unable to import #PostConstruct or #PreDestroy
This happens because of Java 9 and higher.
When using Java 9 and higher, javax.annotation has been removed from its default classpath.
Solution
Download the javax.annotation-api-1.3.2.jar from
maven.org
Copy the JAR file to the lib folder of your project
Use the following steps to add it to your Java Build Path.
Right-click your project, select Properties
On left-hand side, click Java Build Path
In top-center of dialog, click Libraries
Click Classpath and then Click Add JARs ...
Navigate to the JAR file /lib/javax.annotation-api-1.3.2.jar
Click OK then click Apply and Close
Eclipse will perform a rebuild of your project and it will resolve the related build errors.
I'm creating a REST web application with Java, Tomcat and Jersey. I'm using annotations (no web.xml!) I ended up using this application configuration:
package com.my_own.server;
import java.util.Properties;
import javax.ws.rs.ApplicationPath;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.my_own.db.PostgreSQLDb;
#ApplicationPath("/api")
public class Application extends javax.ws.rs.core.Application {
private static Logger logger = LogManager.getLogger(Application.class);
public static Application application = null;
public final Properties properties;
public final PostgreSQLDb adminDb;
public Application() throws Exception {
logger.debug("Loading properties from ",
getClass().getClassLoader().getResource("config.properties"));
properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("config.properties"));
adminDb = new PostgreSQLDb(properties, "admin");
application = this; // Setting the global application object here
}
}
Here is my problem. There is a single global application objects for the web container. I'm saving it into a static field, from the application constructor. I need to access it from other classes later (it holds the global configuration, a global database connection factory, and probably other things.)
Am I doing this right? I suspect that there must be a better way: save a reference to the application when annotations are processed. But I'm not sure how. Can I be sure that the Application's constructor will be called exactly once, and the Application.application reference can be accessed later, from any REST call?
Use dependency injection in jersey, bind your application when initializing:
public class MyApplication extends ResourceConfig {
public MyApplication() {
super(MyApplication.class);
register(new MyBinder());
packages(true, "location.of.my.jersey.classes");
}
/* Bind is used to let jersey know what can be injected */
private static class MyBinder extends AbstractBinder {
#Override
protected void configure() {
bind(MyApplication.class).to(MyApplication.class);
}
}
}
Then in your code:
#Path("myapi")
#Produces(MediaType.APPLICATION_JSON)
public class ServerRoutes {
#Inject
MyApplication application;
//... your rest code here
}
Now you can access MyApplication from within your code without needing any statics or singletons, jersey handles it.
Let me share my opinion: you can use of course a well-known Singleton pattern to store a "global" static object, however, it's really an antipattern these days.
If it's not a homework or something then storing global "static" objects is always a bad idea design-wise. If you want to know why there are many sources that answer this question, like this discussion for example
So I suggest considering using a Dependency Injection container instead, There are many really good containers out there: Spring, Guice to name a few.
These containers can store these objects as beans and if your "pieces" of functionality are also managed by these containers, you'll be able to "inject" application beans right into the controller.
It effectively solves all the issues introduced by singleton pattern.
I want to change bind port when the application runningļ¼
But Meet a error message 'EmbeddedServletContainerCustomizer cannot be resolved to a type'.
My Spring boot version is 2.0.0.BUILD-SNAPSHOT.
the following code:
import org.springframework.boot.context.embedded.*;
import org.springframework.stereotype.Component;
#Component
public class CustomizationBean implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(9000);
}
}
thanks a lot
As far as the port is concerned, I'd use the configuration option as already answered.
However, you can still use a customizer, however, the types and location will change in Spring Boot 2.0, see:
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
#Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
#Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
SpringBoot has an easy configuration for binding port, just use server.port to set the customized port in application.properties
Use SpringApplicationBuilder to set the property server.port programatically. use this inside your spring boot main method.
HashMap<String, Object> properties = new HashMap<>();
properties.put("server.port", 9000);
new SpringApplicationBuilder()
.properties(properties)
.run(args);
JBOSS 7.x has the possibility to activate schema validation on the server side by means of using an #SchemaValidation annotation on the SEI.
However I would like to customize my errors as well. Moreover I would like to change the exception into a report (result).
I've found the following question / answer on Stack Overflow. Which explains how to setup a customized ValidationEventHanlder with CXF. However, JBOSS uses it own way deployment descriptors overriding the CXF ones. It is possible to achieve the same result as with the #Schemavalidation by means of the JBOSS web service deployment descriptor. However, I was not able yet to activate my own event handler.
I'm thinking about not throwing an exception, but storing the validation result in a HTTP header or in a ThreadLocal, in order to create my own result.
Questions:
1) Is it possible to setup a ValidationEventHander in JBOSS 7.x.x (or in JBOSS 6.x.x EAP)?
2) Is it possible to override the default exception (not throwing an exception on non-fatal errors, like ranges, formats etc?) and returning a result?
Thanks!
JBOSS 7.x uses a concept called 'interceptors'. By defining an interceptor one can access the message context. There are 2 flavours of messsage contexts:
The WebService Context that is available via the #Resource annotation in the Servlet or EJB
The CXF WebService Context that is avialable to 'later interceptors' in the chain.
The latter one is available by menas of the setContextualProperty.
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.ValidationEvent;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
public class ValidatingInterceptor extends AbstractPhaseInterceptor<Message> {
public static String CTX_KEY_VALIDATOR_EVENTS = "event_key";
public ValidatingInterceptor() {
super(Phase.READ);
}
#Override
public void handleMessage(Message message) throws Fault {
List<ValidationEvent> validationRes = new ArrayList<ValidationEvent>();
message.put(CTX_KEY_VALIDATOR_EVENTS, validationRes);
message.setContextualProperty("jaxb-validation-event-handler", new XmlValidationHandler(validationRes));
}
}
Here is the validator that is inserted:
import java.util.List;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
public class XmlValidationHandler implements ValidationEventHandler {
private final List<ValidationEvent> results;
public XmlValidationHandler(List<ValidationEvent> results) {
this.results = results;
}
#Override
public boolean handleEvent(ValidationEvent event) {
results.add(event);
return true;
}
}
The validator adds a List to the context described in 1. and is now available for further processing in the EJB or Servlet. The SEI then looks like this:
#SchemaValidation
#InInterceptors(classes = {ValidatingInterceptor.class})
#Stateless
public class LogicBean implements SEI
Note: the #SchemaValidation is still required as annotation, since that triggers the annotation in the first place.