#RestControllerAdvice is not working .I am using Spring boot 2.2.6 version - spring-boot

I am trying to handle Null Pointer exception in my controller through global exception handler but is not returning the expected response.I have enclosed all the classes here :
below code for reference:
Controller:
package com.apps.developerblog.app.ws.ui.controller;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.apps.developerblog.app.ws.ui.model.request.UpdateUserRequestModel;
import com.apps.developerblog.app.ws.ui.model.request.UserDetailsRequestModel;
import com.apps.developerblog.app.ws.ui.model.response.UserRest;
import com.apps.developerblog.ws.exception.UserServiceException;
#RestController
#RequestMapping("/users") // http://localhost
public class UserController {
Map<String, UserRest> users;
#GetMapping(path = "/{userId}", produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<UserRest> getUser(#PathVariable("userId") String userId) {
if (true)
throw new UserServiceException("A user serviec exception is thrown");
if (users.containsKey(userId)) {
return new ResponseEntity<UserRest>(users.get(userId), HttpStatus.OK);
} else {
return new ResponseEntity<UserRest>(HttpStatus.NO_CONTENT);
}
}
/*
* #GetMapping public String getUsers(#RequestParam(value = "page", defaultValue
* = "2") int page,
*
* #RequestParam(value = "limit") int limit,
*
* #RequestParam(value = "sort", defaultValue = "desc", required = false) String
* sort) { return "get User was called with page=" + page + " limit =" + limit +
* " and sort=" + sort; }
*/
#PostMapping(consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = {
MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<UserRest> createUser(#Valid #RequestBody UserDetailsRequestModel userDetails) {
String userId = UUID.randomUUID().toString();
UserRest returnValue = new UserRest();
returnValue.setEmail(userDetails.getEmail());
returnValue.setFirstName(userDetails.getFirstName());
returnValue.setLastName(userDetails.getLastName());
returnValue.setUserId(userId);
if (users == null)
users = new HashMap<>();
users.put(userId, returnValue);
return new ResponseEntity<UserRest>(returnValue, HttpStatus.OK);
}
#PutMapping(path = "/{userId}", consumes = { MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE })
public UserRest updateUser(#PathVariable String userId,
#Valid #RequestBody UpdateUserRequestModel userDetailsRequestModel) {
UserRest storedUserDetails = users.get(userId);
storedUserDetails.setFirstName(userDetailsRequestModel.getFirstName());
storedUserDetails.setLastName(userDetailsRequestModel.getLastName());
users.put(userId, storedUserDetails);
return storedUserDetails;
}
#DeleteMapping(path = "/{userId}")
public ResponseEntity<String> deleteUser(#PathVariable String userId) {
if (users.containsKey(userId)) {
users.remove(userId);
return new ResponseEntity<String>("User Deleted ", HttpStatus.OK);
} else {
return new ResponseEntity<String>("User Not Deleted ", HttpStatus.NOT_FOUND);
}
}
}
GlobalExceptional:
package com.apps.developerblog.ws.exception;
import javax.validation.ConstraintViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
/**
*
* #author
*/
#RestControllerAdvice
public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ApiErrorResponse constraintViolationException(ConstraintViolationException ex, WebRequest request) {
return new ApiErrorResponse(500, 5001, ex.getMessage());
}
#ExceptionHandler(value = { NoHandlerFoundException.class })
#ResponseStatus(HttpStatus.NOT_FOUND)
public ApiErrorResponse noHandlerFoundException(Exception ex, WebRequest reques) {
return new ApiErrorResponse(404, 4041, ex.getMessage());
}
#ExceptionHandler(value = { Exception.class })
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ApiErrorResponse unknownException(Exception ex, WebRequest reques) {
return new ApiErrorResponse(500, 5002, ex.getMessage());
}
#ExceptionHandler(value = { NullPointerException.class })
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ApiErrorResponse nullPointerException(NullPointerException ex, WebRequest reques) {
return new ApiErrorResponse(500, 5002, ex.getMessage());
}
#ExceptionHandler(value = { UserServiceException.class })
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ApiErrorResponse UserServiceException(UserServiceException ex, WebRequest reques) {
return new ApiErrorResponse(500, 5002, ex.getMessage());
}
}
package com.apps.developerblog.ws.exception;
/**
* #author
*/
public class ApiErrorResponse {
private int status;
private int code;
private String message;
public ApiErrorResponse(int status, int code, String message) {
this.status = status;
this.code = code;
this.message = message;
}
public int getStatus() {
return status;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
#Override
public String toString() {
return "ApiErrorResponse{" + "status=" + status + ", code=" + code + ", message=" + message + '}';
}
}
Error class:
package com.apps.developerblog.ws.exception;
public class UserServiceException extends RuntimeException {
private static final long serialVersionUID = 1L;
public UserServiceException(String message) {
super(message);
}
}
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.2.6.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>mobile-app-ws</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mobile-app-ws</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-web</artifactId>
</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>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml/jackson-xml-databind -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

Please try adding following code in your context, your question is very abstract and incomplete.
#ControllerAdvice
class StatusControllerAdvice {
#ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> notFoundException(final RuntimeException e) {
// return new ResponseEntity<String>("false", HttpStatus.INTERNAL_SERVER_ERROR);
return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#RestController
#RequestMapping("/status")
public class StatusController {
#GetMapping("/string")
public String getString() {
return "OK";
}
#GetMapping(value = "/file")
public String getImage() {
File file = new File("/home/ashish1");
if (file.exists()) {
return "true";
}
throw new RuntimeException("Invalid File");
}
}
How to access this url:
http://localhost:8080/status/file

Related

When I run spring boot test cases I got error I could not resolve on my own

I am creating test cases for my controller
package com.bank.controller;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.http.MediaType;
import com.bank.model.Account;
import com.bank.model.Customer;
import com.bank.service.AccountService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
#RunWith(SpringRunner.class)
#WebMvcTest(value=AccountController.class)
class AccountControllerTest{
#Autowired
private MockMvc mockMvc;
#MockBean
private AccountService accountService;
#Test
public void testcreateAccount() throws Exception{
Customer customer=new Customer();
customer.setCustomerId(1);
customer.setCustomerName("Manasa");
Account mockAccount=new Account();
mockAccount.setAccountNumber(456);
mockAccount.setBalance(2000.0);
mockAccount.setAccountType("savings");
mockAccount.setCustomer(customer);
String inputInJson=this.mapToJson(mockAccount);
String URI="/account";
Mockito.when(accountService.accountCreate(Mockito.any(Account.class))).thenReturn(mockAccount);
RequestBuilder requestBuilder=MockMvcRequestBuilders
.post(URI)
.accept(MediaType.APPLICATION_JSON).content(inputInJson)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result=mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response=result.getResponse();
String outputInJson=response.getContentAsString();
assertThat(outputInJson).isEqualTo(outputInJson);
assertEquals(HttpStatus.OK.value(),response.getStatus());
}
#Test
public void testaccountList() throws Exception{
Customer customer1=new Customer();
customer1.setCustomerId(1);
customer1.setCustomerName("Manasa");
Account mockAccount1=new Account();
mockAccount1.setAccountNumber(456);
mockAccount1.setAccountType("savings");
mockAccount1.setBalance(2000.0);
mockAccount1.setCustomer(customer1);
Customer customer2=new Customer();
customer2.setCustomerId(2);
customer2.setCustomerName("Madhu");
Account mockAccount2=new Account();
mockAccount2.setAccountNumber(789);
mockAccount2.setAccountType("savings");
mockAccount2.setBalance(6000.0);
mockAccount2.setCustomer(customer2);
Customer customer3=new Customer();
customer3.setCustomerId(3);
customer3.setCustomerName("Lalasa");
Account mockAccount3=new Account();
mockAccount3.setAccountNumber(123);
mockAccount3.setAccountType("savings");
mockAccount3.setBalance(50000.0);
mockAccount3.setCustomer((Customer)Arrays.asList(3,"Lalasa"));
List<Account> accountList=new ArrayList<>();
accountList.add(mockAccount1);
accountList.add(mockAccount2);
accountList.add(mockAccount3);
Mockito.when(accountService.allAccounts()).thenReturn(accountList);
String URI="/account/aclist";
RequestBuilder requestBuilder=MockMvcRequestBuilders.get(
URI).accept(MediaType.APPLICATION_JSON);
MvcResult result=mockMvc.perform(requestBuilder).andReturn();
String expectedJson=this.mapToJson(accountList);
String outputInJson=result.getResponse().getContentAsString();
assertThat(outputInJson).isEqualTo(expectedJson);
}
#Test
public void testbyAccountType() throws Exception{
Customer customer=new Customer();
customer.setCustomerId(1);
customer.setCustomerName("Manasa");
Account mockAccount=new Account();
mockAccount.setAccountNumber(456);
mockAccount.setAccountType("savings");
mockAccount.setBalance(2000.0);
mockAccount.setCustomer(customer);
String expectedJson=this.mapToJson(mockAccount);
Mockito.when(accountService.accountByType(Mockito.anyString())).thenReturn((List<Account>) mockAccount);
String URI="/account//byType/savings";
RequestBuilder requestBuilder=MockMvcRequestBuilders.get(
URI).accept(MediaType.APPLICATION_JSON);
MvcResult result=mockMvc.perform(requestBuilder).andReturn();
String outputInJson=result.getResponse().getContentAsString();
assertThat(outputInJson).isEqualTo(expectedJson);
}
private String mapToJson(Object object) throws JsonProcessingException{
ObjectMapper objectMapper=new ObjectMapper();
return objectMapper.writeValueAsString(object);
}
}
My Controller class is
package com.bank.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bank.model.Account;
import com.bank.service.AccountService;
import com.bank.Exception.ResourceNotFoundException;
#RestController
#RequestMapping("/account")
public class AccountController {
#Autowired
AccountService accountService;
#PostMapping
public ResponseEntity<?> createAccount(#RequestBody Account account) throws ResourceNotFoundException{
Account acc=accountService.accountCreate(account);
if(acc!=null) {
return new ResponseEntity<>(acc,HttpStatus.CREATED);
}else {
throw new ResourceNotFoundException("Account is not Created!!");
}
}
#GetMapping("/aclist")
public ResponseEntity<?> accountList() throws ResourceNotFoundException{
List<Account> accountList=accountService.allAccounts();
if(!(accountList.isEmpty())) {
return new ResponseEntity<>(accountList,HttpStatus.OK);
}else {
throw new ResourceNotFoundException("accounts not found");
}
}
#GetMapping("/byType/{accountType}")
public ResponseEntity<?> byAccountType(#PathVariable String accountType) throws ResourceNotFoundException{
List<Account> account =accountService.accountByType(accountType);
if(account.isEmpty()) {
throw new ResourceNotFoundException(accountType+""+"Type of account does not exist!!");
}else {
return new ResponseEntity<>(account,HttpStatus.OK);
}
}
#GetMapping("/{accountNumber}")
public ResponseEntity<?> getAccountById(#PathVariable("accountNumber") int accountNumber) throws ResourceNotFoundException{
Account acc=accountService.findAccountById(accountNumber);
if(acc!=null) {
return new ResponseEntity<>(acc,HttpStatus.OK);
}else {
throw new ResourceNotFoundException("account [accountNumber="+accountNumber+"] can't be found");
}
}
#PutMapping("/{from}/{to}/{amount}")
public ResponseEntity<?> transferFunds(#PathVariable("from") int from,#PathVariable("to") int to,#PathVariable("amount") double amount) throws ResourceNotFoundException{
return accountService.transferFunds(from, to, amount);
}
#DeleteMapping("/{accountNumber}")
public ResponseEntity<?> deleteAccById(#PathVariable ("accountNumber") int accountNumber) throws ResourceNotFoundException{
String x=accountService.deleteById(accountNumber);
if(x.equalsIgnoreCase("deleted")){
return new ResponseEntity<>("deleted successfully",HttpStatus.OK);
}else {
throw new ResourceNotFoundException("Account [accountNumber="+accountNumber+"] can't be found");
}
}
#GetMapping("/balance/{accountNumber}")
public ResponseEntity<?> getBalanceById(#PathVariable ("accountNumber") int accountNumber) throws ResourceNotFoundException{
String balance=accountService.getBalanceById(accountNumber);
if(balance.equalsIgnoreCase("Invalid accountNumber")) {
throw new ResourceNotFoundException("Account [accountNumber="+accountNumber+"] can't be found");
}else {
return new ResponseEntity<>(balance,HttpStatus.OK);
}
}
#DeleteMapping("/deleteAll")
public ResponseEntity<?> deleteAllAccounts(){
return new ResponseEntity<>(accountService.deleteAllAccounts(),HttpStatus.OK);
}
#PutMapping("/update/{accountNumber}")
public ResponseEntity<?> UpdateAccount(#PathVariable ("accountNumber") int accountNumber,#RequestBody Account account) throws ResourceNotFoundException{
Account acc=accountService.updateAccount(accountNumber, account);
if(acc!=null) {
return new ResponseEntity<>(acc,HttpStatus.OK);
}else {
throw new ResourceNotFoundException("invalid accountNumber and account");
}
}
#PutMapping("/deposite/{amount}/{accountNumber}")
public ResponseEntity<?> deposite(#PathVariable("amount") double amount,#PathVariable("accountNumber") int accountNumber) throws ResourceNotFoundException{
return accountService.deposite(amount,accountNumber);
}
#PutMapping("/withdraw/{amount}/{accountNumber}")
public ResponseEntity<?> withDraw(#PathVariable("amount") double amount,#PathVariable("accountNumber") int accountNumber) throws ResourceNotFoundException{
return accountService.withdraw(amount,accountNumber);
}
}
My model class is (has many to one relations with customer class)
package com.bank.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Proxy;
#Entity
#Table(name="Account")
#Proxy(lazy = false)
public class Account {
#Id
int accountNumber;
#Column(name="balance")
Double balance;
#Column(name="accountType")
String accountType;
#ManyToOne( targetEntity=Customer.class,cascade = CascadeType.ALL)
#JoinColumn(name="ca_fk",referencedColumnName = "customerId")
Customer customer;
public Account() {
super();
}
public Account(int accountNumber, Double balance, String accountType, Customer customer) {
super();
this.accountNumber = accountNumber;
this.balance = balance;
this.accountType = accountType;
this.customer = customer;
}
#Override
public String toString() {
return "Account [accountNumber=" + accountNumber + ", balance=" + balance + ", accountType=" + accountType
+ ", customer=" + customer + "]";
}
public Integer getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(int accountNumber) {
this.accountNumber = accountNumber;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
public String getAccountType() {
return accountType;
}
public void setAccountType(String accountType) {
this.accountType = accountType;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
customer class
package com.bank.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Proxy;
#Entity
#Table(name="Customer")
#Proxy(lazy = false)
public class Customer {
#Id
Integer customerId;
#Column(name="customerName")
String customerName;
public Customer() {
super();
}
public Customer(int customerId, String customerName) {
super();
this.customerId = customerId;
this.customerName = customerName;
}
#Override
public String toString() {
return "Customer [customerId=" + customerId + ", customerName=" + customerName + "]";
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
}
I got the below error
MockHttpServletRequest:
HTTP Method = POST
Request URI = /account
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"114"]
Body = {"accountNumber":456,"balance":2000.0,"accountType":"savings","customer":{"customerId":1,"customerName":"Manasa"}}
Session Attrs = {}
Handler:
Type = com.bank.controller.AccountController
Method = com.bank.controller.AccountController#createAccount(Account)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"accountNumber":456,"balance":2000.0,"accountType":"savings","customer":{"customerId":1,"customerName":"Manasa"}}
Forwarded URL = null
Redirected URL = null
Cookies = []
2022-03-20 02:11:12.422 WARN 12996 --- [ main] o.s.test.context.TestContextManager : Caught exception while invoking 'afterTestMethod' callback on TestExecutionListener [org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener#485e36bc] for test method [public void com.bank.controller.AccountControllerTest.testbyAccountType() throws java.lang.Exception] and test instance [com.bank.controller.AccountControllerTest#153cfd86]
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.bank.controller.AccountControllerTest.testbyAccountType(AccountControllerTest.java:139)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3. you are stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed
at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.resetMocks(ResetMocksTestExecutionListener.java:83) ~[spring-boot-test-2.6.4.jar:2.6.4]
at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.resetMocks(ResetMocksTestExecutionListener.java:70) ~[spring-boot-test-2.6.4.jar:2.6.4]
at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.afterTestMethod(ResetMocksTestExecutionListener.java:64) ~[spring-boot-test-2.6.4.jar:2.6.4]
at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:445) ~[spring-test-5.3.16.jar:5.3.16]
at org.springframework.test.context.junit.jupiter.SpringExtension.afterEach(SpringExtension.java:206) ~[spring-test-5.3.16.jar:5.3.16]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAfterEachCallbacks$12(TestMethodTestDescriptor.java:257) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAllAfterMethodsOrCallbacks$13(TestMethodTestDescriptor.java:273) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.8.2.jar:1.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAllAfterMethodsOrCallbacks$14(TestMethodTestDescriptor.java:273) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) ~[na:na]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeAllAfterMethodsOrCallbacks(TestMethodTestDescriptor.java:272) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeAfterEachCallbacks(TestMethodTestDescriptor.java:256) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:141) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) ~[junit-platform-engine-1.8.2.jar:1.8.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.8.2.jar:1.8.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.8.2.jar:1.8.2]
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.8.2.jar:1.8.2]
My pom.xml is
<?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.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bank</groupId>
<artifactId>capstoneBankProject</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>capstoneBankProject</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<junit.version>4.13.1</junit.version>
</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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</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>
<!-- exclude junit 4 -->
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
my project structure
You are casting mockAccount to a list without it actually being a list, that's probably why the mock fails.

Error creating bean with name 'activityController'

I'm having this problem while running on Server (Tomcat) my project. I have created a Maven project using Spring. The Java version is Java 1.8.
NO SPRING-BOOT!
Error creating bean with name 'activityController': Unsatisfied dependency expressed through field 'activityService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'activityService': Unsatisfied dependency expressed through field 'dao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.calendar.repository.ActivityDAO' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Could you please help me? Thank you!
This is the code.
Thank you!!!
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>
<groupId>com.calendar</groupId>
<artifactId>calendar</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Calendar</name>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<springframework.version>5.1.4.RELEASE</springframework.version>
<jackson.version>2.11.2</jackson.version>
<email.version>1.6.2</email.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.4.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.1.Final</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.0</version>
</dependency>
</dependencies>
<repositories>
</repositories>
<build>
<finalName>company-calendar</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Activity.java
package com.calendar.entity;
import java.io.Serializable;
import javax.persistence.*;
import java.sql.Timestamp;
import java.util.List;
/**
* The persistent class for the activity database table.
*
*/
#Entity
#NamedQuery(name = "Activity.findAll", query = "SELECT a FROM Activity a")
public class Activity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Integer id;
#Column(name = "create_time")
private Timestamp createTime;
#Column(name = "created_by")
private String createdBy;
private String description;
#Column(name = "update_time")
private Timestamp updateTime;
#Column(name = "updated_by")
private String updatedBy;
//bi-directional many-to-one association to CalendarHeader
#OneToMany(mappedBy = "activity")
private List<CalendarHeader> calendarHeaders;
public Activity() {
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public Timestamp getCreateTime() {
return this.createTime;
}
public void setCreateTime(Timestamp createTime) {
this.createTime = createTime;
}
public String getCreatedBy() {
return this.createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public Timestamp getUpdateTime() {
return this.updateTime;
}
public void setUpdateTime(Timestamp updateTime) {
this.updateTime = updateTime;
}
public String getUpdatedBy() {
return this.updatedBy;
}
public void setUpdatedBy(String updatedBy) {
this.updatedBy = updatedBy;
}
public List<CalendarHeader> getCalendarHeaders() {
return this.calendarHeaders;
}
public void setCalendarHeaders(List<CalendarHeader> calendarHeaders) {
this.calendarHeaders = calendarHeaders;
}
public CalendarHeader addCalendarHeader(CalendarHeader calendarHeader) {
getCalendarHeaders().add(calendarHeader);
calendarHeader.setActivity(this);
return calendarHeader;
}
public CalendarHeader removeCalendarHeader(CalendarHeader calendarHeader) {
getCalendarHeaders().remove(calendarHeader);
calendarHeader.setActivity(null);
return calendarHeader;
}
}
ActivityDAO.java
package com.calendar.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.calendar.entity.Activity;
#Repository
public interface ActivityDAO extends JpaRepository<Activity, Integer> {}
**ActivityDTO.java**
package com.calendar.dto;
import java.sql.Timestamp;
public class ActivityDTO {
private Integer id;
private String description;
private Timestamp createTime;
private String createdBy;
private Timestamp updateTime;
private String updatedBy;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Timestamp getCreateTime() {
return createTime;
}
public void setCreateTime(Timestamp createTime) {
this.createTime = createTime;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Timestamp getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Timestamp updateTime) {
this.updateTime = updateTime;
}
public String getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(String updatedBy) {
this.updatedBy = updatedBy;
}
}
ActivityService.java
package com.calendar.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.calendar.dto.ActivityDTO;
import com.calendar.repository.ActivityDAO;
import com.calendar.entity.Activity;
#Service
public class ActivityService {
#Autowired
ActivityDAO dao;
public List<ActivityDTO> findAll() {
List<ActivityDTO> DTOList = new ArrayList<ActivityDTO>();
List<Activity> entity = dao.findAll();
if (entity != null) {
for (Activity e : entity) {
DTOList.add(getDTOFromEntity(e));
}
}
return DTOList;
};
protected ActivityDTO getDTOFromEntity(Activity entity) {
ActivityDTO activityDTO = new ActivityDTO();
activityDTO.setId(entity.getId());
activityDTO.setDescription(entity.getDescription());
activityDTO.setCreatedBy(entity.getCreatedBy());
activityDTO.setCreateTime(entity.getCreateTime());
activityDTO.setUpdatedBy(entity.getUpdatedBy());
activityDTO.setUpdateTime(entity.getUpdateTime());
return activityDTO;
}
}
ActivityController.java
package com.calendar.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.calendar.dto.ActivityDTO;
import com.calendar.service.ActivityService;
#RestController
#RequestMapping("activity")
public class ActivityController {
#Autowired
private ActivityService activityService;
private ActivityService getService() {
System.out.println("service= " + activityService);
return activityService;
}
#Transactional(readOnly = true)
#GetMapping("")
#ResponseBody
public List<ActivityDTO> findAll() {
System.out.println("SIL findAll");
return getService().findAll();
}
}
AppConfig.java
package com.calendar.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.LocalEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan(basePackages = "com.calendar")
public class AppConfig implements WebMvcConfigurer {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//to see html files
configurer.enable();
}
//Entity manager
#Bean(name = "Calendar_PU")
public LocalEntityManagerFactoryBean getEntityManagerFactoryBean() {
LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName("Calendar_PU");
return factoryBean;
}
}
Regards,
Francesco
Try adding #EnableJpaRepositories to your AppConfig:
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "com.calendar.repository")
#ComponentScan(basePackages = "com.calendar")
public class AppConfig implements WebMvcConfigurer {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//to see html files
configurer.enable();
}
//Entity manager
#Bean(name = "Calendar_PU")
public LocalEntityManagerFactoryBean getEntityManagerFactoryBean() {
LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName("Calendar_PU");
return factoryBean;
}
}
You also need to change your pom.xml. There are too many dependencies (most of them are already part of spring-data-jpa) and spring-data-jpa requires Spring 5.3.5.
<?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>
<groupId>com.calendar</groupId>
<artifactId>calendar</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Calendar</name>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<springframework.version>5.3.5</springframework.version>
<jackson.version>2.11.2</jackson.version>
<email.version>1.6.2</email.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.4.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.0</version>
</dependency>
</dependencies>
<repositories>
</repositories>
<build>
<finalName>company-calendar</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
This is why you should consider using Spring Boot. You will avoid issues with dependencies that do not work together.

