Inversion of control based on properties file - spring

Below is my interface :-
interface Student {
findById();
}
Below are my 2 implementations of interface :-
class Matriculation implements Student {
findById(){
//implementation
}
}
class Graduation implements Student {
findById(){
//implementation
}
}
application.properties
graduation=true
Calling class :-
class Test {
#Autowired
Student student ;
method(){
student.findById();
}
}
If graduation is true i want to call Graduation class findById else i want to call Matriculation findById method .How can i achieve this in SpringBoot?

you can use the #ConditionalOnProperty annotation. Like this:
#ConditionalOnProperty(name = "graduation", havingValue = "true")
#Service
public class Graduation implements Student
{
#Override
public String findById()
{
return "Graduation";
}
}

correctly indicated in comments, here is a possible implementation.
class Test {
#Autowired
Matriculation matriculationStudent ;
#Autowired
Graduation graduationStudent;
#Value("${graduation}")
private boolean graduation;
void method(){
if (graduation) {
graduationStudent.findById();
} else {
matriculationStudent.findById();
}
}
}

You're gonna create a Spring Component like this:
#Component
public class StudentFactory {
#Autowired
private Matriculation matriculation;
#Autowired
private Graduation graduation;
#Value("${graduation}")
private boolean graduationValue;
public Student getImplementation() {
if (graduationValue) {
return graduation;
} else {
return matriculation;
}
}
}
and then you just inject the factory in the Test class and call getImplementation method

Related

Is it possible to invoke mocked object's method in constructor?

