Spring boot: Start an application automatically when Webshere Application Server starts? - spring

Assume I have a SpringBoot Application deployed as a WAR to Websphere Application Server (WAS). This WAR contains a daemon, so it must start straight away when WAS starts (and only once).
However, I still need to activate the SpringBoot Servlet by doing a http request.
Now I understand that the concept of servlets is to act on http requests, I still want to get it auto started on appserver start. This makes my daemon portable from standalone jar/main to war/webapp.
I tried a ServletContextListener, but the contextInitalized also get only called at the first http request.
I do not have a web.xml (servlet 3).
Code:
#SpringBootApplication
#WebListener
public class DemoApplication extends SpringBootServletInitializer implements ServletContextListener {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.err.println("ONSTARTUP");
super.onStartup(servletContext);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(DemoApplication.class);
}
#Override
public void contextInitialized(ServletContextEvent sce) {
System.err.println("contextInitialized");
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
//
}
}
and:
#Component
public class DemoRunner implements ApplicationRunner {
#Override
public void run(ApplicationArguments arg0) throws Exception {
System.err.println("I AM RUNNING");
}
}
When I start WAS I first get this:
Launching defaultServer (WebSphere Application Server
16.0.0.2/wlp-1.0.13.cl160220160526-2258) on Java HotSpot(TM) 64-Bit Server VM, version 1.7.0_79-b15 (en_US)
[...]
[AUDIT ] CWWKT0016I: Web application available (default_host): http://localhost:9080/demo/
[AUDIT ] CWWKZ0001I: Application test started in 17,282 seconds.
To get my Spring Boot application starting, I first need to visit this link (http:/localhost:9080/demo/). Then it starts rolling, starting with the startup method as you can see in the log. But how can I get this starting without doing a http request?
[err] ONSTARTUP
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.4.0.RELEASE)
2016-09-02 10:45:52.670 INFO 23716 --- [dPool-thread-48] com.example.DemoApplication : Starting DemoApplication on [...]
2016-09-02 10:45:58.019 INFO 23716 --- [dPool-thread-48] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
[...]
[err] I AM RUNNING
[...]
2016-09-02 10:45:58.093 INFO 23716 --- [dPool-thread-48] com.example.DemoApplication : Started DemoApplication in 6.372 seconds (JVM running for 31.549)
[...]
[err] contextInitialized
[err] contextInitialized

You can change the loadOnStartup by customize the spring dispatch servlet, here is the sample question and you can use the code
#Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
return new BeanFactoryPostProcessor() {
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bean = beanFactory.getBeanDefinition(
DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
bean.getPropertyValues().add("loadOnStartup", 1);
}
};
}
Reference:
how to configure 'dispatcherServlet' load on startup by spring boot?
Upate
Seems there is a more simple way, you can config it in application.properites
spring.mvc.servlet.load-on-startup=1

Related

Is there a way to launch FXGL with SpringBoot?

