Create repeatative springboot controllers from configuration - java

Dynamic Parent Controller
#CrossOrigin("*")
public abstract class RealtimeController<T> {
public abstract RealtimeService<T> getService();
#PostMapping(value = "/find")
public ResponseEntity<Iterable<T>> find(#RequestBody T entity) {
return ResponseEntity.ok(getService().findAllByKey(entity));
}
}
Dynamic Service
public abstract class RealtimeService<T> {
public abstract RealtimeRepository<T> getRepository();
public Iterable<T> findAllByKey(T entity) {
return getRepository().findAll(
Example.of(
entity,
ExampleMatcher.matching().withIgnorePaths("_class"))
);
}
}
Child Controller1
#Api(tags = "Child1")
#RestController
#RequestMapping("/api/v1/child1")
#Slf4j
public class Child1Controller extends RealtimeController<Child1> {
#Autowired
private Child1Service child1Service;
#Override
public RealtimeService<Child1> getService(){
return child1Service;
}
}
Child Controller2
#Api(tags = "Child2")
#RestController
#RequestMapping("/api/v1/child2")
#Slf4j
public class Child2Controller extends RealtimeController<Child2> {
#Autowired
private Child2Service child2Service;
#Override
public RealtimeService<Child2> getService(){
return child2Service;
}
}
Question1:
I have 10 of these child controllers. Is there a way to dynamically create them from some config? The only difference between every child controller is the Model, tag and endpoint. My idea was to read these config from a property file and generate these controllers dynamically somehow. is it possible?
application.yaml
controllers:
- name: Child1
endpoint: /api/v1/child1
model: Child1
- name: Child2
endpoint: /api/v1/child2
model: Child2
Question2:
Is there another better way to achieve similar results without code duplication ?

You can do
#CrossOrigin("*")
#RestController
#RequestMapping("/api/v1/{childtype}")
public class RealtimeController {
public RealtimeService getService(String childtype){
return map.get(childtype);
}
#PostMapping(value = "/find")
public ResponseEntity<Iterable<Object>> find(#PathVariable("childtype") String childtype, #RequestBody Object entity) {
return ResponseEntity.ok(getServiceByType(childtype).findAllByKey(entity));
}
}
All you need now is to fill map with instances of service.

Related

How to specifiy custom Request handling into spring MVC controller?

