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");
}
}
Related
My class Rest doesn't invoke my other class
#CrossOrigin(allowedHeaders = "*")
#RestController
public class PhoneRest {
#Autowired
private TradePhoneService service;
#CrossOrigin(origins = {"http://localhost:4200","http://localhost:8081"})
#PostMapping(value = "/listarRamaisDiponiveis", produces = "application/json")
public ResponseEntity <List<TpbPrvtRecPhoneExtVO>> listarRamaisDiponiveis() {
List<TpbPrvtRecPhoneExtVO> list = new ArrayList<>();
try {
list =service.listarRamaisDiponiveis(); // here java.lang.classcastexception
About interface TradePhoneService:
public interface TradePhoneService {
public List<TpbPrvtRecPhoneExtVO> listarRamaisDiponiveis();
About class TpbPrvtRecPhoneExtVO:
public class TpbPrvtRecPhoneExtVO extends BaseVO implements Serializable{
And about class TradePhoneServiceImpl that implements TradePhoneService:
#Transactional(rollbackOn=Exception.class)
#Service
public class TradePhoneServiceImpl implements TradePhoneService {
#Autowired
private TradePhoneDAO dao;
public List<TpbPrvtRecPhoneExtVO> listarRamaisDiponiveis() {
return listBeansToVos(dao.listarRamaisDiponiveis());
}
Where is the problem?
Thanks
Marcos Vizine
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.
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();
}
}
I'm trying to create an abstract class that performs the common REST operations that are required, but can't work out if what I'm trying to do is possible. I've tried a number of approaches, but have stripped the code below right back to how it should work in my head
Classes updated as per suggestions below. Problem now is that the constructor in the concrete class isn't valid, as CustomerRepository isn't assignable to JpaRepository, though it extends that interface.
AbstractRestController
public abstract class AbstractRestController<T> {
private final JpaRepository<T, Serializable> repository;
public AbstractRestController(JpaRepository<T, Serializable> repository) {
this.repository = repository;
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<JsonResponseBody<T>> getOne(#PathVariable Long id) {
T restObj = repository.findOne(id);
JsonResponseBody<T> response = new JsonResponseBody<>(ResponseStatus.SUCCESS, restObj);
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON_UTF8).body(response);
}
protected JpaRepository<T, Serializable> getRepository() {
return repository;
}
}
CustomerController
#RestController
#RequestMapping(value = "/api/v1/customer")
public class CustomerController extends AbstractRestController<Customer> {
#Autowired
public CustomerController(CustomerRepository repository){
super(repository);
}
}
CustomerRepository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}
Indeed, as #dino-tw mentions, you are trying to instantiate an abstract class with an undefined dependency. You can absolutely have an abstract controller class, and even define request handling methods that will be inherited by all subclasses. Try this instead:
public abstract class AbstractRestController<T, ID extends Serializable> {
private final JpaRepository<T, ID> repository;
public AbstractRestController(JpaRepository<T, ID> repository){
this.repository = repository;
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<JsonResponseBody<T>> getOne(#PathVariable ID id) {
T restObj = repository.findOne(id);
JsonResponseBody<T> response = new JsonResponseBody<>(ResponseStatus.SUCCESS, restObj);
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON_UTF8).body(response);
}
protected JpaRepository<T, ID> getRepository(){ return repository; }
}
#RestController
#RequestMapping(value = "/api/v1/customer")
public class CustomerController extends AbstractRestController<Customer, Long> {
#Autowired
public CustomerController(CustomerRepository repository){
super(repository);
}
}
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;