I need to launch application with fxgl animations with springboot to utilize it's functionality.
I dont really know how to do it.
I tried putting initialization into different init() methods of the inheritor of the GameApplication.
I tried it like this:
#Override
protected void onPreInit() {
context = SpringApplication.run(getClass(), savedArgs);
context.getAutowireCapableBeanFactory().autowireBean(this);
super.onPreInit();
}
Or tried to mimic web version:
public class App extends Application {
private ConfigurableApplicationContext applicationContext;
#Override
public void init() {
String[] args = getParameters().getRaw().toArray(new String[0]);
this.applicationContext = new SpringApplicationBuilder()
.sources(DiplomaBaseApplication.class)
.run(args);
}
#Override
public void stop() {
this.applicationContext.close();
Platform.exit();
}
#Override
public void start(Stage stage) {
GameApplication gameApplication = new SimulationApplication();
GameApplication.embeddedLaunch(gameApplication);
}
}
#SpringBootApplication
public class DiplomaBaseApplication {
public static void main(String[] args) {
Application.launch(App.class, args);
}
}
Using javafx-weaver-spring-boot-starter.
The application started, but spring initialization ended to early end spring features and beans doesn't work:
22:08:38.879 [JavaFX Application Thread] INFO Engine - FXGL-11.17 (16.07.2021 15.46) on WINDOWS (J:11.0.10 FX:16)
22:08:38.879 [JavaFX Application Thread] INFO Engine - Source code and latest versions at: https://github.com/AlmasB/FXGL
22:08:38.880 [JavaFX Application Thread] INFO Engine - Join the FXGL chat at: https://gitter.im/AlmasB/FXGL
22:08:39.418 [FXGL Background Thread 1 ] INFO FXGLApplication - FXGL initialization took: 0,325 sec
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.7)
2022-05-13 22:08:39.900 INFO 31948 --- [lication Thread] o.s.boot.SpringApplication : Starting application using Java 11.0.10 on DESKTOP-2DKK20I with PID 31948 (started by pro56 in C:\Users\pro56\Desktop\Course)
2022-05-13 22:08:39.903 INFO 31948 --- [lication Thread] o.s.boot.SpringApplication : No active profile set, falling back to 1 default profile: "default"
2022-05-13 22:08:39.985 INFO 31948 --- [lication Thread] o.s.boot.SpringApplication : Started application in 0.425 seconds (JVM running for 1.813)
22:08:40.056 [FXGL Background Thread 1 ] INFO FXGLApplication - Game initialization took: 0,024 sec
22:08:40.852 [FXGL Background Thread 2 ] INFO UpdaterService - Your current version: 11.17
22:08:40.852 [FXGL Background Thread 2 ] INFO UpdaterService - Latest stable version: 17.1
I'm not exactly sure why this did not work for you. I tried it and it worked fine for me.
This warning is generated:
Unsupported JavaFX configuration: classes were loaded from 'unnamed module #307f6b8c'
I guess that is just how fxgl works, it runs JavaFX off the classpath in an unsupported configuration by default. I see from the FXGL documentation that it can be used as a module, so I guess in that configuration it will run a supported configuration using JavaFX modules rather than off the classpath. However, SpringBoot isn't currently coded to use the modulepath (that will happen with SpringBoot 3, I believe). So, for now, it is probably best to run everything in the unsupported configuration as demonstrated by this example.
Although in the example I placed a main method in the DemoSpringApplication for testing, the actual main class to run is not that one, it is instead the BasicSpringGameApp which extends the FXGL GameApplication.
The BasicSpringGameApp will call the SpringApplication static method to run the Spring application (which will create an instance of the Spring application).
The Spring application is separate from the FXGL GameApplication (which is internally separate from the JavaFX Application), so all of those things have different instances (just one of each), applying a separation of concerns.
src/main/java/com/example/glboot/BasicSpringGameApp.java
Extends the FXGL GameApplication. The FXGL GameApplication will (internally) launch a JavaFX application. This example will also run a spring application so that spring services will be available. The spring context is autowired into the GameApplication class so that spring services are available for use within the GameApplication class. The use of a Spring inject service is demonstrated by making calls to the UserService, which is a Spring service.
package com.example.glboot;
import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.app.GameSettings;
import com.almasb.fxgl.dsl.FXGL;
import javafx.scene.control.Label;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
public class BasicSpringGameApp extends GameApplication {
private ConfigurableApplicationContext springContext;
#Autowired
UserService userService;
#Override
protected void initSettings(GameSettings settings) {
settings.setWidth(200);
settings.setHeight(150);
settings.setTitle("Game App");
springContext =
new SpringApplicationBuilder(DemoSpringApplication.class)
.web(WebApplicationType.NONE)
.run();
springContext
.getAutowireCapableBeanFactory()
.autowireBeanProperties(
this,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE,
true
);
}
#Override
protected void initGame() {
String welcomeText =
"hello, " + userService.getUsername() + "\n" + userService.getWelcomeMessage();
FXGL.entityBuilder()
.at(50, 50)
.view(new Label(welcomeText))
.buildAndAttach();
}
public static void main(String[] args) {
launch(args);
}
}
src/main/resources/DemoSpringApplication.java
SpringBoot application, configures and starts up a spring services via the SpringBoot framework.
package com.example.glboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class DemoSpringApplication {
public static void main(String[] args) {
SpringApplication.run(DemoSpringApplication.class, args);
}
}
src/main/java/com/example/glboot/UserService.java
An example user information spring service with some injected values from the spring context and application configuration properties.
package com.example.glboot;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
#Service
public class UserService {
#Value("${user.name}")
private String username;
#Value("${welcome.message}")
private String welcomeMessage;
public String getUsername() {
return username;
}
public String getWelcomeMessage() {
return welcomeMessage;
}
}
src/main/resoruces/application.properties
Spring configuration properties.
welcome.message=Welcome to FXGL Boot
pom.xml
Maven project file.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>glboot</artifactId>
<version>1.0-SNAPSHOT</version>
<name>glboot</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>18.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>18.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>18.0.1</version>
</dependency>
<dependency>
<groupId>com.github.almasb</groupId>
<artifactId>fxgl</artifactId>
<version>17</version>
<exclusions>
<exclusion>
<groupId>org.openjfx</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
<configuration>
<source>18</source>
<target>18</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
console output
/Users/js732745/Library/Java/JavaVirtualMachines/openjdk-18.0.1.1/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=59335:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/js732745/dev/glboot/target/classes:/Users/js732745/.m2/repository/org/openjfx/javafx-controls/18.0.1/javafx-controls-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-controls/18.0.1/javafx-controls-18.0.1-mac.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-graphics/18.0.1/javafx-graphics-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-graphics/18.0.1/javafx-graphics-18.0.1-mac.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-base/18.0.1/javafx-base-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-base/18.0.1/javafx-base-18.0.1-mac.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-fxml/18.0.1/javafx-fxml-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-fxml/18.0.1/javafx-fxml-18.0.1-mac.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-media/18.0.1/javafx-media-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-media/18.0.1/javafx-media-18.0.1-mac.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl/17/fxgl-17.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-core/17/fxgl-core-17.jar:/Users/js732745/.m2/repository/com/gluonhq/attach/audio/4.0.9/audio-4.0.9.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-io/17/fxgl-io-17.jar:/Users/js732745/.m2/repository/com/gluonhq/attach/storage/4.0.9/storage-4.0.9.jar:/Users/js732745/.m2/repository/com/gluonhq/attach/util/4.0.9/util-4.0.9.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-entity/17/fxgl-entity-17.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-scene/17/fxgl-scene-17.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-gameplay/17/fxgl-gameplay-17.jar:/Users/js732745/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.13.2/jackson-annotations-2.13.2.jar:/Users/js732745/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.13.2.1/jackson-databind-2.13.2.1.jar:/Users/js732745/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.13.2/jackson-core-2.13.2.jar:/Users/js732745/.m2/repository/com/gluonhq/attach/lifecycle/4.0.9/lifecycle-4.0.9.jar:/Users/js732745/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib/1.5.32/kotlin-stdlib-1.5.32-modular.jar:/Users/js732745/.m2/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/Users/js732745/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar:/Users/js732745/.m2/repository/org/springframework/boot/spring-boot-starter/2.6.7/spring-boot-starter-2.6.7.jar:/Users/js732745/.m2/repository/org/springframework/boot/spring-boot/2.6.7/spring-boot-2.6.7.jar:/Users/js732745/.m2/repository/org/springframework/spring-context/5.3.19/spring-context-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/spring-aop/5.3.19/spring-aop-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/spring-beans/5.3.19/spring-beans-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/spring-expression/5.3.19/spring-expression-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.6.7/spring-boot-autoconfigure-2.6.7.jar:/Users/js732745/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.6.7/spring-boot-starter-logging-2.6.7.jar:/Users/js732745/.m2/repository/ch/qos/logback/logback-classic/1.2.11/logback-classic-1.2.11.jar:/Users/js732745/.m2/repository/ch/qos/logback/logback-core/1.2.11/logback-core-1.2.11.jar:/Users/js732745/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.17.2/log4j-to-slf4j-2.17.2.jar:/Users/js732745/.m2/repository/org/apache/logging/log4j/log4j-api/2.17.2/log4j-api-2.17.2.jar:/Users/js732745/.m2/repository/org/slf4j/jul-to-slf4j/1.7.36/jul-to-slf4j-1.7.36.jar:/Users/js732745/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/js732745/.m2/repository/org/springframework/spring-core/5.3.19/spring-core-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/spring-jcl/5.3.19/spring-jcl-5.3.19.jar:/Users/js732745/.m2/repository/org/yaml/snakeyaml/1.29/snakeyaml-1.29.jar:/Users/js732745/.m2/repository/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar com.example.glboot.BasicSpringGameApp
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.7)
2022-05-13 15:54:30.381 INFO 32226 --- [ main] com.example.glboot.BasicSpringGameApp : Starting BasicSpringGameApp using Java 18.0.1.1 on C02ZX2NFMD6T with PID 32226 (/Users/js732745/dev/glboot/target/classes started by js732745 in /Users/js732745/dev/glboot)
2022-05-13 15:54:30.384 INFO 32226 --- [ main] com.example.glboot.BasicSpringGameApp : No active profile set, falling back to 1 default profile: "default"
2022-05-13 15:54:30.772 INFO 32226 --- [ main] com.example.glboot.BasicSpringGameApp : Started BasicSpringGameApp in 0.675 seconds (JVM running for 0.988)
2022-05-13 15:54:30.835 WARN 32226 --- [JavaFX-Launcher] javafx : Unsupported JavaFX configuration: classes were loaded from 'unnamed module #307f6b8c'
15:54:31.118 [JavaFX Application Thread] INFO Engine - FXGL-17 (31.12.2021 18.16) on MAC (J:18.0.1.1 FX:18.0.1)
15:54:31.119 [JavaFX Application Thread] INFO Engine - Source code and latest versions at: https://github.com/AlmasB/FXGL
15:54:31.119 [JavaFX Application Thread] INFO Engine - Ask questions and discuss at: https://github.com/AlmasB/FXGL/discussions
15:54:31.119 [JavaFX Application Thread] INFO Engine - Join the FXGL chat at: https://gitter.im/AlmasB/FXGL
15:54:31.534 [FXGL Background Thread 1 ] WARN FXGL.DefaultMenu - FXGLDefaultMenu is not designed for resolutions < 800x600
15:54:31.686 [FXGL Background Thread 1 ] INFO FXGLApplication - FXGL initialization took: 0.399 sec
15:54:31.757 [FXGL Background Thread 4 ] INFO FXGLApplication - Game initialization took: 0.005 sec
15:54:32.035 [FXGL Background Thread 2 ] INFO UpdaterService - Your current version: 17
15:54:32.036 [FXGL Background Thread 2 ] INFO UpdaterService - Latest stable version: 17.1

