Simple Aspect not Executing - spring-boot

I had this Aspect working then all of a sudden it stopped. #AfterThrowing all classes in and below the pointcut. You can see I added a constructor to the Aspect to ensure it is picked up by Spring and it is. Below is a class that I stepped through and watched exceptions thrown and handled. Just throwing (no pun intended) this out there to see if any one notices something.
package mil.ndms.taabatch.af.batchjobs.afdaily.difmsjon;
import java.util.ArrayList;
import java.util.List;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.transform.Range;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;
import mil.ndms.taabatch.af.batchjobs._jobscommon.FileUtilities;
import mil.ndms.taabatch.af.customexceptions.StepException;
#Service
public class DIFMSJonReader implements ItemReader<DIFMSJonModel>, StepExecutionListener
{
private int nextItemIndex;
private List<DIFMSJonModel> difmsJonModelList;
private StepExecution stepExecutionForRead;
#Autowired
FileUtilities fileUtilities;
#Override
/*
* This code in the beforeStep method is required here because this is a custom ItemReader.
* Custom readers need to produce their own list to be iterated over.
* Each item in the List invokes the read method.
*/
public void beforeStep(StepExecution stepExecution)
{
// Initialize Class variables
stepExecutionForRead = stepExecution;
difmsJonModelList = new ArrayList<DIFMSJonModel>();
nextItemIndex = 0;
// Create Flat File Reader
FlatFileItemReader<DIFMSJonModel> reader = null;
try
{
// Get the file location from the BATCH_FILE_PARAMETERS Table
String fileName = fileUtilities.getFileInputLocation("MS204D01.DAT");
// Throw Exception if the location is not found
if(null == fileName)
throw new StepException("File - MS204D01 - Does not exist");
// Build the Flat FileRreader based on the following parameters
reader =
new FlatFileItemReaderBuilder<DIFMSJonModel>()
.name("DIFMSJonReader").linesToSkip(0)
.resource(new FileSystemResource(fileName))
.fixedLength()
.columns(new Range[] { new Range(1, 12), new Range(13, 13), new Range(14, 14), new Range(15, 21) })
.names(new String[] { "jon", "completeIndicator", "laborValid", "jonOpenDate" })
.targetType(DIFMSJonModel.class)
.build();
// Open the file/reader assign it to a context
reader.open(new ExecutionContext());
// Iterate over the open file. Each row will be assigned to a new instance of DIFMSJonModel
// and then stored in the difmsJonModelList
DIFMSJonModel item;
while ((item = (DIFMSJonModel) reader.read()) != null)
{
difmsJonModelList.add(item);
}
}
catch (Exception e)
{
// if this exception is not of type StepException and since we are in a Job Step,
// we are going to add a StepExecution to the Exception to be used by the Aspect which will
// log the exception
if(!(e instanceof StepException))
{
StepException se = new StepException(e);
se.setStepExecution(stepExecution);
e.addSuppressed(new StepException(e));
}
else
{
((StepException) e).setStepExecution(stepExecution);
}
// We must empty the List so that the read() has nothing to read
// This is how we stop the Job
difmsJonModelList.clear();
stepExecution.getJobExecution().setStatus(BatchStatus.FAILED);
}
finally
{
// Close the reader/file
if(null != reader) reader.close();
}
}
#Override
public DIFMSJonModel read()
throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException
{
DIFMSJonModel nextItem = null;
try
{
/* Counter **********************************************************************
* This code is necessary because this is a custom ItemReader.
* This "counter" lets us know which item is next to retrieve from the list.
* Once this method ends the item is passed to the ItemProcessor method
*/
if (nextItemIndex < difmsJonModelList.size())
{
nextItem = difmsJonModelList.get(nextItemIndex);
nextItemIndex++;
}
else
{
nextItemIndex = 0;
}
/* Counter **********************************************************************/
}
catch (Exception e)
{
if(!(e instanceof StepException))
{
StepException se = new StepException(e);
se.setStepExecution(stepExecutionForRead);
e.addSuppressed(se);
}
else
{
((StepException) e).setStepExecution(stepExecutionForRead);
}
// After we process an Exception we must throw the Exception.
// Spring Batch sees the Exception and stops the Job
throw e;
}
return nextItem;
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) { return stepExecution.getExitStatus(); }
}
package mil.ndms.taabatch.af.aspects;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import mil.ndms.taabatch.af.customexceptions.StepException;
#Aspect
#Configuration
public class ExcepetionAspect
{
public ExcepetionAspect()
{
System.out.println("");
System.out.println("This is the Aspect");
System.out.println("");
}
#AfterThrowing(pointcut = "execution(* mil.ndms.taabatch.af.batchjobs..*.*(..))", throwing = "e")
public void logException(JoinPoint jp, Throwable e) throws Throwable
{
String name = e.getClass().toString().substring(e.getClass().toString().lastIndexOf(".")+1);
switch(name)
{
case "Exception":
{
Throwable[] se = e.getSuppressed();
StepException exception = (null != se && null != se[0] && se[0] instanceof StepException)
? null : (StepException) se[0];
if(null != exception) logStepException(jp, exception); else logOtherException(jp, e);
}
case "StepException": logStepException(jp, (StepException) e);
default: logOtherException(jp, e);
}
}
private void logStepException(JoinPoint joinPoint, StepException e )
{
final Signature signature = joinPoint.getSignature();
final Class<?> clazz = signature.getDeclaringType();
final Logger logger = LoggerFactory.getLogger(clazz);
logger.error("The following Step has errors - " + "[step-name:{}]", e.getStepExecution().getStepName());
e.getStepExecution().getFailureExceptions().forEach(th -> logger.error(th.getMessage()));
return;
}
private void logOtherException(JoinPoint joinPoint, Throwable e )
{
final Signature signature = joinPoint.getSignature();
final Class<?> clazz = signature.getDeclaringType();
final Logger logger = LoggerFactory.getLogger(clazz);
logger.error(e.getMessage());
return;
}
}
<?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.5</version>
<relativePath/>
</parent>
<groupId>mil.ndms.taabatch.af</groupId>
<artifactId>taaBatch</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<name>taaBatch</name>
<description>TAA Batch Application</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</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-starter-web</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-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

