Spring Reactive - problem with fromObject() method in BodyInserters class - java

I tried following the documentation tutorial, but I have some issues with depracated method. Specially in this line
.then(item-> ServerResponse.ok().contentType(APPLICATION_JSON).body(fromObject(item)))
The error is : Target type of a lambda conversion must be an interface.
Below is whole code. Thanks for your help
package com.learnreactivespring.learnreactivespring.handler;
import com.learnreactivespring.learnreactivespring.document.Item;
import com.learnreactivespring.learnreactivespring.repository.ItemReactiveRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
#Component
public class ItemsHandler {
#Autowired
ItemReactiveRepository itemReactiveRepository;
public Mono<ServerResponse> getAllItems(ServerRequest serverRequest) {
return ServerResponse.ok()
.contentType(APPLICATION_JSON)
.body(itemReactiveRepository.findAll(), Item.class);
}
public Mono<ServerResponse> getOneItem(ServerRequest request) {
String itemId = request.pathVariable("id");
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
Mono<Item> itemMono = this.itemReactiveRepository.findById(itemId);
return itemMono
.then(item -> ServerResponse.ok().contentType(APPLICATION_JSON).body(fromObject(item)))
.otherwiseIfEmpty(notFound);
}
}

Related

Why does #InjectMocks in Java Spring return null in my test case for a service?

I am trying to write test cases for a service I have implemented for a controller in Spring. I have the following Service and Test classes.
StudentCourseRequestService:
import java.util.List;
import org.springframework.stereotype.Service;
import model.CourseRequest;
import repository.ICourseRequestRepository;
import lombok.RequiredArgsConstructor;
#Service
#RequiredArgsConstructor
public class StudentCourseRequestService implements IStudentCourseRequestService {
private final ICourseRequestRepository courseRequestRepository;
#Override
public boolean requestCourse(CourseRequest courseRequest) {
return courseRequestRepository.saveRequest(courseRequest);
}
#Override
public List<CourseRequest> getAllCourseRequests() {
return courseRequestRepository.getAllCourseRequests();
}
#Override
public List<CourseRequest> getAllCourseRequestsOfStudent(Long studentId) {
return courseRequestRepository.getCourseRequestsByStudentId(studentId);
}
}
Test class:
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import model.CourseRequest;
import repository.ICourseRequestRepository;
public class StudentCourseRequestServiceTests {
#MockBean
private ICourseRequestRepository courseRequestRepository;
#InjectMocks
private StudentCourseRequestService studentCourseRequestService;
#Test
public void requestValidCourse() throws Exception {
final CourseRequest courseRequest = new CourseRequest(
"data0",
"data1",
"data2",
"data3",
"data4",
"data5"
);
Mockito.when(courseRequestRepository.saveRequest(courseRequest)).thenReturn(true);
boolean result = studentCourseRequestService.requestCourse(courseRequest);
assertThat(result).isTrue();
}
}
When I run the requestValidCourse() test case, I get the following error:
java.lang.NullPointerException: Cannot invoke "repository.ICourseRequestRepository.saveRequest(model.CourseRequest)" because "this.courseRequestRepository" is null
at service.StudentCourseRequestServiceTests.requestValidCourse(StudentCourseRequestServiceTests.java:29)
at java.base/java.util.ArrayList.forEach(Unknown Source)
at java.base/java.util.ArrayList.forEach(Unknown Source)
How can I resolve this issue?
As #Lesiak recommended, I added the #ExtendWith(MockitoExtension.class) annotation to the top of the class, and changed courseRequestRepository to have the #Mock annotation, instead of the previous #MockBean annotation. The issue got resolved after these changes.
Previously I had the configuration recommended by #Lesiak during my trials to resolve the issue, but I also had the annotation #ExtendWith(SpringExtension.class) at the top of the class. With this configuration, I got the same error. Removing the #ExtendWith(SpringExtension.class) annotation resolved the issue here.
New test class:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThat;
import model.CourseRequest;
import repository.ICourseRequestRepository;
#ExtendWith(MockitoExtension.class)
public class StudentCourseRequestServiceTests {
#Mock
private ICourseRequestRepository courseRequestRepository;
#InjectMocks
private StudentCourseRequestService studentCourseRequestService;
#Test
public void requestValidCourse() throws Exception {
final CourseRequest courseRequest = new CourseRequest(
"data0",
"data1",
"data2",
"data3",
"data4",
"data5"
);
Mockito.when(courseRequestRepository.saveRequest(courseRequest)).thenReturn(true);
boolean result = studentCourseRequestService.requestCourse(courseRequest);
assertThat(result).isTrue();
}
}