Why can't I log before starting a Spring Boot Application?

I am trying to log something before my Spring Boot application starts. Here is a snippet below. I am using Lombok and Log4J2 and I have done the spring-boot-starter-logging exclusion + added spring-boot-starter-log4j2. I was wondering how to make it work and why the present code does not work.
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import lombok.extern.log4j.Log4j2;
#SpringBootApplication
#Log4j2
public class DemoApplication {
public static void main(String[] args) {
log.info("Does not work");
SpringApplication.run(DemoApplication.class, args);
log.info("Works");
}
}
Result in console:
2020-11-30 19:45:32.667 INFO 25132 --- [ main] c.e.d.DemoApplication : Starting DemoApplication using Java 11.0.1 on *** with PID 25132 (**** started by **** in ****)
2020-11-30 19:45:32.702 INFO 25132 --- [ main] c.e.d.DemoApplication : No active profile set, falling back to default profiles: default
2020-11-30 19:45:33.921 INFO 25132 --- [ main] c.e.d.DemoApplication : Started DemoApplication in 2.008 seconds (JVM running for 3.103)
2020-11-30 19:45:33.927 INFO 25132 --- [ main] c.e.d.DemoApplication : Works
Updated:
However, as shown below, using Slf4j with default LogBack works, why not Log4j2?
(Log4j2 with Slf4j still does not)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
log.info("Does not work");
SpringApplication.run(DemoApplication.class, args);
log.info("Works");
}
}
20:04:25.945 [main] INFO com.example.demo.DemoApplication - Does not work
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.0)
2020-11-30 20:04:26.463 INFO 33284 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 11.0.1 on **** with PID 33284 (**** started by **** in ****)
2020-11-30 20:04:26.465 INFO 33284 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2020-11-30 20:04:27.234 INFO 33284 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.17 seconds (JVM running for 2.109)
2020-11-30 20:04:27.242 INFO 33284 --- [ main] com.example.demo.DemoApplication : Works
In the end I was able to answer my own question by stepping through the code + some research. Sharing my results:
In Log4j2, the default root filter level, when not providing a config (or before Spring instantiates the Log4j2-spring config) is ERROR, therefore isEnabled returns false and the logger does not print to the console the first time. However, once instantiated by Spring, the level becomes INFO and therefore the messages is printed to the console as the log level is now superior or equal to INFO level. QED

