Quarkus reactive : test Mutli<Response> - java

Introduction
I'm working on a project that uses Quarkus Reactive.
And currently I made a method that returns a Multi, but I can't test this one.
I would like to ensure that all responses in the Multi have a code of 2XX.
And check the content of each answer.
The codes:
package com.xx.xx.xx.rest;
import com.xx.xx.xx.domain.customerprofile.CustomerProfileService;
import com.xx.xx.xx.infrastructure.binding.CustomerProfileMapper;
import com.xx.xx.xx.rest.annotation.ValidationGroups;
import com.xx.xx.xx.rest.dto.customerprofile.CustomerProfileDTO;
import com.xx.xx.xx.rest.exception.ErrorCode;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import javax.inject.Inject;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.groups.ConvertGroup;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.util.Collection;
/**
* BulkImportResource is a rest resource class for bulk import of customer profiles.
*
* #version 1.0.0
*/
#Path("/bulkimport")
public class BulkImportResource {
private final CustomerProfileService customerProfileService;
/**
* Instantiates a new Customer profile resource.
*
* #param customerProfileService the customer profile service
*/
#Inject
public BulkImportResource(CustomerProfileService customerProfileService) {
this.customerProfileService = customerProfileService;
}
/**
* Bulk import of customer profiles.
*
* #param customerProfileDTOs the list of customer profiles to import
* #return the list of customer profiles
*/
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Multi<Response> postFormReturnAddedObject(#NotNull(message = ErrorCode.Constants.ERROR_EMPTY_BODY)
Collection<CustomerProfileDTO> customerProfileDTOs) {
return Multi.createFrom().items(customerProfileDTOs.parallelStream())
.onItem().transformToUniAndMerge(this::createCustomerProfileAndBuildResponse);
}
private Uni<Response> createCustomerProfileAndBuildResponse(
#Valid #ConvertGroup(to = ValidationGroups.Post.class) CustomerProfileDTO customerProfileDTO) {
return customerProfileService.createCustomerProfile(
CustomerProfileMapper.INSTANCE.dtoToDomain(customerProfileDTO))
.map(CustomerProfileMapper.INSTANCE::domainToDTO)
.map(dto -> Response
.created(UriBuilder.fromPath("customerProfiles/{id}")
.build(dto.getId()))
.entity(dto))
.map(Response.ResponseBuilder::build);
}
}
The test:
#QuarkusTest
#Tag("IntegrationTest")
#TestHTTPEndpoint(BulkImportResource.class)
#QuarkusTestResource(value = CouchbaseTestContainer.class)
public class BulkImportResourceIntegrationTest {
private static final String JSON_FILE_NAME = "/profiles.json";
private static File jsonFile;
private List<CustomerProfileDTO> customerProfileDTOList;
#BeforeAll
public static void setUpAll() {
jsonFile = new File(Objects.requireNonNull(MultipartBodyUnitTest.class.getResource(JSON_FILE_NAME)).getFile());
}
#BeforeEach
public void setUp() throws IOException {
var objectWriter = new ObjectMapper();
customerProfileDTOList = List.of(objectWriter.readValue(jsonFile, CustomerProfileDTO[].class));
}
#Test
void createCustomerProfile() throws IOException {
var responses = given()
.contentType(ContentType.JSON)
.body(customerProfileDTOList)
.when().post()
.then()
.extract().as(Response[].class);
}
}
This test doesn't work, the returned error :
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `javax.ws.rs.core.Response` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (String)"[{"status":201,"entity":{"id":"5","cas":1642517845455405056,"created":"2022-01-18T15:57:25Z","lastName":"DUPOND","firstName":"Marianne","gender":"FEMALE","title":"MRS","addresses":[{"street1":"rue du Testeur","zipCode":"34000","city":"MONTPELLIER","country":"France","typeKey":"PERSONAL"}],"phones":[{"number":"0610529856","typeKey":"PERSONAL"}],"emails":[{"email":"marianne.dupond#test.com","typeKey":"PERSONAL"}],"birthday":"1987-11-13","languages":["FR","EN"],"socialNetworks":[{"socialNetworkId":"[truncated 2143 chars]; line: 1, column: 2] (through reference chain: java.lang.Object[][0])
Outro
Someone have a solution for this problem ?