Springboot adding a "_class" field in POST Request

I am new to SpringBoot and I am trying to connect my SpringBoot App to MongoDB. The GET Request is working completely fine but the POST Request is adding a "_class" field in the data which I don't want. I did some searching and found that I have to add a #Configuration class to solve this issue but when I added the #Configuration class, I am getting the following error :
Field mongoDbFactory in com.example.demo.configuration.MongoConfig required a bean of type 'org.springframework.data.mongodb.MongoDbFactory' that could not be found.
My Confuguration class code is as follows :-
MongoConfig.java :-
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
#Configuration
public class MongoConfig {
#Autowired
private MongoDbFactory mongoDbFactory;
#Autowired
private MongoMappingContext mongoMappingContext;
#Bean
public MappingMongoConverter mappingMongoConverter() {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver,
mongoMappingContext);
converter.setTypeMapper(new DefaultMongoTypeMapper(null));
return converter;
}
}
Controller.java :-
import com.example.demo.model.Todo;
import com.example.demo.services.TodoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
public class Controller {
#Autowired
private TodoService todoService;
#GetMapping("/")
public List<Todo> getTodos() {
return todoService.getTodos();
}
#PostMapping("/")
public Todo addTodo(#RequestBody Todo todo) {
return todoService.addTodo(todo);
}
}
TodoService.java :-
import com.example.demo.model.Todo;
import java.util.List;
public interface TodoService {
public List<Todo> getTodos();
public Todo addTodo(Todo todo);
}
TodoServiceImplementation.java :-
import com.example.demo.model.Todo;
import com.example.demo.repository.TodoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class TodoServiceImplementation implements TodoService{
#Autowired
private TodoRepository todoRepository;
#Override
public List<Todo> getTodos() {
return todoRepository.findAll();
}
#Override
public Todo addTodo(Todo todo) {
return todoRepository.save(todo);
}
}
It is asking me to do the following action :-
Consider defining a bean of type 'org.springframework.data.mongodb.MongoDbFactory' in your configuration.

How to wire classes and objects in SpringBoot

Have SpringBoot Java app with different classes. I am not able to inject the dependencies and initialize/access the object of one class into another . Have seen the spring doc and used the annotations (#component,#Autowired etc. ), still there is an issue.
following are the classes.
Main Class ()
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
#SpringBootApplication
public class CostmanagementApplication {
public static void main(String[] args) {
SpringApplication.run(CostmanagementApplication.class, args);
}
}
Controller class
package com.test;
import javax.swing.text.rtf.RTFEditorKit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
#Component
#Controller
public class HighChartsController {
#Autowired
private RequestToken rt;
#GetMapping("/costdata")
public static String customerForm(Model model) {
//here not able to access the getToken() method
model.addAttribute("costdata", new CostDataModel());
return "costdata";
}
}
RequestToken Class
package com.test;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.stream.Collectors;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
#Component
public class RequestToken {
public String getToken() throws IOException, InterruptedException {
// TODO Auto-generated method stub
// code to get the token
return token;
}
}
now eventhough , I have all annotation in place , not getting why the getToken() method is not accessible in controller class using rt object. please suggest
Okay, let's go in order.
First of all, all the annotations #Service, #Controller and #Repository are specifications from #Component, so you don't need to specify #Component and #Controller in your HighChartsController.
Actually, if you check what the annotation #Controller definition is, you'll find this:
#Component
public #interface Controller {
...
}
Secondly, I don't really know what do you mean with that you aren't able to access the getToken() method, but as you wrote it seems you tried to access to that method as an static method.
You're injecting the object, so you use the methods of the objects like in plain Java: rt.getToken(). The only difference is that the RequestToken object will be already initialized at the moment you call it.
package com.test;
import javax.swing.text.rtf.RTFEditorKit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class HighChartsController {
#Autowired
private RequestToken rt;
#GetMapping("/costdata")
public static String customerForm(Model model) {
String token = rt.getToken();
...
model.addAttribute("costdata", new CostDataModel());
return "costdata";
}
}