# WebMvcTest loading application context

I have a GreetingController
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public #ResponseBody String greeting() {
return "Hello, same to you";
}
}
and GreetingControllerTest
#WebMvcTest(GreetingController.class)
public class WebMockTest {
#Autowired
private MockMvc mockMvc;
#Test
public void greetingShouldReturnMessageFromService() throws Exception {
this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, same to you")));
}
}
I am running the test in intelliJ hoping that it will not load the application context, but it starts with launching the application.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.5.RELEASE)
{"thread":"main","level":"INFO","loggerName":..........
As per spring doc we can narrow the tests to only the web layer by using #WebMvcTest. Does this mean that it still loads the application context? Or maybe I did not understand it correctly.
With #WebMvcTest you still get an application context, but not the full application context.
The started Spring Test Context only contains beans that are relevant for testing your Spring MVC components: #Controller, #ControllerAdvice, Converter, Filter, WebMvcConfigurer.
Injecting MockMvc using #Autowired MockMvc mockMvc; also indicates that you are working with a Spring context and the JUnit Jupiter extension (#ExtendWith(SpringExtension.class which is part of #WebMvcTest) takes care to resolve your fields by retrieving them from the Test context.
If you still don't want a Spring Test context to be started, you can write a unit test using only JUnit and Mockito. With such tests, you would only be able to verify the business logic of your controller and not things like: correct HTTP response, path variable and query parameter resolving, exception handling with different HTTP status, etc.
You can read more on the different Spring Boot Test slices here and on how to use MockMvc to test your web layer.

SpringBoot and log4j2 - application context loaded twice when SpringApplication.run()

I want to use Spring boot and log4j2.
I have this pom:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
This is my main class:
#Component("batchLauncher")
#Import({ MyConfiguration.class })
public class MyLauncher implements CommandLineRunner {
private static Logger log = LogManager.getLogger(MyLauncher.class);
#Autowired
MyController myController;
public static void main(String[] args) {
log.info("STARTING");
SpringApplication.run(MyLauncher.class, args);
log.info("FINISHED");
}
#Override
public void run(String... args) throws Exception {
log.info("START Batch");
MyController.start();
log.info("END Batch");
}
}
I launch the jar with this option:
-Dlog4j.configurationFile=C:\log4j2.properties
When the application start, the console show me:
DEBUG StatusLogger Reconfiguration complete for context[name=18b4aac2] at URI C:\log4j2.properties (org.apache.logging.log4j.core.LoggerContext#72057ecf) with optional ClassLoader: null
DEBUG StatusLogger Shutdown hook enabled. Registering a new one.
DEBUG StatusLogger LoggerContext[name=18b4aac2, org.apache.logging.log4j.core.LoggerContext#72057ecf] started OK.
2019-02-08 14:57:31.047 INFO [main] [it.batch.MyLauncher] [main] [it.batch.MyLauncher.main] - STARTING THE APPLICATION
DEBUG StatusLogger Using configurationFactory org.apache.logging.log4j.core.config.ConfigurationFactory$Factory#6c80d78a
DEBUG StatusLogger Not in a ServletContext environment, thus not loading WebLookup plugin.
DEBUG StatusLogger Loaded configuration from
...
DEBUG StatusLogger LoggerContext[name=18b4aac2, org.apache.logging.log4j.core.LoggerContext#72057ecf] started OK with configuration XmlConfiguration[location=jar:file:/C:/Users/G0426/.m2/repository/org/springframework/boot/spring-boot/2.1.2.RELEASE/spring-boot-2.1.2.RELEASE.jar!/org/springframework/boot/logging/log4j2/log4j2.xml].
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.2.RELEASE)
2019-02-08 14:57:31.753 INFO 13496 --- [ main] i.f.c.c.o.b.Info
As you can see from logs, log4j2 load my log4j properties file until the line
SpringApplication.run(MyLauncher.class, args);
The previous line is writed in log file, after that running SpringApplication.run(...), a second instance/context is loaded and log4j2 start logging using the default configuration located at:
file:/C:/Users/G0426/.m2/repository/org/springframework/boot/spring-boot/2.1.2.RELEASE/spring-boot-2.1.2.RELEASE.jar!/org/springframework/boot/logging/log4j2/log4j2.xml
What I am doing wrong?
Thanks.
SpringApplication.run(MyLauncher.class, args); is THE starting point of a Spring application. Spring has no control over anything logged before its execution.
Hence the behavior is appropriate considering Spring based logging configuration kicks in only after its context is loaded. Also, Spring Boot uses logging.config property to load the logging configuration.
You can try setting -Dlogging.config=C:\log4j.properties.
See Spring Boot Documentation for further details.

Spring boot - Messaging with RabbitMQ to listen message queue continuously

When I run the Messaging with RabbitMQ guide [I tried this demo application][1] , the listener only receive the message once (the runner send once and then exited). I hope the listener can continuously listen the message queue, how should I achieve it ?
the receiver code:
package hello;
import java.util.concurrent.CountDownLatch;
import org.springframework.stereotype.Component;
#Component
public class Receiver {
private CountDownLatch latch = new CountDownLatch(1);
public void receiveMessage(String message) {
System.out.println("Received <" + message + ">");
latch.countDown();
}
public CountDownLatch getLatch() {
return latch;
}
}
the runner code:
package hello;
import java.util.concurrent.TimeUnit;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
#Component
public class Runner implements CommandLineRunner {
private final RabbitTemplate rabbitTemplate;
private final Receiver receiver;
public Runner(Receiver receiver, RabbitTemplate rabbitTemplate) {
this.receiver = receiver;
this.rabbitTemplate = rabbitTemplate;
}
#Override
public void run(String... args) throws Exception {
System.out.println("Sending message...");
rabbitTemplate.convertAndSend(Application.topicExchangeName, "foo.bar.baz", "====1===========Hello from RabbitMQ!");
rabbitTemplate.convertAndSend(Application.topicExchangeName, "foo.bar.baz", "====2===========Hello from RabbitMQ!");
rabbitTemplate.convertAndSend(Application.topicExchangeName, "foo.bar.baz", "=====3==========Hello from RabbitMQ!");
receiver.getLatch().await(10000, TimeUnit.MILLISECONDS);
}
}
the application code:
ackage hello;
import hello.test.TestListener;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class Application {
static final String topicExchangeName = "spring-boot-exchange";
public static final String queueName = "spring-boot";
#Bean
Queue queue() {
return new Queue(queueName, false);
}
#Bean
TopicExchange exchange() {
return new TopicExchange(topicExchangeName);
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("foo.bar.#");
}
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
container.setMessageListener(listenerAdapter);
return container;
}
#Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(Application.class, args).close();
}
}
the log
[opuser#iZ25fprd8a9Z java]$ java -jar gs-messaging-rabbitmq-0.1.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.1.RELEASE)
2019-01-10 09:40:53.846 INFO 412 --- [ main] hello.Application : Starting Application on iZ25fprd8a9Z with PID 412 (/data/workspace/test/java/gs-messaging-rabbitmq-0.1.0.jar started by opuser in /data/workspace/test/java)
2019-01-10 09:40:53.854 INFO 412 --- [ main] hello.Application : No active profile set, falling back to default profiles: default
2019-01-10 09:40:55.019 INFO 412 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration' of type [org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration$$EnhancerBySpringCGLIB$$870d6481] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-01-10 09:40:56.047 INFO 412 --- [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [10.171.135.109:5672]
2019-01-10 09:40:56.145 INFO 412 --- [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#76f2b07d:0/SimpleConnection#49c43f4e [delegate=amqp://petmq#10.171.135.109:5672/, localPort= 41068]
2019-01-10 09:40:56.152 INFO 412 --- [ main] o.s.amqp.rabbit.core.RabbitAdmin : Auto-declaring a non-durable, auto-delete, or exclusive Queue (spring-boot) durable:false, auto-delete:false, exclusive:false. It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.
2019-01-10 09:40:56.244 INFO 412 --- [ main] hello.Application : Started Application in 3.116 seconds (JVM running for 4.001)
Sending message...
Received <====1===========Hello from RabbitMQ!>
Received <====2===========Hello from RabbitMQ!>
Received <=====3==========Hello from RabbitMQ!>
2019-01-10 09:40:56.269 INFO 412 --- [ main] o.s.a.r.l.SimpleMessageListenerContainer : Waiting for workers to finish.
2019-01-10 09:40:57.267 INFO 412 --- [ main] o.s.a.r.l.SimpleMessageListenerContainer : Successfully waited for workers to finish.
2019-01-10 09:40:57.272 INFO 412 --- [ main] o.s.a.r.l.SimpleMessageListenerContainer : Shutdown ignored - container is not active already
If you add the dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Your application will not close out.

Resources