My method did not throw and exception. It was handled internally so Spring see it.
Thanks to -kriegaex

Related

Feign Client with Spring Cloud GatewayFilter cause cycle dependency

During certain checks, I need to make a request to my Microservice in the Gateway Filter.
When I define the Feign class in the GatewayFilter(my SecurityFilter.java) class, it gives the following error.
How can I resolve this situation.
Error:
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| securityFilter defined in file [/target/classes/com/example/SecurityFilter.class]
↑ ↓
| cachedCompositeRouteLocator defined in class path resource [org/springframework/cloud/gateway/config/GatewayAutoConfiguration.class]
↑ ↓
| routeDefinitionRouteLocator defined in class path resource [org/springframework/cloud/gateway/config/GatewayAutoConfiguration.class]
└─────┘
Action:
Despite circular references being allowed, the dependency cycle between beans could not be broken. Update your application to remove the dependency cycle.
GatewayFilter class
#Component
public class SecurityFilter implements GatewayFilterFactory<SecurityFilter.Config> {
private final UserApi userApi;
public SecurityFilter(UserApi userApi) {
this.userApi = userApi;
}
#Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
return chain.filter(exchange.mutate().request(exchange.getRequest()).build());
});
}
#Override
public Class<Config> getConfigClass() {
return Config.class;
}
#Override
public Config newConfig() {
Config c = new Config();
return c;
}
public static class Config {
}
}
pom.xml
<properties>
<java.version>17</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</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>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Main class
#SpringBootApplication
#ComponentScan("com.example")
#EnableFeignClients(basePackages = {"com.example.feign"})
public class GatewayApplicationMain {
public static void main(String[] args) {
SpringApplication.run(GatewayApplicationMain.class, args);
}
}
Feign api class
#FeignClient(name = "user", path="userService/", url="http://localhost:8091/")
public interface UserApi {
#GetMapping("/getUserByUserName/{userName}")
ResponseEntity<Object> getUserByUserName(#PathVariable(name = "userName") String userName);
}
Security class
#Configuration
public class SecurityConfig {
#Bean
public SecurityWebFilterChain configure(ServerHttpSecurity http) {
http.csrf().disable()
.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.oauth2Login()
.and().oauth2ResourceServer().jwt();
return http.build();
}
}
application.properties file
server.port=8090
spring.main.allow-circular-references=true
private final UserApi userApi;
#Autowired
public SecurityFilter(#Lazy UserApi userApi) {
this.userApi = userApi;
}
This is how I was able to fix the problem by adding #Lazy.
If you intend to keep the circular dependency you can add this configuration
spring.main.allow-circular-references: true
I'm using #Lazy annotation whenever the feign client intent to be use, to avoid Circular Reference Error when using spring-cloud-gateway along with OpenFeign, even in my CustomGatewayFilter :)
#Component
public class CustomApiGatewayFilter extends AbstractGatewayFilterFactory<CustomApiGatewayFilter.Config> {
private static Logger logger = LogManager.getLogger(CustomApiGatewayFilter.class);
private final JwtConfig jwtConfig;
private final ThirdPartyFeignClientService thirdPartyFeignClientService;
public CustomApiGatewayFilter(JwtConfig jwtConfig, #Lazy ThirdPartyFeignClientService thirdPartyFeignClientService) {
super(Config.class);
this.jwtConfig = jwtConfig;
this.thirdPartyFeignClientService = thirdPartyFeignClientService;
}
....
....
}
And the usage of this feign client placed on a separate function used by this filter
private String validateToken(String jwtToken) throws MyAppException {
String json = "";
ObjectMapper mapper = new ObjectMapper();
try {
Claims claims = jwtConfig.getAllClaimsFromToken(jwtToken);
String additionalInfo = (String) claims.get("additionalInfo");
JwtPayload payload = mapper.readValue(additionalInfo, JwtPayload.class);
String username = jwtConfig.getUsernameFromToken(jwtToken);
if(thirdPartyFeignClientService.validateClient(payload.getClientId(), payload.getClientSecret())){
return username;
}else{
throw new MyAppException("Invalid Client", ErrorCode.GENERIC_FAILURE);
}
} catch (ExpiredJwtException e) {
logger.error(e.getMessage());
throw new MyAppException(e.getMessage(), ErrorCode.GENERIC_FAILURE);
} catch (Exception e) {
logger.error("Other Exception :" + e);
throw new MyAppException (e.getMessage(), ErrorCode.GENERIC_FAILURE);
}
}
And it works :)

