Production equivalent to Spring Boot EmbeddedServletContainerCustomizer - spring-boot

My question relates to Spring Boot and how to configure error pages in a production web app running in cloudfoundry.
In the Spring IO Sagan reference application, I noticed in the MvcConfig, the following code:
#Configuration
public static class ErrorConfig implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer factory) {
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404"));
factory.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500"));
}
}
Is this configuration used in the cloud too? If so why is it named: EmbeddedServletContainerCustomizer? If not what is the equivalent for the cloud?

Yes, you can use EmbeddedServletContainerCustomizer when deploying to the cloud. Sagan itself is doing exactly that on CloudFoundry for the spring.io website.
The "embedded" in the name of EmbeddedServletContainerCustomizer refers to the fact that the servlet container is embedded in your application's executable jar file. It's the recommended approach for cloud deployment.

Related

Importance of SpringBootServletInitializer in the WAR deployment

I have gone through some tutorials and learnt that ApplicationContext is responsible for managing all the beans created in the spring application. When the WAR is to be deployed to a servlet container, it is essential to extend SpringBootServletInitializer and from the documentation it says
An opinionated WebApplicationInitializer to run a SpringApplication from a traditional WAR deployment. Binds Servlet, Filter and ServletContextInitializer beans from the application context to the server.
Can someone help me understand what the above line means ? How ServletContext initializer is related to the SpringBootServletInitializer ? Why can't a servlet container create a ServletContext on it's own ? If this the case how can we get the hold of ServletContext reference of a SpringApplication programatically created by Tomcat after using SpringBootServletInitializer?
The web application uses the static main entry point when you run the embedded application server. The main entry point usually looks like this:
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
or this:
public static void main(final String[] args) {
SpringApplication springApplication = new SpringApplicationBuilder(Application.class)
.properties("spring.main.banner-mode=log")
.build();
springApplication.run(args);
}
Both use the SpringApplication.run method to scan, configure, and run the web application.
The static main doesn't mean anything to an application server. Instead we add a SpringBootServletInitializer implementation that implements in turn the WebApplicationInitializer interface.
Interface to be implemented in Servlet 3.0+ environments in order to configure the ServletContext programmatically -- as opposed to (or possibly in conjunction with) the traditional web.xml-based approach.
Implementations of this SPI will be detected automatically by SpringServletContainerInitializer, which itself is bootstrapped automatically by any Servlet 3.0 container. See its Javadoc for details on this bootstrapping mechanism.
The SpringServletContainerInitializer gets bootstrapped by the Servlet 3.0+ container and executes the WebApplicationInitializer.onStartup(ServletContext context) methods to configure the ServletContext.
We implement the SpringServletContainerInitializer to define the SpringApplication[s] to be run.
The application server can create a ServletContext on its own, it just can't configure the ServletContext on its own.
Why do you want access to the ServletContext? Spring will handle the configuration for you.

Set default application server Spring Boot

I have multiple application servers on my classpath, namely Netty via spring-boot-starter-webflux and Tomcat through another dependency chain. How can I determine which application server to use in Spring Boot?
Currently, Tomcat is being started instead Netty.
Important note: I can't exclude any of them, Tomcat is used by CXF, Netty is used by WebClient.
Just use proper spring-boot-starter-package
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-web-servers.html
In your case it will be probably spring-boot-starter-reactor-netty
Also it would be wise to exclude multiple JEE embedded containers and remove those you don't need.
You can specify the application is reactive directly in you configuration at startup something like this
#Configuration
#EnableAutoConfiguration
public class Application {
public static void main(final String[] args) {
final SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.setWebApplicationType(WebApplicationType.REACTIVE);
springApplication.run(args);
}
}

What Is the Correct Way To Use AbstractReactiveWebInitializer

