shutdown ehcache on a spring boot application - spring

I have a spring boot application. I have implemented caching using the ehcahce in my spring boot application. The caching is working fine, but the tomcat is not shutting down when the shutdown script is triggered. I have skipped the default container in my build and I have tested using jstack and could find the ehcache is preventing the application to shutdown. I need to implement the shutdown for ehcache when the spring boot shut down. I know I need to implement a shutdown listener for ehcahce. I have tried setting the property in application.properties.
net.sf.ehcache.enableShutdownHook=true
But it did not work out. This should be the last option to try out ideally. I need to try adding a listener in web.xml
<listener>
<listener-class>
net.sf.ehcache.constructs.web.ShutdownListener</listener-class>
</listener>
But as spring boot does not have a web.xml how can implement this listener? Can i do it in webconfig? Any one implemented this please help.
I have looked into some of the older posts Tomcat not shutting down when Spring boot app is deployed with ehcache but does not look like having any proper response.
Adding configuration.(As per comment below)
This is my main class, I have configured #EnableCaching
#SpringBootApplication
#EnableAsync
#EnableCaching
public class Application extends SpringBootServletInitializer implements AsyncConfigurer
{
My ehcache.xml in root class path name ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="java.io.tmpdir" />
<defaultCache maxElementsInMemory="10" eternal="false"
timeToIdleSeconds="1200" timeToLiveSeconds="600" overflowToDisk="true" />
<cache name="cache1" maxElementsInMemory="60000" eternal="false"
overflowToDisk="false" timeToIdleSeconds="0" timeToLiveSeconds="43200" memoryStoreEvictionPolicy="LFU"/>
<cache name="cache2" maxElementsInMemory="500" eternal="false"
overflowToDisk="false" timeToIdleSeconds="0" timeToLiveSeconds="43200" memoryStoreEvictionPolicy="LFU"/>
</ehcache>
I have configured to load it on start up.
public class ApplicationStartupService implements
ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
//load cache
}
Method annotated with caching.
#Cacheable(value = CACHE_1, key = "#root.target.KEY")
public Map<String, String> cache1() {
}
in pom.xml I have configured the cache start up.
<packaging>war</packaging>
<name>myapp</name>
<description>my test application</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<jcl.slf4j.version>1.7.12</jcl.slf4j.version>
<logback.version>1.1.3</logback.version>
<rootDir>${project.basedir}</rootDir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
As per comment I have tried adding bean and it did not help.
#Bean
public CacheManager cacheManager() {
net.sf.ehcache.CacheManager cacheManager = new net.sf.ehcache.CacheManager();
return new EhCacheCacheManager(cacheManager);
}

How about create a spring context listener. Trap the context destroy even and shutdown the ehcache.
public class SpringEhcacheShutdownListenerBean implements ApplicationListener {
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
// now you can do ehcache shutdown
// ...
}
}
}
Don't forget to register the class as a spring bean.

Related

Unable to Access Spring Boot Actuator Endpoints