Spring Security not working properly in my spring boot project?

I am using spring security with JWT token in my spring boot application and I am trying to use spring security related features(like isauthenticated,hasRole,hasAuthority,etc) in my jsp page but they are not working. Facing same issue in my controller whereas In RESTController, They works fine with #preauthorize tag.
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 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.bdtool</groupId>
<artifactId>BD-Tool</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>BD-Tool</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</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-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</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-web-services</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-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client</artifactId>
<version>1.31.5</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-gson</artifactId>
<version>1.22.0</version>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client-jackson2</artifactId>
<version>1.20.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Here is my config class:-
package com.bdtool.config;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "jwtUserDetailsService")
private UserDetailsService userDetailsService;
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests().antMatchers("/", "/index", "/resources/**", "/JS/**", "/register", "/authenticate", "/login",
"/signout", "/signup", "/forgotPassword", "/submitSignupDetails")
.permitAll().anyRequest().authenticated().and().exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public JwtRequestFilter authenticationTokenFilterBean() throws Exception {
return new JwtRequestFilter();
}
}
Here is my JwtRequestFilter
package com.bdtool.config;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.bdtool.service.JwtTokenBeanService;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.jsonwebtoken.ExpiredJwtException;
#Component
#JsonSerialize
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService JwtUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
JwtTokenBeanService jwtTokenBeanService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ") ) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
System.out.println("From jwt req filter:-"+username);
} catch (IllegalArgumentException e) {
throw new ServletException("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
throw new ServletException("JWT Token has expired");
}
} else {
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.JwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
Here is JwttokenUtil
package com.bdtool.config;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
#Component
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = 1L;
public static final long JWT_TOKEN_VALIDITY = 60 * 60;
#Value("${jwt.secret}")
private String secret;
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userDetails.getUsername());
}
private String doGenerateToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, secret).compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
Userdetailservice class:-
package com.bdtool.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.bdtool.bean.Users;
import com.bdtool.dao.UsersDao;
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UsersDao userdao;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users user=userdao.findUserData(username);
if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}
List<GrantedAuthority> authrole = new ArrayList<GrantedAuthority>() ;
authrole.add(new SimpleGrantedAuthority(user.getUserType()));
return new org.springframework.security.core.userdetails.User(username, user.getUserPassword(),
authrole);
}
}
AuthenticateController :-
package com.bdtool.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.bdtool.bean.Users;
import com.bdtool.config.JwtTokenUtil;
import com.bdtool.config.JwtUserDetailsService;
import com.bdtool.dao.UsersDao;
import com.bdtool.model.JwtRequest;
import com.bdtool.model.JwtResponse;
#RestController
#CrossOrigin
public class AuthenticateController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private JwtUserDetailsService userDetailsService;
#PostMapping(value = "/authenticate")
public ResponseEntity<?> createAuthenticationToken(JwtRequest jwtRequest) {
try {
authenticate(jwtRequest.getUsername(), jwtRequest.getPassword());
final UserDetails userDetails = userDetailsService.loadUserByUsername(jwtRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
e.printStackTrace();
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
e.printStackTrace();
throw new Exception("Invalid Credentials", e);
}
}
}
JwtAuthenticationEntryPoint:-
package com.bdtool.config;
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
AJAX->
$("#somebutton").click(function() {
jQuery.ajax({
url: 'logout',
type: 'GET',
headers: {
'Authorization': 'Bearer ' + sessionStorage.getItem("Token")
},
success: function() {
console.log("ok");
}
});
});
My JSP code:-
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%# taglib prefix="sec"
uri="http://www.springframework.org/security/tags"%>
class="menu-arrow"></span></a>
<ul>
<sec:authorize access="isAuthenticated()">
<li>Project Dashboard</li>
<li>Product Dashboard</li>
</sec:authorize>
</ul></li>
This seams over-complicated to me. I have a sample resource-server retrieving authorities from a JPA repo here and web security config is no more complicated than:
#EnableGlobalMethodSecurity(prePostEnabled = true)
public static class WebSecurityConfig {
#Bean
public ExpressionInterceptUrlRegistryPostProcessor expressionInterceptUrlRegistryPostProcessor() {
return (ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) -> registry
.antMatchers("/secured-route")
.hasRole("AUTHORIZED_PERSONNEL")
.anyRequest()
.authenticated();
}
#Bean
public SynchronizedJwt2AuthenticationConverter<JwtAuthenticationToken> authenticationConverter(
ClaimSet2AuthoritiesConverter<ClaimSet> authoritiesConverter) {
return jwt -> new JwtAuthenticationToken(jwt, authoritiesConverter.convert(new UnmodifiableClaimSet(jwt.getClaims())));
}
#Bean
public ClaimSet2AuthoritiesConverter<ClaimSet> authoritiesConverter(UserAuthorityRepository authoritiesRepo) {
return new PersistedGrantedAuthoritiesRetriever(authoritiesRepo);
}
}
It makes usage of a few #ConditionalOnMissingBean auto-configured by spring-boot, but you can find the list in source (and they are not so many).
PS
A way more efficient approach is adding authorities to JWT access-tokens (inside authorization-server): SQL request is executed only when a new token is emitted, not at each request.
It is easy to map authorities from JWT private claims.
Other PS
The Java version you are using is very deprecated and Spring versions comming this fall will require Java 17 as minimum
Last PS
your boot version is deprecated too and WebSecurityConfigurerAdapter should not be used anymore, in favor of providing a SecurityFiterChain #Bean (this already works in the boot version you use)