I have, for example, these classes with Spring Boot. I try to do a REST API without a database and wieh files as data. The data files are like this:
{
"persons": [
{ "firstName":"John", "lastName":"Boyd", "address":"1509 Culver St", "city":"Culver", "zip":"97451", "phone":"841-874-6512", "email":"jaboyd#email.com" },
{ "firstName":"Jacob", "lastName":"Boyd", "address":"1509 Culver St", "city":"Culver", "zip":"97451", "phone":"841-874-6513", "email":"drk#email.com" }
] }
#Repository
public class PersonRepository {
private List<Person> persons;
private DataLoaderService loaderService;
#Autowired
public PersonRepository(DataLoaderService loaderService){
persons= loaderService.convertJsonToPojo("Persons",Person.class);
}
public List<Person> getAll(){
return persons;
}
}
#Service
public class DataLoaderService{
private JsonFileService jsonFileService;
private ObjectMapper mapper
#Autowired
public DataLoaderService(JsonFileService jsonFileService,ObjectMapper mapper){
this.JsonFileService =jsonFileService;
this.mapper=mapper;
}
public <T> List<T> convertJsonToPojo (String nodeName,Class <T>
classOfT){
}
}
So, I have a file. How can I read to transform to a list of Pojo?
When I want to mock the test method getAll(), my list size is 0. The mock doesn't give me values because I think the problem is that I initialized the value in the constructor. Here is my test:
#ExtendWith(MockitoExtension.class)
public class PersonRepositoryTest {
PersonRepository repository;
#Mock
private DataLoaderService loaderService;
#BeforeEach
public void setUp() {
repository = new PersonRepository(loaderService);
}
#Test
public void getAllPersonnesInConstructor() {
List<Person> mockedList = Arrays.asList(
new Person("Paul","Moes","1", "7777", "adresse tour", "Chicago", "pauln#gmail.com"),
new Person("Eleson","Moc","2", "77777", "ddkdkd", "New York", "eleson#gmail.com")
);
doReturn(mockedList).when(loaderService).convertJsonToPojo("persons",Person.class);
List<Person> persons = repository.getAll();
assertEquals(2,persons.size(),"Expected list size is 2");
assertEquals(persons,mockedList);
}
If i use #Spy, I have an error.
When I use method getAll() without initializing the variable persons in the constructor but in the method getAll, it is OK, like this:
public List<Person> getAll(){
this.persons=this.dataLoaderService.convertJsonToPojo("persons", Person.class);
log.debug("persons getALL repository" + persons);
return this.persons;
}
What can I do to test it?
Test a method which initializes a value in the constructor.

MyHow to inject bean using constructor and call method in springboot

#Service
abstract class A {
protected MyObj obj;
protected A(MyObj obj) {
this.obj = obj;
}
public abstract XYZ getTrade();
}
#Service
public class B extends A {
B(MyObj obj) {
super(obj);
}
public XYZ getTrade() {}
}
#Service
public class C extends A {
C(MyObj obj) {
super(obj);
}
public XYZ getTrade() {}
}
#Controller
public class MyController {
#GemMapping("/test")
public void test() {
MyObj obj = new MyObj();
if(condition 1) {
//call getTrade() method of class B
}
if(condition 2) {
//call getTrade() method of class C
}
}
}
MyObj is a user-defined POJO that is not managed bean.
I have the above classes. now I have to call getTrade() method based on some condition in Controller. Could you please help/advise?
this answer is based on the information provided by the question.
to inject beans you could do:
#Controller
public class MyController {
private final B b;
private final C c;
public MyController (B b, C c) {
this.b = b;
this.c = c;
}
.........
}
in this case, beans must have to have one no-args constructor. so, In your case what you can do is have a no-args constructor in each service. then set your myObj by setter. that will do the trick.
If you want to achieve polyformism on your sevices you need to add a Qualifier in your classes B and C. So the question now became how to inject a service dynamically on runtime, and this is answered here:
How to dynamically inject a service using a runtime "qualifier" variable?

How to use a property in spring data #Query

I can't manage to inject a property from application.yml to a spring data #Query.
The following results in an EL1008E error:
public interface MyRepository extends JpaRepository<MyEntity, Long> {
#Query("select e from MyEntity e where e.foo = :foo and e.env= ?#{env}")
MyEntity findByFoo(#Param("foo") String foo);
}
According to this blog it is possible to inject a property of spring's principal, which is not very different from what I would like to do.
Any hints on this?
I should really stop asking questions and answer them by myself shortly after ... That is not on purpose.
The mentioned blog has the solution included. Add this:
public class PropertyEvaluationContextExtension extends EvaluationContextExtensionSupport {
private final MyProps p;
public PropertyEvaluationContextExtension(final MyProps p) {
this.p= p;
}
#Override
public String getExtensionId() {
return "foo";
}
#Override
public MyProps getRootObject() {
return this.p;
}
}
#Configuration
public class PropertyConfig {
private final MyProps p;
public PropertyConfig(final MyProps p) {
this.p= p;
}
#Bean
EvaluationContextExtensionSupport propertyExtension() {
return new PropertyEvaluationContextExtension(p);
}
}
Now every property of MyProps is accessible via SpEL.

Customize update entity on Spring Data repository

I have XRepository interface (extends JpaRepository). On create or update X entity i need to call method of another repository (YRepository) in transaction (exactly: update some field and use new value in created/updated entity X).
To do that i created a service class with #Transactional methods and custom REST Controller. POST mapping on controller works OK and is acceptable for me, but have problem how to implement in more elegant way update (PUT/PATCH) existing entity in my service layer. It works too, but had to use BeanUtils.copyProperties(). Is a better, more conventional way to do that ?
public interface XRepository extends JpaRepository<X, Long> {
}
public interface YRepository extends JpaRepository<Y, Long> {
}
#BasePathAwareController
public class XRestControllerCustom {
#Autowired
private MyService myService;
#PostMapping("/x")
public #ResponseBody ResponseEntity<X> create(#RequestBody Resource<X> x) {
return new ResponseEntity<X>(myService.save(x.getContent()), HttpStatus.CREATED);
}
#PatchMapping("/x/{id}")
public #ResponseBody ResponseEntity<X> update(#PathVariable Long id, #RequestBody Resource<X> x) {
myService.update(id, x.getContent());
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
#Component
public class MyService {
#Autowired
private XRepository xRepository;
#Autowired
private YRepository yRepository;
#Transactional
public X save(X x) {
yRepository.update();
x.setField(yRepository.get());
return xRepository.save(x);
}
#Transactional
public X update(Long id, X partial) {
X x = xRepository.getOne(id);
BeanUtils.copyProperties(x, partial);
x.setId(id); // because copyProperties makes id null
yRepository.update();
x.setField(yRepository.get());
return xRepository.save(x);
}
}

Spring Rest Url ID Validation in DB - Eg: universities/{universityId}/campuses/{campusId}/buildings

I wanted to know the best practice of how to validate the ID of the path of my Rest API.
For example:
When I do a GET to retrieve a Building, I need to validate first if the {universityId} and {campusId} are actually valid (Existing in the DB) before proceeding.
Right now I have implemented a custom RepositoryValidation that provides those functionalities by throwing a ResourceNotFoundException() and those methods are called in my service class for the GET,PUT,POST..etc
Is there a better way to do the validation? I have read about Interceptors or Filters but not sure if that's the best practice.
Custom Exception:
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() {
super();
}
public ResourceNotFoundException(String message) {
super(message);
}
Repository Validation:
#Component
public class RepositoryValidation {
#Autowired
private UniversityRepository universityRepository;
#Autowired
private CampusRepository campusRepository;
#Autowired
private BuildingRepository buildingRepository;
public void checkIfUniversityExists(Long universityId){
if (!universityRepository.exists(universityId))
throw new ResourceNotFoundException("University with id: " + universityId + " not found");
}
public void checkIfCampusExists(Long campusId){
if (!campusRepository.exists(campusId))
throw new ResourceNotFoundException("Campus with id: " + campusId + " not found");
}
public void checkIfBuildingExists(Long buildingId){
if (!buildingRepository.exists(buildingId))
throw new ResourceNotFoundException("Building with id: " + buildingId + " not found");
}
}
Service:
#Service
public class BuildingService {
#Autowired
private BuildingRepository buildingRepository;
#Autowired
private RepositoryValidation repositoryValidation;
public Iterable<Building> list(Long campusId) {
return buildingRepository.findAllByCampusId(campusId);
}
#Transactional
public Building create(Building building) {
return buildingRepository.save(building);
}
public Building read(Long buildingId,Long campusId) {
repositoryValidation.checkIfCampusExists(campusId);
repositoryValidation.checkIfBuildingExists(buildingId);
return buildingRepository.findBuildingByIdAndCampusId(buildingId,campusId);
}
#Transactional
public Building update(Long buildingId,Building update) {
repositoryValidation.checkIfBuildingExists(buildingId);
Building building = buildingRepository.findOne(buildingId);
building.setBuildingName(update.getBuildingName());
return buildingRepository.save(building);
}
#Transactional
public void delete(Long buildingId,Long campusId) {
repositoryValidation.checkIfCampusExists(campusId);
repositoryValidation.checkIfBuildingExists(buildingId);
buildingRepository.deleteBuildingByIdAndCampusId(buildingId, campusId);
}
You should look into Springs' Validation-Beanvalidation.
With this, you can use #Valid to do simple validations on properties, for example:
#NotNull
#Size(max=64)
private String name;
You can also add the #Valid to inputs in a REST endpoint:
#RequestMapping("/foo", method=RequestMethod.POST)
public void processFoo(#Valid Foo foo) { /* ... */ }
For your needs, you could consider creating a custom #Constraint.
You would first create the constraint annotation:
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy=MyConstraintValidator.class)
public #interface MyConstraint {
}
And then the constraint validator:
import javax.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
#Autowired;
private Foo aDependency;
...
}
Notice you can inject other Spring beans into the ConstraintValidator as well.
Once implemented, this could easily be re-used and looks very concise.

Resources