I try to use actuator endpoints in spring boot. The application runs smoothly. My pom file is given below:
<?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.7.3</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.luv2code.springboot</groupId>
<artifactId>thymeleafdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>thymeleafdemo</name>
<description>Ab Jove principium</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- umumi bağımlılıklar -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Here is the content of the application.properties file:
spring.datasource.url=DATABASE_URL
spring.datasource.username=USERNAME
spring.datasource.password=PASSWORD
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect
# Spring Data JPA properties
spring.data.jpa.repository.packages=com.yok.springboot.thymeleafdemo.dao
spring.data.jpa.entity.packages-to-scan=com.yok.springboot.thymeleafdemo.entity
spring.jpa.hibernate.use-new-id-generator-mappings=false
spring.jpa.hibernate.ddl-auto=create
#
# JDBC properties
#
app.datasource.jdbc-url=DATABASE_URL
app.datasource.username=USERNAME
app.datasource.password=PASSWORD
#
# Hikari properties
spring.datasource.hikari.maximumPoolSize=10
spring.datasource.hikari.idleTimeout=2000
spring.datasource.hikari.poolName=SpringBootJPAHikariCP
spring.datasource.hikari.maxLifetime=20000
spring.datasource.hikari.connectionTimeout=30000
# Actuator properties
# expose all endpoints:
management.endpoints.web.exposure.include=*
management.endpoints.beans.enabled=true
management.endpoints.web.exposure.include=info,env
management.endpoint.env.enabled=true
management.endpoint.info.enabled=true
management.endpoints.enabled-by-default=true
This is the start of my Spring Boot Application:
package com.yok.springboot.thymeleafdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ThymeleafdemoApplication {
public static void main(String[] args) {
SpringApplication.run(ThymeleafdemoApplication.class, args);
}
}
Whenever I try to connect /health,/Info or /metrics endpoint by typing http://localhost:8080/health, the HTTP request transfers to http://localhost:8080/showMyLoginPage. I cannot reach endpoint. How can I solve this? Thanks in advance.
Edit -1
Mr. Fatih demands me to observe the result "http://localhost:8080/actuator" and this picture reveals: the picture
Here is the console output of the application:
https://drive.google.com/file/d/1zYP1qe-Ohbcan93ZO6rqjxX9LqlGiIIg/view?usp=sharing
Edit-2
The problem is partly solved. The actuators are available after the login of the application. But the problem is, after the login page, the homepage appears. All actuators are working, however, whenever I hit http://localhost:8080/actuator/health URL, {"status":"DOWN"} appears at the screen. Here is the console output taken during this operation:
reached urls:
http://localhost:8080/showMyLoginPage
http://localhost:8080/students/list/page/1
http://localhost:8080/actuator/health
http://localhost:8080/actuator/heapdump
http://localhost:8080/actuator/env
console output: (exception has thrown)
java.lang.IllegalArgumentException: dataSource or dataSourceClassName
or jdbcUrl is required. at
com.zaxxer.hikari.HikariConfig.validate(HikariConfig.java:1029)
~[HikariCP-4.0.3.jar:na] at
com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:109)
~[HikariCP-4.0.3.jar:na] at
org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:159)
~[spring-jdbc-5.3.22.jar:5.3.22] at
org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:117)
~[spring-jdbc-5.3.22.jar:5.3.22] at
org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
~[spring-jdbc-5.3.22.jar:5.3.22] at
org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:330)
~[spring-jdbc-5.3.22.jar:5.3.22] at
org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.getProduct(DataSourceHealthIndicator.java:122)
~[spring-boot-actuator-2.7.3.jar:2.7.3] at
org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.doDataSourceHealthCheck(DataSourceHealthIndicator.java:105)
~[spring-boot-actuator-2.7.3.jar:2.7.3] at
org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.doHealthCheck(DataSourceHealthIndicator.java:100)
~[spring-boot-actuator-2.7.3.jar:2.7.3]
Edit-3
Mr Fatih pointed out some of the changes at the WebSecurityConfiguration. I have changed the code and I am getting this error:
java.lang.IllegalStateException: permitAll only works with either
HttpSecurity.authorizeRequests() or
HttpSecurity.authorizeHttpRequests(). Please define one or the other
but not both.
Here is the change I've made:
/*
* import section have omitted for brevity
*/
#Configuration
#EnableWebSecurity
public class DemoSecurityConfig {
/*
* other codes have omitted for brevity
*/
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(
(authz) -> authz.antMatchers("/actuator/**").permitAll().anyRequest().authenticated());
http.authorizeRequests(
configurer -> configurer.antMatchers("/**").hasRole("ADMIN").antMatchers("/**").hasRole("USER"))
.formLogin(configurer -> configurer.loginPage("/showMyLoginPage")
.loginProcessingUrl("/authenticateTheUser").permitAll())
.logout(configurer -> configurer.permitAll())
.exceptionHandling(configurer -> configurer.accessDeniedPage("/access-denied"));
return http.build();
}
}
Here is the console output: https://drive.google.com/file/d/1CtjRBHXVRqirZ0Vt_3FEhx_N9oEwyfFZ/view
You are using the spring-security package for application security. So when you want to access your /actuator endpoints, you need to log in first. If you want to access your /actuator endpoints without logging in, you must configure a security configuration. With the following configuration, you can exclude all endpoints starting with /actuator from security.
#EnableWebSecurity
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/actuator/**").permitAll().anyRequest().authenticated();
}
}
Since WebSecurityConfigurerAdapter has been deprecated, you can do this as well.
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(autz -> autz
.mvcMatchers("/actuator/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
** is a wildcard definition and allows you to access this endpoint without logging in, regardless of what comes after the actuator part.
Please Consider using a version for your dependency as far as i remember 1.9.5 RELEASE or 1.9.5 might help in this context , i had the same issue a year ago.

Adding Spring Security starter to my Spring Boot app automatically redirects all paths to one that's set in a completely different project

I added Spring Security Starter dependency (I use Maven) to secure my app (following a tutorial); the problem is that once I add it, whenever I go to localhost to check my progress, I'm always redirected to a path that's coded in another project (I can't even find the view right now).
I currently follow the tutorial on TutorialsPoint, but I use Spring Tools Suite 4 for adding dependencies, coding and running my Spring application instead of CLI on localhost:8080.
I tried finding the view in question, tinkering with another project's web.xml, but I can't find the source of the problem, all I know is that Spring Security Starter is somehow mucking it up.
Thymeleaf template
<!DOCTYPE html>
<html>
<head>
<meta charset = "ISO-8859-1" />
<link href = "css/styles.css" rel = "stylesheet"/>
<title>Spring Boot Application</title>
</head>
<body>
<h4>Welcome to Thymeleaf Spring Boot web application</h4>
</body>
</html>
Web controller
package com.example.vj7.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class WebController {
#RequestMapping(value = "/index")
public String index() {
return "index";
}
}
Application class
package com.example.vj7;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Vj7Application {
public static void main(String[] args) {
SpringApplication.run(Vj7Application.class, args);
}
}
Pom.xml
<?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 http://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.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>vj7</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Vj7</name>
<description>Vježba 7</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
The expected result from "localhost:8080" is just a header "Demo" (like in index.html and set in web controller), but instead a form I worked on before on another Spring project shows up with path "/login".
You need to override some of web security's default configurations if you want index.html to be accessed without authorization. taken from https://docs.spring.io/spring-security/site/docs/4.2.5.RELEASE/apidocs/org/springframework/security/config/annotation/web/configuration/EnableWebSecurity.html
#Configuration
#EnableWebSecurity
public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/index").permitAll().anyRequest()
.permitAll();
}
}

Use RouterFunction with embedded Eureka server

I'm having an issue getting a simple web app using Spring Reactive's RouterFunction to work with an embedded Eureka server. I tried a couple of things, including using the old way of annotating #RestController which works fine. Furthermore, I tried to removing the Eureka dependency from the POM file after which the RouterFunction endpoint worked fine.
POM:
<?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 http://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.0.3.RELEASE</version>
</parent>
<groupId>com.test</groupId>
<artifactId>tds</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--SPRING-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
Router, that does not get hit:
#Configuration
public class Router {
#Bean
public RouterFunction<ServerResponse> route(Handler handler) {
return route(GET("/test"), Handler::handle);
}
}
Controller, that get's hit fine:
#RestController
#ResponseStatus(HttpStatus.OK)
public class controller {
#GetMapping("/discovery")
public String getApplication() {
return "test2222";
}
}
Main:
#SpringBootApplication
#EnableEurekaServer
public class DiscoveryApp {
public static void main(String[] args) {
SpringApplication.run(com.test.tds.DiscoveryApp.class, args);
}
}
As stated above, after removing the dependency to Eureka the RouterFunction endpoint works fine.
Does anyone have an idea how to make this work?
Apparently Webflux is currently not supported by Eureka server. It is suggested to use the old spring-boot-starter-web way.
As future reference:
https://github.com/spring-cloud/spring-cloud-netflix/issues/3108#issuecomment-408262981

Unable to load Swagger-UI with spring boot

I am trying to dynamically document my SpringBoot application's REST API using Swagger.
In order to do that I am using springfox and it's swagger integration. I have followed the official documentation, which can be found here.
Once I finished implementing the integration I can access the JSON output via http://localhost:8080/v2/api-docs, however I am unable to see any information in the Swagger UI via http://localhost:8080/swagger-ui.html.
This is my setup:
pom.xml:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<java.version>1.8</java.version>
<main.basedir>${basedir}/../..</main.basedir>
<ing.continuous-delivery.version>00.04.04</ing.continuous-delivery.version>
<maven.assembly.version>2.3</maven.assembly.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- tag::spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<!-- end::spring -->
<!-- tag::web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<!-- end::web -->
<!-- tag::swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<!-- end:: swagger -->
</dependencies>
</project>
Web MVC configuration:
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Security configuration:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/**", "/swagger-resources").permitAll();
httpSecurity.csrf().disable();
httpSecurity.headers().frameOptions().disable();
}
}
Swagger configuration:
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()).build();
}
}
Thanks for the help!
I'm bad for English, that's why GoogleTranslate.
Nowadays spingdoc is available, which implements openapi3, it gives you the same expected functionality and it is much easier to use it, already in an answer to another question I explained how to do it, in addition to how to customize the information of the api using java annotations
SpringDoc simplifies the generation and maintenance of API documents, based on the OpenAPI 3 specification, for Spring Boot 1.x and 2.x applications.
For magic to happen we simply add the dependency to our pom:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.2.32</version>
</dependency>
then access the description that already has it http://localhost:8080/v3/api-docs/
and for swagger: http://localhost:8080/swagger-ui.html
that's all there is to it.
While trying to access the swagger UI, you need to add your API's base url before /swagger-ui.html.
Something like this:
http://localhost:8080/nameOfAPI/swagger-ui.html
This article helped me:
https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api

Unable to Inject EntityManager in JPA Integration Testing With Arquillian and WildFly

I'm trying do integration testing with the following stack:
App server: Embedded WildFly
CDI container: Weld
Database: In-memory H2
ORM: Hibernate/JPA
Platform: Java 8
OS: Mac OS X 10.10
I've setup basic integration testing with Arquillian (as done here) and I'm able to inject dependencies but injecting EntityManager proves to be a challenge. Dereferencing the entity manager field always results in a NullPointerException.
I've seen many articles (including this and this) but I'm still not able to get this seemingly simple thing to work.
Please see below my pom.xml
<dependencies>
<dependency>
<groupId>org.jboss.spec</groupId>
<artifactId>jboss-javaee-7.0</artifactId>
<version>1.0.0.Final</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<!-- JUnit Container Implementation for the Arquillian Project -->
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.protocol</groupId>
<artifactId>arquillian-protocol-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-weld-ee-embedded-1.1</artifactId>
<version>1.0.0.CR3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-persistence-dbunit</artifactId>
<version>1.0.0.Alpha7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-core</artifactId>
<version>1.1.5.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.1.8.Final</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
test-persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="test" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.xyz.hellomaven.DummyEntity</class>
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
<!--<jta-data-source>java:/DefaultDS</jta-data-source>-->
<!--<jta-data-source>jdbc/arquillian</jta-data-source>-->
<properties>
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update"/>
<!--<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />-->
</properties>
</persistence-unit>
</persistence>
Test case
#RunWith(Arquillian.class)
public class GreeterTest {
#Inject
private Greeter instance; // Injection works!
#PersistenceContext
private EntityManager em; // Null pointer.
public GreeterTest() {
}
#Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class)
.addClasses(Greeter.class, PhraseBuilder.class, DummyInterceptor.class)
.addAsResource("logging.properties", "META-INF/logging.properties")
.addAsResource("test-persistence.xml", "META-INF/persistence.xml")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
#Test
public void testCreateGreeting() {
System.out.println("createGreeting");
assertEquals("Hello, Steve!", instance.createGreeting("Steve"));
}
#Test
public void testPersistence() {
DummyEntity de = new DummyEntity();
de.setId(1l);
de.setName("Petr Cech");
de.setAge(10);
em.persist(de);
Query q = em.createQuery("SELECT d.age FROM DummyEntity d");
assertEquals(10, q.getResultList().get(0));
}
}
Complete Maven project available on GitHub.
Please what am I doing wrong?
just don't use weld, sins data-sources is out of things that could be covered by CI and DI. probably you may mock it with Mokito and stay with light Weld,
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-weld-ee-embedded-1.1</artifactId>
<version>1.0.0.CR3</version>
<scope>test</scope>
</dependency>
But if you want to deal with real DB use managed jboss (ExampleDS is a demo jboss h2 datasource) or managed glassfish instead.
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-arquillian-container-managed</artifactId>
<version>7.1.1.Final</version>
<scope>test</scope>
</dependency>
ref. https://github.com/arquillian/arquillian-examples/blob/master/arquillian-persistence-tutorial/pom.xml
As stated by #Soloviev Dmitry, you use a CDI container for your integration test, which only enables CDI.
There are two options I see:
First one is to use a wildfly-embedded container configured in your maven project, so during maven phase running your integration-tests, wildfly will be downloaded and your test package will be deployed to it. So with ExampleDS it would work fine, as it comes with Wildfly out of the box.
See this post for details
Second one would consist in not using Arquillian for your integration test. So if your integration test only covers managed beans, (not session beans, Wildfly specific resources, ...), you could just instantiate a CDI container prior to your test execution (in #Before or #BeforeClass annotated method using Junit for example) and then instantiate your EntityManager by using the EntityManagerFactory class, referencing your persistence unit used for this integration test. With this method, you could also create CDI producers to inject other resources for your integration test, mocks, depending on the scope of your test.
maven dependency
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se</artifactId>
<version>2.1.2.Final</version>
<scope>test</scope>
</dependency>
The test class
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import org.junit.*;
public class ExampleIT {
private EntityManager em;
protected static Weld weld;
protected static WeldContainer container;
#BeforeClass
public static void init() {
weld = new Weld();
container = weld.initialize();
}
#AfterClass
public static void close() {
weld.shutdown();
}
#Before
private void before(){
em = Persistence.createEntityManagerFactory("MyPersistenceUnit").createEntityManager();
}
#Test
public void testToto(){
// Do something with entity manager ...
}
}
I usually opt for second solution for Integration tests, because it's easier to setup than Arquillian tests, and faster to execute.
Use the application managed entitymanager.
#PersistenceUnit
EntityManagerFactory emf;
and create entityManager using
EntityManager em = emf.createEntityManager();
a container managed entitymanager is created and injected by container itself. If you are not under a server environment, then you need to use application managed persistence context.
I guess the transaction manager + Entity Manger Factory are missing in your context file. Configure both in test-persistence.xml, then make the entity manager factory a property of the transaction manager.

Resources