I have defined an class for some specific handling:
public abstract class ListProvider {
...
public abstract ResponseObject getResponse(RequestObject request) {}
}
I will create several derived class that I will define as services.
The purpose is to use it to create a json API using Jackson to deserialize the RequestObject and to serialize the ResponseObject. For example:
#Service
public class ClientListProvider extends ListProvider {
public ResponseObject getResponse(RequestObject request) {
return ...
}
I can use it in a controller like that:
#RestController
#RequestMapping("/client")
public class ClientController {
#AutoWired
ClientListProvider provider;
#PostMapping("/list")
public ResponseObject ResponseObject list(#RequestBody RequestObject request) {
return provider.getResponse(request);
}
}
But I would like to use it without the boiler plate, like:
#RestController
#RequestMapping("/client")
public class ClientController {
#PostMapping("/list")
#Provider(ClientListProvider.class)
public list() {}
}
Or maybe:
#RestController
#RequestMapping("/client")
#Provider(ClientListProvider.class,path="/list")
public class ClientController {
}
Or something like that.
Do you know if there any way? If necessary I could replace the Request/ResponseObject by HttpServletRequest/Response or something else in the ListProvider interface.

Spring Boot Common JPA repository class

I have a number of JPA repositories classes and I want to create one common class where I will create a getter method of a respective repository and I will use that common class in the service layer.
So Can you please guide me with best practices that how can I achieve this?
Here I am sharing my idea by using sample code,
JPA repository
#Repository
public interface IConfigRepository extends JpaRepository<Config, Integer> {
}
public interface IBusinessRepository extends JpaRepository<Business, Integer> {
}
Repo Factory (Common Class for all repositories)
public class RepoFactory {
#Autowired
private IConfigRepository configRepo;
#Autowired
private IBusinessRepository businessRepo;
public IConfigRepository getConfigRepository() {
return configRepo;
}
public IBusinessRepository getBusinessRepository() {
return businessRepo;
}
}
Service Class
#Service
public class ServiceA {
public final RepoFactory repoFactory;
public ServiceA(RepoFactory repoFactory) {
this.repoFactory = repoFactory
}
#Transactional(rollbackOn = Exception.class)
public void saveOrUpdate(Config config) {
repoFactory.getConfigRepository().save(config);
}
}
#Service
public class ServiceB {
public final RepoFactory repoFactory;
public ServiceB(RepoFactory repoFactory) {
this.repoFactory = repoFactory
}
#Transactional(rollbackOn = Exception.class)
public void saveOrUpdate(Business reqBusiness) {
repoFactory.getBusinessRepository().save(reqBusiness);
}
}
Thanks, everyone for helping me in advance.
It looks like, you're trying to do something the #Profile annotation can help you with. If I were you, I would keep a common interface (not class) and make the IConfigRepository extend it. Then you can mark IConfigRepository with the #Profile annotation. If in the future, you have to write an analogue interface, you should also mark it with the #Profile annotation and you can switch between these interfaces anytime you want by setting the appropriate profile to active.
#Repository
#Profile("config")
public interface IConfigRepository extends CommonRepository, JpaRepository<Config,Integer> {
}
public interface CommonRepository {
}
#Service
public class ServiceA {
public final CommonRepository commonRepository;
public ServiceA(CommonRepository commonRepository) {
this.commonRepository = commonRepository
}
...
}

Configuration properties not autowired when used in request body?

I'm using a particular class (ClassA) in my controller as the request body, but within that class, my autowired ConfigurationProperties is null.
Controller:
#RestController
#RequestMapping(value = "/rest/v1/")
public class XyzController {
#Autowired
ServiceXyz serviceXyz;
#PostMapping(value = "/route")
public void route(#RequestBody ClassA classA) {
serviceXyz.methodAbc(classA);
}
}
ServiceXYZ:
#Service
public class ServiceXyz {
public boolean methodAbc(ClassA classA) {
return classA.methodA() && otherStuff();
}
}
ClassA.java:
#Component
public class ClassA {
#Autowired
ApplicationProperties applicationProperties;
public boolean methodA() {
return fieldA.equals(applicationProperties.someProperty());
}
}
ApplicationProperties.java:
#Component
#ConfigurationProperties(prefix="stuff")
public class ApplicationProperties {
// etc.
}
It's within ClassA.methodA that applicationProperties is null, even though everybody is marked with the correct annotations, and autowiring is working throughout the rest of the application.
Is it possible that this just doesn't work?
Autowiring works for objects from Spring context. In your request object of ClassA is parsed from JSON I think and is not taken from Spring context.
You'd better change your code to make ClassA as simple DTO and inject ApplicationProperties into your service class.
You can change your ClassA to this
public class ClassA {
public boolean methodA(ApplicationProperties applicationProperties) {
return fieldA.equals(applicationProperties.someProperty());
}
}
And your service to this:
#Service
public class ServiceXyz {
#Autowired
private ApplicationProperties applicationProperties;
public boolean methodAbc(ClassA classA) {
return classA.methodA(applicationProperties) && otherStuff();
}
}

#Autowired abstract class from subclass

I have a controller for REST services for a particular type of resources (Symptoms) that looks like this:
#RequestMapping(value = "/symptom", produces = "application/json")
public class SymtomController {
#Autowired
private SymptomRepository repository;
#Autowired
private SymptomResourceAssembler assembler;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Collection<SymptomResource>> findAllSymptoms() {
List<SymptomEntity> symptoms = repository.findAll();
return new ResponseEntity<>(assembler.toResourceCollection(symptoms), HttpStatus.OK);
}
...
}
But, as I need to produce more controllers, for other resources, I would like to generate an abstract class and subclasses
public class AbstractController<Entity extends AbstractEntity, Resource extends GenericResource {
// repository and assembler somehow transferred from subclasses
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Collection<Resource>> findAllResources() {
List<Entity> entities = repository.findAll();
return new ResponseEntity<>(assembler.toResourceCollection(entities), HttpStatus.OK);
}
...
}
#RequestMapping(value = "/symptom", produces = "application/json")
public class SymtomController extends ResourceController<SymptomEntity, SymptomResource>{
#Autowired
private SymptomRepository repository;
#Autowired
private SymptomResourceAssembler assembler;
...
}
But I do not know it is possible, somehow, to transfer the autowired elements in the subclasses to the abstract class in a nice way (i.e. not sending them as parameters on each function call).
Any ideas?
Move the dependencies to the parent.
abstract class Parent {
#Autowired
protected MyRepository repo;
#PostConstruct
public void initialize(){
System.out.println("Parent init");
Assert.notNull(repo, "repo must not be null");
}
}
#Component
class Child extends Parent {
#PostConstruct
public void init(){
System.out.println("Child init");
Assert.notNull(repo, "repo must not be null");
}
}

Java class inheritance/interface implementation principle

I am not sure of using inheritance / interface implementation in particular situation.
In my simple Spring MVC application I have #Entity class TennisPlayer, which is inherited from abstract class Player (TennisPlayer adds some attributes).
Also I have class TennisPlayerForm, which is inherited from abstract class PlayerForm (TennisPlayerForm adds some attributes again).
User fills the form about tennis player in .jsp page and TennisPlayerForm object is used to represent filled values and then on the basis of this object is created TennisPlayer object and saved into database.
Creation of TennisPlayer object is responsibility of class TennisPlayerDbService. This class is implementation of interface PlayerService.
I have following #Controller, which handles requests:
#Controller
public class NewPlayerController {
#Resource(name="tennisPlayerService")
private PlayerService playerService;
//omitted RequestMethod.GET handler method
#RequestMapping(value = "/newplayer", method = RequestMethod.POST)
public String newplayer(Locale locale, #ModelAttribute("tennisPlayerForm") #Valid TennisPlayerForm tennisPlayerForm,
BindingResult result, RedirectAttributes redirectAttributes) {
playerService.createPlayer(tennisPlayerForm);
return "redirect:/allplayers";
}
}
Part of my source code looks like this:
public interface PlayerService {
public void createPlayer(PlayerForm playerForm);
}
#Service(value="tennisPlayerService")
public class TennisPlayerDbService implements PlayerService {
private TennisPlayerDAO dao;
#Autowired
public void setDao(TennisPlayerDAO dao) {
this.dao = dao;
}
#Override
public void createPlayer(PlayerForm playerForm) {
TennisPlayerForm tennisPlayerForm = null;
if (playerForm instanceof TennisPlayerForm) {
tennisPlayerForm = (TennisPlayerForm) playerForm;
}
else {
throw new IllegalArgumentException("Must be of type TennisPlayerForm.");
}
TennisPlayer player = new TennisPlayer();
player.setName(tennisPlayerForm.getName());
player.setSurname(tennisPlayerForm.getSurname());
player.setAge(tennisPlayerForm.getAge());
player.setRacket(tennisPlayerForm.getRacket());
player.setRanking(tennisPlayerForm.getRanking());
player.setSponsor(tennisPlayerForm.getSponsor());
player.setCoach(tennisPlayerForm.getCoach());
player.setClub(tennisPlayerForm.getClub());
dao.saveAndFlush(player);
}
}
Is it justified to use inheritance and interface implementations like this in this situation, when concrete implementation of PlayerService (TennisPlayerDbService) expects instance of particular class, although these potential classes have common parent?
Finally I solved my problem according to your comments and answers.
I deleted PlayerForm abstract class, TennisPlayerForm and mixed javax.validation and javax.persistence annotations in #Entity classes Player and Tennis Player.
Previously mentioned code now looks like this:
#Controller
public class NewPlayerController {
#Resource(name="tennisPlayerService")
private PlayerService<TennisPlayer> playerService;
//omitted RequestMethod.GET handler method
#RequestMapping(value = "/newplayer", method = RequestMethod.POST)
public String newplayer(Locale locale, #ModelAttribute("tennisPlayer") #Valid TennisPlayer tennisPlayer,
BindingResult result, RedirectAttributes redirectAttributes) {
if(result.hasErrors()) {
return "newplayer";
}
playerService.createPlayer(tennisPlayer);
MessageUtil.flash(locale, redirectAttributes, "success", "signup.success");
return "redirect:/allplayers";
}
}
public interface PlayerService<T extends Player> {
public void createPlayer(T player);
public List<T> getAllPlayers();
}
#Service(value="tennisPlayerService")
public class TennisPlayerDbService implements PlayerService<TennisPlayer> {
private TennisPlayerDAO dao;
#Autowired
public void setDao(TennisPlayerDAO dao) {
this.dao = dao;
}
#Override
public void createPlayer(TennisPlayer player) {
dao.saveAndFlush(player);
}
#Override
public List<TennisPlayer> getAllPlayers() {
return dao.findAll();
}
}
Normally your service does not need to know you are working with a form. Your form is purely created to be the model in the model-view-controller architecture of your webpage. (your jsp being the view and your controller being the c-part)
Are you also planning on using other types of players than a TennisPlayer? If not it all seems like premature optimisation and you should keep it as simple as possible.

Categories

Resources