Related

Cucumber StepDef ResultActions NullPointerException

I am receiving the following NullPointerException when trying to execute a Cucumber StepDefs file that passes a JSON from a .feature file to a REST endpoint on localhost;
I have tried to instantiate the ResultActions in every other way, receiving the same error.
The Controller works linked to the test is fine, and is pointing to the correct REST endpoint.
The issue is with the result in personStepDefs
I don't think I'm missing a parameter for ResultActions result as I've built my RequestBuilder
java.lang.NullPointerException at com.///.v2.PersonStepDefs.i\_add\_a\_new\_Person\_using\_POST\_at\_with\_JSON([PersonStepDefs.java:49](https://PersonStepDefs.java:49)) at
✽.I add a new Person using POST at "[http://localhost:8080/services/person/add](http://localhost:8080/services/person/add)" with JSON:(file:///C:/path/to/src/test/resources/Person.feature:6)
PersonStepDefs.java
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#Transactional
/**
* Person Step Definition class to execute Scenario contained in Person.feature
* #author Lewis Jones
*
*/
public class PersonStepDefs {
#Autowired
private volatile WebApplicationContext wac;
#Autowired
private volatile MockMvc mockMvc;
private ResultActions result;
/**
* Runs the application server before every scenario.
*/
#BeforeClass
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#When("I add a new Person using POST at {string} with JSON:")
public void i_add_a_new_Person_using_POST_at_with_JSON(String request, String json) throws Exception {
result = mockMvc.perform(post(request).contentType(MediaType.APPLICATION_JSON)
.content(json.getBytes()));
}
#Then("the response code should be {int}")
public void the_response_code_should_be(Integer responseCode) throws Exception {
result.andExpect(status().is(responseCode));
}
}
RunMvcTest.java
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
#RunWith(Cucumber.class)
#CucumberOptions(
plugin = {"pretty","html:build/cucumber-html-report"},
features = "src/test/resources", strict = true)
#WebAppConfiguration
#ContextConfiguration(classes = V2Application.class)
/**
* A class to run the Cucumber .feature files located in 'features'
* #author Lewis Jones
*
*/
public class RunMvcTest {
}
Person.feature
Feature: Person CRUD
As a User, I want to add a Person
#repo
Scenario: Person.Repo.Add
When I add a new Person using POST at "http://localhost:8080/services/person/add" with JSON:
"""
{"firstName":"Lewis","lastName":"Jones","addressId":"1", "dob":"1999-07-11"}
"""
Then the response code should be 200
A null pointer exception happens when you try to de-reference a variable or field that is null. So if you try to call perform on mockMvc while mockMvc is null you will get a null pointer exception.
If you read the stack trace carefully you can see that this it what it tries to tell you.
result = mockMvc.perform(....);
So how can mockMcv be null? You initialize it in setup method right? That means setup isn't called. You can confirm this by putting a break point in the method and debugging your test.
import org.junit.BeforeClass;
....
/**
* Runs the application server before every scenario.
*/
#BeforeClass
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
And setup doesn't get called because BeforeClass is a JUnit annotation. Cucumber uses io.cucumber.java.Before.

Spring Boot 2 and method parameter validation