Spring batch - FlatFileItemReader - Sequence and Mapping of csv elements

I have my POJO as this:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class FileInfo {
private String filepath;
private String ignorestr1;
private String firstname;
private String lastname;
private String employeeid;
private String applicantid;
private String createdate;
private String startretdate;
private String retlength;
private String emporapplicant;
}
And my ItemReader is like this:
#Bean
#StepScope
#Qualifier("FileInfoItemReader")
#DependsOn("partitioner")
public FlatFileItemReader<FileInfo> FileInfoItemReader(#Value("#{stepExecutionContext['fileName']}") String filename)
throws MalformedURLException {
return new FlatFileItemReaderBuilder<FileInfo>().name("FileInfoItemReader").delimited().delimiter("|")
.names(new String[] { "filepath", "ignorestr1", "firstname", "lastname", "employeeid", "applicantid", "createdate", "startretdate", "retlength", "emporapplicant" })
.fieldSetMapper(new BeanWrapperFieldSetMapper<FileInfo>() {
{
setTargetType(FileInfo.class);
}
}).resource(new UrlResource(filename)).build();
}
Update:
My complete BatchConfig:
#Configuration
#EnableBatchProcessing
public class BatchConfiguration {
private static final Logger log = LoggerFactory.getLogger(BatchConfiguration.class);
#Autowired
public JobBuilderFactory jobBuilderFactory;
#Autowired
public StepBuilderFactory stepBuilderFactory;
#Autowired
private FlatFileItemReader<FileInfo> FileInfoItemReader;
#Bean("partitioner")
#StepScope
public Partitioner partitioner() {
log.info("In Partitioner");
MultiResourcePartitioner partitioner = new MultiResourcePartitioner();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = null;
try {
resources = resolver.getResources("*.csv");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
partitioner.setResources(resources);
partitioner.partition(10);
return partitioner;
}
#Bean
public FileInfoItemProcessor processor() {
return new FileInfoItemProcessor();
}
#Bean
public FileInfoWriter<FileInfo> writer() {
return new FileInfoWriter<FileInfo>();
}
#Bean
public Job importUserJob(JobCompletionNotificationListener listener, Step step1) {
return jobBuilderFactory.get("importUserJob").incrementer(new RunIdIncrementer()).listener(listener)
.flow(masterStep()).end().build();
}
#Bean
public Step step1() {
return stepBuilderFactory.get("step1").<FileInfo, FileInfo>chunk(10).reader(FileInfoItemReader).processor(processor()).writer(writer())
.build();
}
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(25);
taskExecutor.setCorePoolSize(25);
taskExecutor.setQueueCapacity(25);
taskExecutor.afterPropertiesSet();
return taskExecutor;
}
#Bean
#Qualifier("masterStep")
public Step masterStep() {
return stepBuilderFactory.get("masterStep").partitioner("step1", partitioner()).step(step1())
.taskExecutor(taskExecutor()).build();
}
#Bean
#StepScope
#Qualifier("FileInfoItemReader")
#DependsOn("partitioner")
public FlatFileItemReader<FileInfo> FileInfoItemReader(#Value("#{stepExecutionContext['fileName']}") String filename)
throws MalformedURLException {
return new FlatFileItemReaderBuilder<FileInfo>().name("FileInfoItemReader").delimited().delimiter("|")
.names(new String[] { "I", "can", "put", "literally", "anything", "here", "and", "it", "works", "just_fine" })
.fieldSetMapper(new BeanWrapperFieldSetMapper<FileInfo>() {
{
setTargetType(FileInfo.class);
}
}).resource(new UrlResource(filename)).build();
}
}
Doubt/Question: My mapping follows the strict sequence in my FileInfo. If I switch the position of any of private String.... in my POJO, the csv's row elements' mappings are messed up. Is that the expected behavior? If not, then what I am missing here? Or what is the correct way to make it POJO sequence independent?
The BeanWrapperFieldSetMapper uses reflection to map fields, so their declaration order in your class should not matter.
The order of fields that you declare in the array parameter in .names() corresponds to the order of columns in the input file, not to the declaration order in the POJO.
EDIT: Add sample
persons.csv
1,foo
2,bar
SO69224405.java
import javax.sql.DataSource;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
#Configuration
#EnableBatchProcessing
public class SO69224405 {
#Bean
public FlatFileItemReader<Person> itemReader() {
return new FlatFileItemReaderBuilder<Person>()
.name("personItemReader")
.resource(new FileSystemResource("persons.csv"))
.delimited()
.names("id", "name") // with names("name", "id") the example fails
.targetType(Person.class)
.build();
}
#Bean
public ItemWriter<Person> itemWriter() {
return items -> items.forEach(System.out::println);
}
#Bean
public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
return jobs.get("job")
.start(steps.get("step")
.<Person, Person>chunk(5)
.reader(itemReader())
.writer(itemWriter())
.build())
.build();
}
public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(SO69224405.class);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job job = context.getBean(Job.class);
jobLauncher.run(job, new JobParameters());
}
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("/org/springframework/batch/core/schema-h2.sql")
.build();
}
public static class Person {
// the declaration order of fields should not matter
private String name;
private int id;
public Person() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Person{id=" + id + ", name='" + name + '\'' + '}';
}
}
}
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>
<groupId>com.example</groupId>
<artifactId>so69224405</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>so69224405</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

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.

