I created a custom exception hanle in a separate module and wanted to use the same GlobalExceptionHandle class in another module. Although an exception appears on the console, but no custom exception appears on the postman. What could be the reason for this? pls help me
GlobalExceptionHandle.java
#RestControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandle extends ResponseEntityExceptionHandler {
#ExceptionHandler(NotFoundException.class)
public ResponseEntity<CustomExceptionResponseDto> handle(NotFoundException exception) {
return new ResponseEntity<CustomExceptionResponseDto>(
CustomExceptionResponseDto.builder()
.errorCode(HttpStatus.NOT_FOUND)
.message(exception.getMessage())
.status(HttpStatus.NOT_FOUND.value())
.timestamp(LocalDateTime.now())
.build(), HttpStatus.NOT_FOUND
);
}
#ExceptionHandler(InvalidStateException.class)
public ResponseEntity<CustomExceptionResponseDto> handle(InvalidStateException exception) {
return new ResponseEntity<CustomExceptionResponseDto>(
CustomExceptionResponseDto.builder()
.errorCode(HttpStatus.BAD_REQUEST)
.message(exception.getMessage())
.status(HttpStatus.BAD_REQUEST.value())
.timestamp(LocalDateTime.now())
.build(), HttpStatus.BAD_REQUEST
);
}
}
UserController.java
#RestController
#RequestMapping("/user")
#RequiredArgsConstructor
public class UserController {
private final UserService userService;
#PostMapping
public ResponseEntity<UserResponseDto> save(#RequestBody UserRequestDto userRequestDto) {
return ResponseEntity.ok(userService.save(userRequestDto));
}
}
UserServiceImpl.java
#Service
#RequiredArgsConstructor
#Slf4j
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final UserMapper userMapper;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
public UserResponseDto save(UserRequestDto userRequestDto) {
log.info("User request dto: " + userRequestDto);
userRepository.findUserByEmail(userRequestDto.getEmail()).ifPresent((user) -> {
throw new EmailAlreadyUseException(user.getEmail());
});
var user = userMapper.userRequestDtoToUser(userRequestDto);
user.setPassword(bCryptPasswordEncoder.encode(userRequestDto.getPassword()));
return userMapper.userToUserResponseDto(userRepository.save(user));
}
}
InvalidStateException.java
public class InvalidStateException extends RuntimeException {
private static final long serialVersionUID = 1L;
public InvalidStateException(String message) {
super(message);
}
}
EmailAlreadyUseException.java
public class EmailAlreadyUseException extends InvalidStateException {
public EmailAlreadyUseException(String email) {
super(email+" already registered,try different email");
}
}
I see you're throwing throw new EmailAlreadyUseException(user.getEmail()); but you handle #ExceptionHandler(InvalidStateException.class) try to change into#ExceptionHandler(EmailAlreadyUseException.class) and check it out.
Related
#Component
public abstract class CommandBase {
#Autowired
WebServiceProxy nbiService;
#Autowired
OperationCacheRepository cacheRepository;
public CommandBase(
WebServiceProxy nbiService,
OperationCacheRepository cacheRepository) {
this.nbiService = nbiService;
this.cacheRepository = cacheRepository;
}
public abstract void executeSPV(SpeedTestDTO stDTO) throws NBIException;
public abstract long executeGPV(long guid, OperationCache operationCache) throws NBIException;
#Slf4j
public class DownloadDiagnosticsCommand extends CommandBase {
public DownloadDiagnosticsCommand(WebServiceProxy nbiService, OperationCacheRepository cacheRepository) {
super(nbiService, cacheRepository);
}
#Override
public void executeSPV(SpeedTestDTO stDTO) throws NBIException {
// some executable code
}
#Override
public long executeGPV(long guid, OperationCache operationCache) throws NBIException {
// some executable code
}
}
#Slf4j
public class UploadDiagnosticsCommand extends CommandBase {
public UploadDiagnosticsCommand(WebServiceProxy nbiService, OperationCacheRepository cacheRepository) {
super(nbiService, cacheRepository);
}
#Override
public void executeSPV(SpeedTestDTO stDTO) throws NBIException {
// some executable code
}
#Override
public long executeGPV(long guid, OperationCache operationCache) throws NBIException {
//some executable code
}
}
#Component
public class RFACommandFactory {
#Autowired
WebServiceProxy nbiServiceProxy;
#Autowired
OperationCacheRepository cacheRepository;
public final CommandBase createCommand(final String measureType) {
if ("download".equalsIgnoreCase(measureType)) {
return new DownloadDiagnosticsCommand(nbiServiceProxy, cacheRepository);
} else if ("upload".equalsIgnoreCase(measureType)) {
return new UploadDiagnosticsCommand(nbiServiceProxy, cacheRepository);
}
return null;
}
}
Calling method executeSPV from abstract class
#RestController
#RequestMapping("/rfa/speedtest/v1")
#Slf4j
public class Controller {
#Autowired
CommandBase command;
#Autowired
RFACommandFactory rfaCommandFactory;
#PostMapping(value = "{id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
private ResponseEntity<String> post(
#PathVariable String assetId,
#RequestBody Payload payload) {
log.info("Received new payload:{}", payload);
command = rfaCommandFactory.createCommand(speedTestDTO.getType());
try {
command.executeSPV(speedTestDTO);
} catch (NBIException e) {
log.info("NBIException", e);
return new ResponseEntity(payload, HttpStatus.BAD_REQUEST);
}
return new ResponseEntity(payload, HttpStatus.CREATED);
}
}
If I remove #Componet from Upload and Download classes I receive error I need to add Bean for abstrcat class CommndBase
If I use #Compoment on Upload and Download classes I receive dual Bean is useed...
Field command in .Controller required a single bean, but 2 were found:
You should not use #Component for abstract class, because Spring context will not be able to initialize that bean. You should remove it then.
Another thing is the way you want to implement a factory pattern here - I recommend you the way described here: https://stackoverflow.com/a/39361500/14056755, refactored version https://stackoverflow.com/a/55060326/14056755.
I'm testing my RestController with mockMvc. I have a global RestExceptionHandler to resolve all exceptions. In my RestController I throw custom Exception RequestValidationException like this:
#ApiOperation("Search something")
#RequestMapping(path = "/search", method = RequestMethod.POST)
public CompletableFuture<SomeResponse> search(
#RequestBody #Validated SearchRequest request, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
throw new RequestValidationException(bindingResult);
}
return searchService.search(request);
}
And when i pass empty request it must throw RequestValidationException(bindingResult)
but when i start tests they fall in that place where i throw Exception instead to resolve it.
i try to configure my mockMvc like this:
#RunWith(SpringRunner.class)
public class SearchControllerTest {
private MockMvc mockMvc;
#InjectMocks
protected SearchController searchController;
#MockBean
private SearchService searchService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(searchController)
.setHandlerExceptionResolvers(getHandlerExceptionResolver())
.build();
}
private HandlerExceptionResolver getHandlerExceptionResolver() {
final StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("exceptionHandler", RestExceptionHandler.class);
final WebMvcConfigurationSupport webMvcConfigurationSupport = new WebMvcConfigurationSupport();
webMvcConfigurationSupport.setApplicationContext(applicationContext);
return webMvcConfigurationSupport.handlerExceptionResolver();
}
but it doesnt help. i'm getting an Exception insted json with message.
My RequestValidationExceptionHandler:
#Component
public class RequestValidationExceptionHandler implements ApiExceptionHandler {
#Override
public ResponseEntity<ApiResponse> process(Throwable throwable) {
RequestValidationException e = (RequestValidationException) throwable;
if (e.getBindingResult() != null) {
return new ResponseEntity<>(ApiResponse.badRequest(e.getBindingResult()), HttpStatus.OK);
}
return new ResponseEntity<>(ApiResponse.badRequest(throwable, ApiResponseCode.BAD_REQUEST), HttpStatus.OK);
}
#Override
public Class<? extends Throwable> getSupportedException() {
return RequestValidationException.class;
}
}
2) My #ControllerAdvice:
#Slf4j
#ControllerAdvice
#SuppressWarnings({"checkstyle:JavadocMethod", "checkstyle:MultipleStringLiterals"})
public class RestExceptionHandler {
#Autowired
private ExceptionHandlerRegistry handlerRegistry;
#ExceptionHandler
public ResponseEntity handleThrowable(Throwable throwable, WebRequest request) {
request.setAttribute(Constants.ERROR_ATTRIBUTE_NAME, throwable, RequestAttributes.SCOPE_REQUEST);
Throwable ex = throwable instanceof CompletionException ?
ObjectUtils.defaultIfNull(throwable.getCause(), throwable) : throwable;
for (ApiExceptionHandler handler : handlerRegistry.getHandlers()) {
if (handler.isSupported(ex)) {
return handler.process(ex);
}
}
return new ResponseEntity<>(ApiResponse.badRequest(throwable, ApiResponseCode.SERVER_ERROR), HttpStatus.OK);
}
}
3) And ExceptionHandlerRegistry :
#Component
public class ExceptionHandlerRegistry {
#Getter
private final List<ApiExceptionHandler> handlers;
#Autowired
public ExceptionHandlerRegistry(List<ApiExceptionHandler> handlers) {
this.handlers = ObjectUtils.defaultIfNull(handlers, Collections.emptyList());
}
}
The Error message:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is ru.filit.mvideo.mb2c.api.exceptions.RequestValidationException
UPDATE
So after some discussion with #MichaelMichailidis, i try to do this, i just add an inner #Configuration class with needed beans:
#TestConfiguration
static class SearchControllerTestConfiguration {
#Bean
public RequestValidationExceptionHandler requestValidationExceptionHandler(){
return new RequestValidationExceptionHandler();
}
#Bean
public ExceptionHandlerRegistry getExceptionHandlerRegistry(final RequestValidationExceptionHandler requestValidationExceptionHandler){
return new ExceptionHandlerRegistry(Collections.singletonList(requestValidationExceptionHandler));
}
#Bean
public RestExceptionHandler getRestExceptionHandler(){
return new RestExceptionHandler();
}
}
and my test pass. But i can't understand why test were working without configuration before i add #ControllerAdvice?
You can try importing your exception handler in your test class:
#RunWith(SpringRunner.class)
#Import(RestExceptionHandler.class) // EXCEPTION HANDLER CLASS
public class SearchControllerTest {
I'm trying to write a really basic application using Spring-Boot. The only thing I'm currently trying is to get some information out of a SQL Server database.
Application.java
#SpringBootApplication(scanBasePackageClasses = { MainView.class, Application.class })
#EnableJpaRepositories(basePackageClasses = CustomerRepository.class)
#EntityScan(basePackageClasses = Customer.class)
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
Customerrepository.java
#Repository
public interface CustomerRepository extends JpaRepository<Customer, Integer>
{
}
CustomerController.java
#Controller
#RequestMapping(path = "/customer")
public class CustomerController {
#Autowired
private CustomerRepository customerRepository;
#GetMapping(path = "/all")
public #ResponseBody Iterable<Customer> getAllCustomers() {
return customerRepository.findAll();
}
}
CustomersView.java
#Tag("customers-view")
#HtmlImport("src/views/customers/customers-view.html")
#Route(value = ApplicationConst.PAGE_CUSTOMERS, layout = MainView.class)
#PageTitle(ApplicationConst.TITLE_CUSTOMERS)
public class CustomersView extends PolymerTemplate<TemplateModel> {
#Autowired
CustomerRepository customerRepository;
public CustomersView() {
customerRepository.findAll();
}
}
Going to http://localhost:8080/customer returns every customer in my database just fine.
But when I try to find all the customers in my CustomersView.java, the autowired CustomerRepository returns a nullpointerexception.
Is somebody able to point me in the right direction?
Try to #Autowire the Repository in the constructor like this:
#Tag("customers-view")
#HtmlImport("src/views/customers/customers-view.html")
#Route(value = ApplicationConst.PAGE_CUSTOMERS, layout = MainView.class)
#PageTitle(ApplicationConst.TITLE_CUSTOMERS)
public class CustomersView extends PolymerTemplate<TemplateModel> {
CustomerRepository customerRepository;
#Autowired
public CustomersView(CustomerRepository customerRepository) {
this.costumerRepository = customerRepository;
this.customerRepository.findAll();
}
}
This happens because all #autowired-attributes are inserted after the constructor gets completed. If you want to inject the #autowired-attributes at constructor-time, you have to use the method above.
I want to call all the request mapping method(which has #Resource injection) before the server starts. How I can do this?
#Controller
public class ServiceController {
#Resource(name="userService")
private IUserService userService;
#RequestMapping("/getAllCountry")
public String getAllCountry() {
return userService.getAllCountry();
}
#RequestMapping("/getAllStates")
public String getAllStates() {
return userService.getStates();
}
#PostConstruct
public void cacheData(){
cache.put("ALL_COUNTRY_DATA", getAllCountry());
cache.put("ALL_STATE_DATA", getAllStates());
}
}
The above code fails and give me IllegalStateException. What is the best way to call the request mapping methods before the server is up and populate the cache.
Try using ApplicationListener in conjunction with ContextRefreshedEvent:
#Controller
public class ServiceController implements ApplicationListener<ContextRefreshedEvent> {
private static final Map<String, String> cache = new HashMap<>();
#Resource(name = "userService")
private IUserService userService;
#RequestMapping("/getAllCountry")
public String getAllCountry() {
return userService.getAllCountry();
}
#RequestMapping("/getAllStates")
public String getAllStates() {
return userService.getStates();
}
public void cacheData() {
cache.put("ALL_COUNTRY_DATA", getAllCountry());
cache.put("ALL_STATE_DATA", getAllStates());
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
cacheData();
}
}
I'm trying to create a simple web-page, which is displaying the data, received from web-service
#Service
#Transactional
public class TopicService {
#Autowired
private TopicRepository topicRepository;
public int saveTopic(Topic topic){
return topicRepository.save(topic).getId();
}
public Iterable<Topic> findAllTopics(){
return topicRepository.findAll();
}
public Topic findTopicByID(Long id){
return topicRepository.findOne(id);
}
public List<Topic> findTopicsByTag(Tag tag){
return topicRepository.findAllByTopicTag(tag);
}
}
topic repository extends CRUD-repository
#Repository
public interface TopicRepository extends CrudRepository<Topic, Long>
{
List<Topic> findAllByTopicTag(Tag currentTag);
}
the controller invokes a service in the following way
#Controller
public class HomeController {
private TopicService service;
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model)
{
Iterable<Topic> listTopic = service.findAllTopics();
return new ModelAndView("home", "model", listTopic);
}
}
this string
Iterable listTopic = service.findAllTopics();
throws null-pointer exception. I guess, because the service isn't initialized. How could I perform correct initialization of the service?
You need to autowire the TopicService in HomeController:
#Controller
public class HomeController {
#Aurowired
private TopicService service;