I created the following service interface:
import javax.validation.constraints.NotBlank;
import org.springframework.lang.NonNull;
import org.springframework.validation.annotation.Validated;
#Validated
public interface UserService {
User create(#NonNull Long telegramId, #NotBlank String name, #NonNull Boolean isBot);
}
but the following invocation:
userService.create(telegramId, "Mike", null);
passes the #NotNull validation for isBot parameter. How to correctly configure Spring Boot and my service in order to take into account #NonNull annotation and prevent method execution in case of null parameter?
I played around with this problem for a bit.
Your code looks fine to me: Make sure that the implementation of UserService also has the validation annotations present.
Ensure that you allow Spring to create the Bean; it should work as you expect.
Example
Service Definition
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
#Validated
public interface GreetingService {
String greet(#NotNull #NotBlank String greeting);
}
Service Implementation
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
#Service
public class HelloGreetingService implements GreetingService {
public String greet(#NotNull #NotBlank String greeting) {
return "hello " + greeting;
}
}
Testcase
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import javax.validation.ConstraintViolationException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
#SpringBootTest
class HelloGreetingServiceTest {
#Autowired
private GreetingService helloGreetingService;
#Test
void whenGreetWithStringInput_shouldDisplayGreeting() {
String input = "john doe";
assertEquals("hello john doe", helloGreetingService.greet(input));
}
#Test
void whenGreetWithNullInput_shouldThrowException() {
assertThrows(ConstraintViolationException.class, () -> helloGreetingService.greet(null));
}
#Test
void whenGreetWithBlankInput_shouldThrowException() {
assertThrows(ConstraintViolationException.class, () -> helloGreetingService.greet(""));
}
}
Testcases are green for me.
Github: https://github.com/almac777/spring-validation-playground
Source: https://www.baeldung.com/javax-validation-method-constraints
HTH!
Use the same thing in Implementation class instead interface.
Also can write one global exception like:
#Order(Ordered.HIGHEST_PRECEDENCE)
#RestControllerAdvice
public class GlobalRestException extends ResponseEntityExceptionHandler {
...
...
/**
* Handle MethodArgumentNotValidException. Triggered when an object fails #Valid
* validation.
*
* #param ex the MethodArgumentNotValidException that is thrown when #Valid
* validation fails
* #param headers HttpHeaders
* #param status HttpStatus
* #param request WebRequest
* #return the ApiException object
*/
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
Error apiError = new Error(BAD_REQUEST);
apiError.setMessage("Validation error");
apiError.addValidationErrors(ex.getBindingResult().getFieldErrors());
apiError.addValidationError(ex.getBindingResult().getGlobalErrors());
return buildResponseEntity(apiError);
}
}
There are more method that can be override to handle different kind of exception like :
/**
* Handles javax.validation.ConstraintViolationException. Thrown when #Validated
* fails.
*
* #param ex the ConstraintViolationException
* #return the ApiException object
*/
#ExceptionHandler(javax.validation.ConstraintViolationException.class)
protected ResponseEntity<Object> handleConstraintViolation(javax.validation.ConstraintViolationException ex) {
Error apiError = new Error(BAD_REQUEST);
apiError.setMessage("Validation error");
apiError.addValidationErrors(ex.getConstraintViolations());
return buildResponseEntity(apiError);
}
You need to make sure that #Validated annotation is used on 'class' which method arguments will need to be validated and Spring configuration need to be added
#Configuration
public class MethodValidationConfig {
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}

Test the Spring Boot Rest API Post Method

I completely new to Junit and I have to write Junit Test case for my Rest Controller but I am not getting from where should I start. Any help would be really appreciated.
This is My Rest Controller class.
#RestController
public class RecognitionController {
private FrameDecoder frameDecoder;
private TagEncoder tagEncoder;
private RecognitionService recognitionService;
#Autowired
public RecognitionController(FrameDecoder frameDecoder, TagEncoder tagEncoder,
RecognitionService recognitionService) {
this.frameDecoder = frameDecoder;
this.tagEncoder = tagEncoder;
this.recognitionService = recognitionService;
}
/**
*
* #param take the input as Json Frame and map the output at api/detection Url.
* #return List of Json tag in the Http response.
*/
#RequestMapping(value = "/api/detection", method = RequestMethod.POST)
public List<JsonTag> analyseframe(#RequestBody JsonFrame frame) {
SimpleFrame simpleFrame = frameDecoder.decodeFrame(frame);
List<OrientedTag> orientedTags = recognitionService.analyseFrame(simpleFrame);
return tagEncoder.encodeTag(orientedTags);
}
}
For testing Rest Controller you need:
JUnit
Mockito
Spring Test
JsonPath
Controller:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
#RestController
#RequestMapping(value = "/Entity")
public class EntityRestController {
private EntityService service;
#RequestMapping(value = "/entity/all", method = RequestMethod.GET)
public List<Entity> findAll() {
List<Entity> models = service.findAll();
return createEntities(models);
}
private List<EntityDTO> createDTOs(List<Entity> models) {
List<EntityDTO> dtos = new ArrayList<>();
for (Entitymodel: models) {
dtos.add(createDTO(model));
}
return dtos;
}
private EntityDTO createDTO(Entity model) {
EntityDTO dto = new EntityDTO();
dto.setId(model.getId());
dto.setDescription(model.getDescription());
dto.setTitle(model.getTitle());
return dto;
}
}
Test example:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestContext.class, WebAppContext.class})
#WebAppConfiguration
public class EntityRestControllerTest {
private MockMvc mockMvc;
#Autowired
private EntityService entityServiceMock;
//Add WebApplicationContext field here.
//The setUp() method is omitted.
#Test
public void findAllEntitiesTest() throws Exception {
Entity first = new Entity();
first.setId(1L);
first.setDescription("Lorem ipsum")
first.setTitle("Foo");
Entity second = new Entity();
second.setId(2L);
second.setDescription("Lorem ipsum")
second.setTitle("Bar");
when(entityServiceMock.findAll()).thenReturn(Arrays.asList(first, second));
mockMvc.perform(get("/entity/all"))
.andExpect(status().isOk())
.andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].id", is(1)))
.andExpect(jsonPath("$[0].description", is("Lorem ipsum")))
.andExpect(jsonPath("$[0].title", is("Foo")))
.andExpect(jsonPath("$[1].id", is(2)))
.andExpect(jsonPath("$[1].description", is("Lorem ipsum")))
.andExpect(jsonPath("$[1].title", is("Bar")));
verify(entityServiceMock, times(1)).findAll();
verifyNoMoreInteractions(entityServiceMock);
}
}
Please follow the full tutorial for more details.
___EDIT_1___
I didn't understand from where "thenReturn" Method came
static method Mockito.when() has the following signature:
public static <T> OngoingStubbing<T> when(T methodCall)
When you mocking some service and putting it inside when as parameter - it returns object which IS OngoingStubbing<T>. All classes which implement OngoingStubbing<T> have thenReturn(T value) method and it's called.