AspectJ (Spring) + how to add the embedding of an aspect in a private method and get the specified data

I have created an aspect that should embed logging on methods marked with an annotation and with the private modifier.
In addition, I would like to add information to the log that will be available at the time of execution of the method (for example, the object with which the method works and the name of the method and the class with which it is currently working).
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</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</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
UserController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping
public List<User> getUsers() {
List<User> userList = getUsersInternal();
return userList;
}
#AuditAnnotation()
private List<User> getUsersInternal() {
List<User> allUsers = userService.getAllUsers();
return allUsers;
}
}
annotation
#Retention(RUNTIME)
#Target(METHOD)
#Documented
public #interface AuditAnnotation {
public String nameMethod() default "";
}
loggingService
public interface LoggingService {
void log(String message);
}
/**
* A dummy implementation of logging service,
* just to inject it in {#link com.aspectj.in.spring.boot.aop.aspect.auditlog.interceptor.LoggingInterceptorAspect}
* that's managed by AspectJ
*/
#Service
public class DefaultLoggingService implements LoggingService {
private static final Logger logger = LoggerFactory.getLogger("sample-spring-aspectj");
#Override
public void log(String message) {
logger.info(message);
}
}
aspect
#Aspect
#Component
public class LoggingInterceptorAspect {
#Autowired
private LoggingService loggingService;
#Pointcut("execution(private * *(..))")
public void privateMethod() {}
#Pointcut("#annotation(com.aspectj.in.spring.boot.aop.aspect.auditlog.annotation.AuditAnnotation)")
public void annotatedMethodCustom() {}
#Before("annotatedMethodCustom() && privateMethod()")
public void addCommandDetailsToMessage() throws Throwable {
ZonedDateTime dateTime = ZonedDateTime.now(ZoneOffset.UTC);
String message = String.format("User controller getUsers method called at %s", dateTime);
System.out.println("+++++++++++++++++++++++++");
loggingService.log(message);
}
}
configuratinAspect
#Configuration
public class LoggingInterceptorConfig {
#Bean
public LoggingInterceptorAspect getAutowireCapableLoggingInterceptor() {
return Aspects.aspectOf(LoggingInterceptorAspect.class);
}
}
test
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class AspectjInSpringBootApplicationTests {
#Autowired
protected TestRestTemplate testRestTemplate;
}
class UserControllerTest extends AspectjInSpringBootApplicationTests {
#Test
void getUsers() {
String url = "/v1/users";
ParameterizedTypeReference<List<User>> typeReference =
new ParameterizedTypeReference<>() {
};
ResponseEntity<List<User>> responseEntity =
testRestTemplate.exchange(url, HttpMethod.GET, null, typeReference);
HttpStatus statusCode = responseEntity.getStatusCode();
assertThat(statusCode, is(HttpStatus.OK));
List<User> employeeDtoList = responseEntity.getBody();
System.out.println(employeeDtoList);
}
}
But at the moment I have no errors .
so far, I see that the aspect is embedded,
but I want it to be detailed so that the aspect is universal and I would not have to explicitly specify in the message in which class it works.
Maybe someone has ideas on how to fix it.
I suggest you to explore the AspectJ documentation and the JoinPoint API, too. Here is a little example in stand-alone AspectJ without Spring. You can adjust it to your needs:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Application application = new Application();
System.out.println(application.add(4, application.multiply(2, 3)));
application.divide(5, 0);
}
private int add(int i, int j) {
return i + j;
}
private int multiply(int i, int j) {
return i * j;
}
private double divide(int i, int j) {
return i / j;
}
}
package de.scrum_master.aspect;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class MyAspect {
#Before("execution(private * *(..))")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
System.out.println(" Signature: " + joinPoint.getSignature());
System.out.println(" Target: " + joinPoint.getTarget());
System.out.println(" Arguments: " + Arrays.deepToString(joinPoint.getArgs()));
}
#AfterReturning(pointcut = "execution(private * *(..))", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println(" Result: " + result);
}
#AfterThrowing(pointcut = "execution(private * *(..))", throwing = "exception")
public void afterThrowingAdvice(JoinPoint joinPoint, Throwable exception) {
System.out.println(" Exception: " + exception);
}
}
Console log:
Picked up _JAVA_OPTIONS: -Djava.net.preferIPv4Stack=true
execution(int de.scrum_master.app.Application.multiply(int, int))
Signature: int de.scrum_master.app.Application.multiply(int, int)
Target: de.scrum_master.app.Application#7cdbc5d3
Arguments: [2, 3]
Result: 6
execution(int de.scrum_master.app.Application.add(int, int))
Signature: int de.scrum_master.app.Application.add(int, int)
Target: de.scrum_master.app.Application#7cdbc5d3
Arguments: [4, 6]
Result: 10
10
execution(double de.scrum_master.app.Application.divide(int, int))
Signature: double de.scrum_master.app.Application.divide(int, int)
Target: de.scrum_master.app.Application#7cdbc5d3
Arguments: [5, 0]
Exception: java.lang.ArithmeticException: / by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
at de.scrum_master.app.Application.divide(Application.java:19)
at de.scrum_master.app.Application.main(Application.java:7)
In addition to the context data I printed here, you can also get annotations and their properties, method parameter names (even though I think that is unnecessary and availability depends on compilation options) etc.