Deprecated methods when using Feign with Oauth2 in SpringBoot 2.4.3

I am trying to use feign client with Oauth2 in my project based in Springboot 2.4.3. I implemented this example but the DefaultOAuth2ClientContext, OAuth2ProtectedResourceDetails and ClientCredentialsResourceDetails methods are deprecated.
pom.xml
<dependencies>
....
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
....
</dependencies>
application.yml
config:
serverUrl : exampleUrl
clientId: exampleClientId
clientSecret: exampleClientSecret
scopes: exampleScopes
feign:
post:
name: postService
url: https://localhost:8102/post
get:
name: getService
url: https://localhost:8102/get
FeignClient.java
#FeignClient(name = "example", configuration = OAuth2ClientConfig.class)
public interface FeignClient {
#PostMapping(value = "${feign.post.url}")
HttpEntity search(#PathVariable String id);
#GetMapping(value = "${feign.get.url}")
HttpEntity upload(#PathVariable String id);
}
OAuth2ClientProperties.java
#ConfigurationProperties(prefix = "config")
public class OAuth2ClientProperties {
private String serverUrl;
private String clientId;
private String clientSecret;
private List<String> scopes = new ArrayList<>();
public String getAccessTokenUri() {
return serverUrl;
}
public void setAccessTokenUri(String accessTokenUri) {
this.serverUrl = accessTokenUri;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientSecret() {
return clientSecret;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public List<String> getScopes() {
return scopes;
}
public void setScopes(List<String> scopes) {
this.scopes = scopes;
}
}
OAuth2ClientConfig.java
import feign.RequestInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.security.oauth2.client.feign.OAuth2FeignRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
#Configuration
#EnableConfigurationProperties(OAuth2ClientProperties.class)
public class OAuth2ClientConfig {
#Autowired
private OAuth2ClientProperties oAuth2ClientProperties;
#Bean
public RequestInterceptor requestInterceptor() {
return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(),
oAuth2ProtectedResourceDetails());
}
private OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setClientId(oAuth2ClientProperties.getClientId());
details.setClientSecret(oAuth2ClientProperties.getClientSecret());
details.setAccessTokenUri(oAuth2ClientProperties.getAccessTokenUri());
details.setScope(oAuth2ClientProperties.getScopes());
return details;
}
}
I have not yet tested whether it works or not, but what bothers me is having deprecated methods and I have not found a solution to replace them with equivalents taken into account by the new version.
Any ideas ?

Resources