How to test RESTful application?

I have an application example with a service:
RestApp.java
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/webapi")
public class RestApp extends Application {
#Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(MessageService.class);
return classes;
}
}
MessageService.java
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.util.List;
#Stateless
#Path("/messages")
public class MessageService {
#Inject
private MessagesManager messagesManager;
#GET
#Path("all")
#Produces({MediaType.APPLICATION_JSON})
public List<Message> getMessages() {
return messagesManager.getMessages();
}
}
and the service depends on the singleton MessagesManager.java:
import javax.ejb.*;
import javax.inject.Singleton;
#Singleton
#Startup
#ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class MessagesManager implements Serializable {
private List<Message> messages = new ArrayList<>();
#Lock(LockType.READ)
public List<Message> getMessages() {
messages.add(new Message(1, "message text"));
return messages;
}
}
and this app works fine. But during the test occurs error of injection:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=MessagesManager,parent=MessageService,qualifiers={},position=-1,optional=false,self=false,unqualified=null,1232089028)
Test code is:
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import static org.junit.Assert.assertEquals;
public class RestAppTest extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(MessageService.class);
}
#Test
public void testGet() {
final Response response = target("messages/all").request().get();
assertEquals(200, response.getStatus());
}
}
Why it happens and how to fix it?
The class MessagesManager is missing in an application context. Add the class to configure method like this:
return new ResourceConfig(MessageService.class, MessagesManager.class);
You need couple of things
1> Well formed JSON structure for your REST API
2> Some kind of REST client such as advanced REST client for chrome, Mozilla etc which can be used as a plugin. POSTMAN is also a useful tool

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