JavaFX 12 maven and Spring

I'm trying to add spring to my pom
but I'm wondering if my build will look like this.
Could someone help me how can I do this spring integration with java fx?
my pom:
<properties>
<java.version>12</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>12.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>12.0.2</version>
</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-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</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>
main class:
public class App extends Application {
private static Scene scene;
#Override
public void start(Stage stage) throws IOException {
scene = new Scene(loadFXML("primary"));
stage.setScene(scene);
stage.show();
}
static void setRoot(String fxml) throws IOException {
scene.setRoot(loadFXML(fxml));
}
private static Parent loadFXML(String fxml) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
return fxmlLoader.load();
}
public static void main(String[] args) {
launch();
} }
I searched and found nothing about integrating javafx with spring
Could someone help me how I can integrate spring?
I tried to use what I used in my swing project but it didn't work out
Your main app has to maintain Spring's application context like this.
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
#Configuration // or #SpringBootApplication if you used spring boot.
public class App extends Application {
private static Scene scene;
private AbstractApplicationContext context;
#Override
public void init() throws Exception {
this.context = new AnnotationConfigApplicationContext(App.class);
super.init();
}
#Override
public void start(Stage stage) throws IOException {
SpringFxmlLoader loader = new SpringFxmlLoader(this.context);
Parent root = loader.loadFromResource("primary.fxml");
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
use SpringFxmlLoader for returning a controller as a spring bean.
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.springframework.context.ApplicationContext;
import javafx.fxml.FXMLLoader;
public class SpringFxmlLoader extends FXMLLoader {
private ApplicationContext context;
protected SpringFxmlLoader() {
}
public SpringFxmlLoader(ApplicationContext context) {
this.context = context;
setControllerFactory(context::getBean); // return a spring bean from spring's application context.
}
public ApplicationContext getContext() {
return this.context;
}
public <T> T loadFromResource(String resource) throws IOException {
return loadFromResource(resource, null);
}
public <T> T loadFromResource(String resource, Class<?> root) throws IOException {
URL resourceURL = context.getResource(resource).getURL();
setLocation(resourceURL);
if(root != null) setRoot(root);
try (InputStream fxml = resourceURL.openStream()) {
return load(fxml);
}
}
}
Your controller should be looked like this as a spring bean.
#Controller
#Scope("prototype")
public class PrimaryController {
#FXML private Parent rootView;
#FXML private TitledPane settingPane;
#Autowired private SomeBean someBean; // now you can autowire spring beans.
// braa....
}
Don't forget specify the controller class in your primary.fxml with fx:controller attribute.

Cucumber: no backend found when running from Spring Boot jar

I am creating a small testing framework that should utilize both Cucumber and the Spring Boot platform. The idea is to let the whole application be packaged as a single jar and run after the BDD features have been properly parametrized.
The framework starts in command line runner mode like this:
public class FwApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(FwApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
JUnitCore.main(CucumberIntegration.class.getCanonicalName());
}
}
Then there is the CucumberIntegration class:
#RunWith(Cucumber.class)
#CucumberOptions(features = "config/features")
#ContextConfiguration(classes= AppConfiguration.class)
public class CucumberIntegration {
}
I have also some simple tests which run fine under my IDE, but when I try to package the application and run it over java -jar fw-0.0.1-SNAPSHOT.jar I get to see following:
There was 1 failure:
1) initializationError(com.fmr.bddfw.test.CucumberIntegration)
cucumber.runtime.CucumberException: No backends were found. Please make sure you have a backend module on your CLASSPATH.
at cucumber.runtime.Runtime.<init>(Runtime.java:81)
at cucumber.runtime.Runtime.<init>(Runtime.java:70)
(...)
All necessary jars are already in the one jar created by maven and it works fine under my IDE.
Any ideas what could help?
EDIT: Here my pom file.
<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>
<groupId>com.xxx</groupId>
<artifactId>fw</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>fw</name>
<description>BDD Testing Framework</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<cucumber.version>1.2.5</cucumber.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Using the suggestion given by Marcus:
Step1: Create your custom MultiLoader class:
package cucumber.runtime.io;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
public class CustomMultiLoader implements ResourceLoader {
public static final String CLASSPATH_SCHEME = "classpath*:";
public static final String CLASSPATH_SCHEME_TO_REPLACE = "classpath:";
private final ClasspathResourceLoader classpath;
private final FileResourceLoader fs;
public CustomMultiLoader(ClassLoader classLoader) {
classpath = new ClasspathResourceLoader(classLoader);
fs = new FileResourceLoader();
}
#Override
public Iterable<Resource> resources(String path, String suffix) {
if (isClasspathPath(path)) {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
String locationPattern = path.replace(CLASSPATH_SCHEME_TO_REPLACE, CLASSPATH_SCHEME) + "/**/*" + suffix;
org.springframework.core.io.Resource[] resources;
try {
resources = resolver.getResources(locationPattern);
} catch (IOException e) {
resources = null;
e.printStackTrace();
}
return convertToCucumberIterator(resources);
} else {
return fs.resources(path, suffix);
}
}
private Iterable<Resource> convertToCucumberIterator(org.springframework.core.io.Resource[] resources) {
List<Resource> results = new ArrayList<Resource>();
for (org.springframework.core.io.Resource resource : resources) {
results.add(new ResourceAdapter(resource));
}
return results;
}
public static String packageName(String gluePath) {
if (isClasspathPath(gluePath)) {
gluePath = stripClasspathPrefix(gluePath);
}
return gluePath.replace('/', '.').replace('\\', '.');
}
private static boolean isClasspathPath(String path) {
if (path.startsWith(CLASSPATH_SCHEME_TO_REPLACE)) {
path = path.replace(CLASSPATH_SCHEME_TO_REPLACE, CLASSPATH_SCHEME);
}
return path.startsWith(CLASSPATH_SCHEME);
}
private static String stripClasspathPrefix(String path) {
if (path.startsWith(CLASSPATH_SCHEME_TO_REPLACE)) {
path = path.replace(CLASSPATH_SCHEME_TO_REPLACE, CLASSPATH_SCHEME);
}
return path.substring(CLASSPATH_SCHEME.length());
}
}
Step2: Create an adapter between org.springframework.core.io.Resource and cucumber.runtime.io.Resource:
package cucumber.runtime.io;
import java.io.IOException;
import java.io.InputStream;
public class ResourceAdapter implements Resource {
org.springframework.core.io.Resource springResource;
public ResourceAdapter(org.springframework.core.io.Resource springResource) {
this.springResource = springResource;
}
public String getPath() {
try {
return springResource.getFile().getPath();
} catch (IOException e) {
try {
return springResource.getURL().toString();
} catch (IOException e1) {
e1.printStackTrace();
return "";
}
}
}
public String getAbsolutePath() {
try {
return springResource.getFile().getAbsolutePath();
} catch (IOException e) {
return null;
}
}
public InputStream getInputStream() throws IOException {
return this.springResource.getInputStream();
}
public String getClassName(String extension) {
String path = this.getPath();
if (path.startsWith("jar:")) {
path = path.substring(path.lastIndexOf("!") + 2);
return path.substring(0, path.length() - extension.length()).replace('/', '.');
} else {
path = path.substring(path.lastIndexOf("classes") + 8);
return path.substring(0, path.length() - extension.length()).replace('\\', '.');
}
}
}
Step3: Create your custom main class that uses your CustomMultiLoader:
package cucumber.runtime.io;
import static java.util.Arrays.asList;
import java.io.IOException;
import java.util.ArrayList;
import cucumber.runtime.ClassFinder;
import cucumber.runtime.Runtime;
import cucumber.runtime.RuntimeOptions;
public class CucumberStaticRunner {
public static void startTests(String[] argv) throws Throwable {
byte exitstatus = run(argv, Thread.currentThread().getContextClassLoader());
System.exit(exitstatus);
}
public static byte run(String[] argv, ClassLoader classLoader) throws IOException {
RuntimeOptions runtimeOptions = new RuntimeOptions(new ArrayList<String>(asList(argv)));
ResourceLoader resourceLoader = new CustomMultiLoader(classLoader);
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
runtime.run();
return runtime.exitStatus();
}
}
Step4: Call your custom main class instead of cucumber.api.cli.Main.main:
String[] cucumberOptions = { "--glue", "my.test.pack", "--no-dry-run", "--monochrome", "classpath:features" };
CucumberStaticRunner.startTests(cucumberOptions);
Fixed it by following configuration:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<requiresUnpack>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>
You should add cucumber-java dependency
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
private static byte run(String[] argv, ClassLoader classLoader) throws IOException {
// cucumber/Spring Boot classloader problem
// CucumberException: No backends were found. Please make sure you have a backend module on your CLASSPATH
RuntimeOptions runtimeOptions = new RuntimeOptions(new ArrayList<String>(asList(argv)));
ResourceLoader resourceLoader = new MultiLoader(classLoader);
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
Reflections reflections = new Reflections(classFinder);
List<Backend> list = new ArrayList<>();
list.addAll(reflections.instantiateSubclasses(Backend.class, "cucumber.runtime", new Class[]{ResourceLoader.class}, new Object[]{resourceLoader}));
if (list.size() == 0) {
JavaBackend javaBackend = new JavaBackend(resourceLoader);
list.add(javaBackend);
}
Runtime runtime = new Runtime(resourceLoader, classLoader, list, runtimeOptions);
runtime.run();
return runtime.exitStatus();
}
I used below code in POM.xml file
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>1.2.4</version>
<scope>test</scope>
</dependency>
It is working fine now.

Resources