I've got a Spring WebFlux application running successfully as a standalone spring boot application.
I am attempting to run the same application in a Tomcat container, and following the documentation, I've created a class that extends AbstractReactiveWebInitializer. The class requires that I implement a method getConfigClasses that would return classes normally annotated with #Configuration. If the working spring boot app started with a class called ApplicationInitializer, then the resulting implementations would look like this:
#SpringBootApplication(scanBasePackages = "my.pkg")
#EnableDiscoveryClient
#EnableCaching
public class ApplicationInitializer {
public static void main(String... args) {
SpringApplication.run(ApplicationInitializer.class, args);
}
}
and
public class ServletInitializer extends AbstractReactiveWebInitializer {
#Override
protected Class<?>[] getConfigClasses() {
return new Class[] {ApplicationInitializer.class};
}
}
When deployed, the only thing that starts is ApplicationInitializer, none of the autoconfigured Spring Boot classes (Cloud Config, DataSource, etc) ever kick off.
The documenation states this is the class I need to implement, I just expected the remainder of the spring environment to "just work".
How should I be using this class to deploy a Reactive WebFlux Spring Boot application to a Tomcat container ?
Edit:
After some additional research, I've narrowed it down to likely just Cloud Config. During bean post processing on startup, the ConfigurationPropertiesBindingPostProcessor should be enriched with additional property sources (from cloud config), but it appears to be the default Spring properties instead, with no additional sources.
The misisng properties is causing downstream beans to fail.
Spring Boot does not support WAR packaging for Spring WebFlux applications.
The documentation you're referring to is the Spring Framework doc; Spring Framework does support that use case, but without Spring Boot.
you can extend SpringBootServletInitializer, add add reactive servlet on onStartup method

SpringBoot, run a daemon as CommandLine (JAR) and inside Tomcat (WAR)?

I want to create a Java deamon process (MQ processor) which can be run both from the commandline (java -jar ...) but also as WAR inside a JEE Container like Tomcat. It should automatically start once the WebApp starts. This App is not going to have a WebGUI.
It seems that I can use SpringBoot for this. SpringBoot can both create WAR and JAR-files.
My question is: should I use SpringBoot ApplicationRunner for a portable daemon?
What is the best practice/recipe to create a portable (CLI/WebApp) daemon process with SpringBoot?
How does this work under the hood? If I use ApplicationRunner and create a WAR, does SpringBoot create a Servlet of this?
Tx
ApplicationRunner is new in Spring Boot 1.4. It is similar to CommandLineRunner of Spring Boot 1.3. It isn't involved in the wiring up of the Application context as such that interface wouldn't be responsible for creating any Servlets in the WAR deployment. Here is details on deploying your app as a WAR:
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
The SpringBootServletInitializer is what will create the servlet.
Now I am not sure how you are setting up the MQ processor but if it is wired up as a bean and has some listener thread for messages on a queue then you don't really need the ApplicationRunner. You would just need to wire up the processor as a bean and have a #PostConstruct annotation on it so that you can start the listener thread. If you don't have control of the annotations on the bean then you can use the ApplicationRunner and have it Autowired with the processor bean. The runner would then start up the listener thread.

cannot resolve reference to bean 'jmsconnectionfactory' when using spring boot + spring integration

I had a problem i'm using hornetq using spring boot and had to create a jmschannel in spring configuration using spring integration <int-jms:channel id="jmsChannel" queue-name="${spring.hornetq.embedded.queues}" connection-factory="jmsConnectionFactory">
<int-jms:interceptors><int:wire-tap channel="logger"/></int-jms:interceptors>
</int-jms:channel>
This is working fine in local when loading with undertow, when deployed the war to Jboss it is throwing up saying bean named jmsConnectionFactory not found, any help is greatly appreiciated
Looks like there is nothing to do with the Spring Integration, but only Spring Boot stuff, which is called deployable war:
The first step in producing a deployable war file is to provide a SpringBootServletInitializer subclass and override its configure method. This makes use of Spring Framework’s Servlet 3.0 support and allows you to configure your application when it’s launched by the servlet container. Typically, you update your application’s main class to extend SpringBootServletInitializer:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}

Resources