Mapping two #GetMapping to same #RestController into Spring Boot

I need to mapping two GET methods look like:
GET /tickets - Retrieves a list of tickets
GET /tickets/12 - Retrieves a specific ticket
But when I mapped this, the Spring got confused!
When I hit http://localhost:8080/tickets in the Chrome, the result on server is:
DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "tickets"]
When I hit http://localhost:8080/tickets/12 in the Chrome, the result on server is:
QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
My Spring controller is:
package wendelsilverio.api.ticket;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
#RestController("tickets")
public class TicketController {
#Autowired
private TicketRepository repository;
#GetMapping
public List<TicketEntity> getTickets() {
return repository.findAll();
}
#GetMapping("/{id}")
public Optional<TicketEntity> getTicket(#PathVariable("id") Long id) {
return repository.findById(Long.valueOf(id));
}
}
My unit test is:
package wendelsilverio.api.ticket;
import static org.hamcrest.CoreMatchers.is;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Arrays;
import java.util.Optional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
#SpringBootTest
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
public class TicketControllerRestfulTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private TicketController mockTicketController;
#Test
public void getTickets() throws Exception {
given(mockTicketController.getTickets())
.willReturn(Arrays.asList(new TicketEntity(1L, "First ticket"), new TicketEntity(2L, "Second ticket")));
mockMvc.perform(get("tickets").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(jsonPath("$[0].content", is("First ticket")))
.andExpect(jsonPath("$[1].content", is("Second ticket")));
}
#Test
public void getTicket12() throws Exception {
Optional<TicketEntity> twelveTicket = Optional.of(new TicketEntity(12L, "Twelve ticket"));
given(mockTicketController.getTicket(12L)).willReturn(twelveTicket);
mockMvc.perform(get("tickets/12").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(jsonPath("$.id", is(12L))).andExpect(jsonPath("$.content", is("Twelve ticket")));
}
}
I'm using Java 11 and Spring Boot 2.1.6
Use
#RestController
#RequestMapping("/tickets")
...
#GetMapping
...
#GetMapping("{id}")
In your code
1) #RestController("tickets") means 'create bean named "tickets"'
2) second URL (#GetMapping("/{id}")) tells 'put ID at root' (http://localhost:8080/ID) - so controller cannot convert 'tickets' to long.

Spring Boot Inject CrudRepository into Service

I am having difficulty injecting a CrudRepository into a service annotated with the #Service annotation. I have two packages one "core" package containing #Service definitions and reusable controller definitions.
My main application in the x.y.application package is as follows:
package x.y.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan({ "x.y.application", "x.y.core" })
public class Application {
public static void main( String[] args ) {
SpringApplication.run( Application.class, args );
}
}
Then an example Controller.
package x.y.application.controller;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import x.y.application.model.User;
import x.y.core.controller.Controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.data.repository.CrudRepository;
#RestController
#RequestMapping("/test")
public class HelloController extends Controller<User> {
}
Then my re-usable controller class
package x.y.core.controller;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.HttpStatus;
import org.springframework.data.repository.CrudRepository;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.beans.factory.annotation.Autowired;
import x.y.core.service.Service;
public class Controller<T> {
#Inject
Service<T> service;
#RequestMapping(value = "/index.json", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public T create( #RequestBody T item ) throws Exception {
return service.create( item );
}
#RequestMapping(value = "/{id}.json", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public T update( #PathVariable Long id, #RequestBody T item ) throws Exception {
return service.update( item );
}
#RequestMapping(value = "/{id}.json", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public T read( #PathVariable Long id ) throws Exception {
return service.findOne( id );
}
#RequestMapping(value = "/index.json", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public List<T> readAll() throws Exception {
return service.findAll();
}
#RequestMapping(value = "/{id}.json", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(value = HttpStatus.NO_CONTENT)
public void delete( #PathVariable Long id ) throws Exception {
service.delete( id );
}
}
Then my service interface
package x.y.core.service;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.lang.reflect.*;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.HttpStatus;
import org.springframework.data.repository.CrudRepository;
import org.springframework.dao.DataIntegrityViolationException;
public interface Service<T> {
/**
* create.
* Creates a new entity in the database.
*/
public T create( T item );
/**
* update.
* Updates an existing entity in the database.
*/
public T update( T item );
public T findOne( Long id );
public List<T> findAll();
public void delete( Long id );
}
And finally the problematic implementation of service.
package x.y.core.service;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.lang.reflect.*;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.http.HttpStatus;
import org.springframework.data.repository.CrudRepository;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.beans.factory.annotation.Autowired;
#org.springframework.stereotype.Service
public class RepositoryService<T> implements Service<T> {
#Inject //Throws exception
CrudRepository<T,Long> repository;
/**
* create.
* Creates a new entity in the database.
*/
public T create( T item ) throws DataIntegrityViolationException {
/*try {
Field field = item.getClass().getDeclaredField( "id" );
field.setAccessible( true );
if( repository.exists( field.getLong( item ) ) ) {
throw new DataIntegrityViolationException( "Entity object already exists." );
}
} catch ( Exception exception ) {
throw new DataIntegrityViolationException( "Entity class does not contain Id attribute." );
}
return repository.save( item );*/ return item;
}
/**
* update.
* Updates an existing entity in the database.
*/
public T update( T item ) throws DataIntegrityViolationException {
/*try {
Field field = item.getClass().getDeclaredField( "id" );
field.setAccessible( true );
if( !repository.exists( field.getLong( item ) ) ) {
throw new DataIntegrityViolationException( "Entity object does not exists." );
}
} catch ( Exception exception ) {
throw new DataIntegrityViolationException( "Entity class does not contain Id attribute." );
}
return repository.save( item );*/ return item;
}
public T findOne( Long id ) {
/*if( !repository.exists( id ) ) {
throw new DataIntegrityViolationException( "Item with id does not exists." );
}
return repository.findOne( id );*/ return null;
}
public List<T> findAll() {
final List<T> resultList = new ArrayList<>();
/*/ final Iterator<T> all = repository.findAll().iterator();
while( all.hasNext() ) {
resultList.add( all.next() );
}*/
return resultList;
}
public void delete( Long id ) {
/*if( !repository.exists( id ) ) {
throw new DataIntegrityViolationException( "Item with id does not exists." );
}
repository.delete( id );*/
}
}
The exception
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.data.repository.CrudRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#javax.inject.Inject()}
It seems that any spring registered Component, Service, Resource cannot inject CrudRepository?
However if i do not annotate the CrudRepository in the x.y.application package it compiles and the CrudRepository is injected?
For dependency injection to work application context has to know a recipe for creating an instance of a specific class. Classes whose instance creation recipes are known are referred to as "beans". You can define beans either via XML configuration file (old school) or annotations (new school).
The error message you are receiving states that the application context does not have CrudRepository bean, i.e. it does not know how to create an instance of a class that implements this interface.
To create a bean definition in a new way you may annotate a class or a specific method which return instance of a specific class with #Bean or any other meta-annotation that includes it (#Service, #Controller, etc.).
If you intend to use Spring Data suite of projects to automate repository implementation generation, you need to annotate an interface which extends one of the core Spring Data interfaces (Repository, CrudRepository, PagingAndSortingRepository) with #Repository annotation like so
#Repository
public interface MyRepository extends CrudRepository<Entity, Long> {
}
This provides a bean definition for the application context and makes it aware that you want the repository implementation to be generated for you.
Then you can inject MyRepository to the service class.
The only doubt I have is about the generic type to be used in repository type definition. I would expect the repository implementation (the one you want to be generated for you) to be entity-specific rather than abstract.
This is probably because CrudRepository is not part of your package scan. And hence spring is unable to inject a proxy implementation for it.

Categories