Share a common parent path when implementing Spring controllers - spring

I want to be able to define a parent Controller class which will have a mapping of "/api", and then extend that controller with my different implementations.
So ApiController will have:
#Controller
#RequestMapping("/api")
For example, my User controller should extend the base api controller and also add "/users" to the path, so it will answer to "/api/users" requests. So UserController will have:
#Controller
#RequestMapping("/users")
but since it extends ApiController, it will effectively answer to /api/users.
Naturally I can prepend "/api" to all controllers so that this is achieved without the parent class, but I prefer to do it "the right way" if it's possible, so that I can define my api implementations with a cleaner and more visible path.
I tried extending the ApiController base class, but this does not work, UserController still answers to "/users" and ignores the base class "/api".

Hmmm. You can try this, it is what works for me:
#RequestMapping("/abstract")
public abstract class AbstractController {
}
#Controller
public class ExtendedAbstractController extends AbstractController {
#RequestMapping("/another")
public String anotherTest() {
return "another";
}
}
Note, your base class must have no #Controller annotation and must be abstract.
If you try to do extend not abstract class annotated as controller and that has #RequestMappings you get errors on step where RequestMappingHandlerMapping initializing.

You can achieve it with:
create sub-context configuration for controllers with same path prefix
create sub-context with own DispatcherServlet mapped to certain path
Look at this answer. It's similar problem, but for #RestControllers.

Related

Have a common path with variables in different services

I have two interfaces with the annotation #RequestMapping:
#RestController
#RequestMapping("/users")
public interface UserRestService{}
and
#RestController
#RequestMapping("/clients")
public interface ClientRestService{}
They both have methods to manage users and clients.
I already defined the initial path with the property:
servlet:
context-path: /api
Now, I want to define a common path with a path parameter /v1/tenant/{tenant}/, so the complete URL for the services will be :
/api/v1/tenant/{tenant}/users
/api/v1/tenant/{tenant}/clients
I already tried having those two interfaces extending a common interface and with an annotation but without luck.
How can I do this without define for each interface the complete path?
You can define /v1/tenant/{tenant} as an entry in the properties file. Then, you can inject it like this:
#RestController
#RequestMapping("${my.common.path}/clients")`
public interface ClientRestService{}
#RestController
#RequestMapping("${my.common.path}/users")`
public interface UserRestService{}
Properties file:
my.common.path = /v1/tenant/{tenant}
Finally, to access that path variable inside a method from either controller, you can do this:
#GetMapping("/")
public String test(#PathVariable String tenant) {
return tenant;
}

resilience4j annotations not working on chlid class

I am using resilience4j with SpringBoot. I see that the resilience4j annotations work only if they are placed in the class which throws the exception. If the class is extended by another class & the parent class has the annotation then the retries do not work.
Resilience4j Config
resilience4j.retry:
instances:
service:
maxRetryAttempts: 5
waitDuration: 1000
retryException:
- com.common.exception.RetriableException
Parent Class
#Retry(name = "service")
#Component
public class httpClient extends client{
// This method is invoked from outside
public HttpResponse<T> getResponse(
String url, Class<T> responseType) {
return super.getResponse(url, requestEntity, responseType);
}
}
Child Class
#Retry(name = "service") // Without this line, the retries don't work, even though it is present in the parent class
#Component
public class client{
public HttpResponse<T> getResponse(
String url, Class<T> responseType) {
//Impl which throws RetriableException
}
}
Is this the expected behaviour ? Can you let me know if I am missing something
I never used Resilience4j before, but what I can tell you about Java annotations in general is:
An overridden method in a subclass never inherits annotations from the original parent class method.
In a class implementing an interface an implementing method never inherits annotations from the corresponding interface method.
A classes implementing an interface never inherits annotations from the interface itself.
An interface extending another interface also never inherits any type or method level annotations.
By default not even a subclass inherits annotations from its parent class.
There is a single exception to this "annotations are never inherited" rule: A type level annotation (something like #Foo class Base, can also be abstract) can be inherited by a subclass (something like class Sub extends Base) if and only if the annotation class itself carries the meta annotation #Inherited.
Having said that and looking at the #Retry annotation, you will notice that there is no #Inherited annotation there, so it also cannot work in your case.
If there is another way (e.g. via reflection) to get this done in Resilience4j, I do not know because, as I said, I never used it before.

Spring AOP: #within behavior when extending classes

I have an annotation which will be used on extended placeholder classes. Basically, our service will have an implementation, and we will have an explicit extension which will be annotated. I am not sure what the problem is, but #within is not invoking the code whereas #target is.
Here is a sample code - https://github.com/sahil-ag/Spring-AOP-Sample
#Component
public BaseClass { public void getData() {return "";}}
#SampleAnnotation
#Component
public DerivedClass extends BaseClass {}
Here if we now use a #within(SampleAnnotation) pointcut, we will not be able to intercept the getData() when called from a derived class bean.
The #within Annotation is used when you want to define a class where the pointcuts are located in. So make sure that your within-clause looks like:
#within(#MyAnnotation *)
The '*' is used to say any class. This is the part you are missing in your example.
Another approach would be to use the #annotation pointcut:
#Annotation(#MyAnnotation)
Official documentation:
https://www.eclipse.org/aspectj/doc/next/adk15notebook/annotations-pointcuts-and-advice.html

Auto-wiring Multiple implementation of a Abstract class

I have been working on a spring MVC project structure where multiple concrete service classes extends from a Abstract Class.
In my controller, i am trying to autowire the abstract class and choose the implementation based on the user choice. Here's the relevant portion of Controller class:
#Controller
#RequestMapping("/")
public class DashboardController {
#Autowired
LogAnalyzerAbstract logAnalyzer;//new LogAnalyzer();
private static final Log logger =LogFactory.getLog(DashboardController.class);
....
logAnalyzer.process();
}
I want to the controller to use the implementation based on the user input and used the Abstract class LogAnalyzer reference in the further implementaion.
Is my logic valid? Can you guide me through?
If I understand what you ask correctly, what you need to do is to create several Spring-MVC controllers, one for each use case, with its own base path, and to autowire there the abstract service class (or the service interface), based on the bean name of the concrete service (if you use the annotation #Service on concrete service classes, the bean name should be the name of the concrete service class, starting with a lowercase letter).
For example, something like the following:
#Service
public class MyFirstUseCaseService extends AbstractService {
...
}
and in the controller
#Controller
#RequestMapping("/firstUseCaseBaseUrl")
MyFirstUseCaseController {
#Autowired
#Qualifier("myFirstUseCaseService")
private AbstractService service;
....
}

Suggest MVC frameworks that do not use controller base classes

Pseudo-code:
class SomeController {
def myAction() {
// controler is an property passed via ctor
controller.redirect(toWhereever)
}
}
// another variant
class AnotherController {
def myAction(controller) {
// controler is an method argument
controller.redirect(toWhereever)
}
}
Any suggestions?
Edit: Because the question is a bit dry you could try to spice up your answers with some experience with the framework and what do you think is better with that approach.
Spring MVC and Grails (built ontop of spring) favour dependency injection with no inheritance whatsoever. Each controller is a class that does not extend anything. Instead you can inject other components into it (using dependency-injection). For example:
#Controller
#RequestMapping("/user")
public class UserController {
#Inject
private UserService service;
#RequestMapping("/register")
public String register(User user) {..}
}
#Controller
#RequestMapping("/orders")
public class OrderController {
#Inject
private UserController userController
}
(Although it is not a common practice to inject controllers into other controllers, you can inject any object)
web2py - controllers are functions
web.py
puremvc - controllers are Commands
camping - odd, replaces internal controllers
limonade - procedural
Django has decided to use different terms for the MVC pattern. In django-speak "view" is what most people call controller. a django view is just a function taking a request instance and returning a response instance. Roughly it looks like this:
from django.http import HttpResponse, Http404
import datetime
def current_datetime(request):
if request.method != 'GET':
raise Http404
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
JSF & Spring MVC
An easy tutorial for JSF: www.coreservlets.com/JSF-Tutorial/jsf2/index.html#Annotations
A basic example from SpringSource: http://src.springframework.org/svn/spring-samples/mvc-basic/trunk
Q: Why JSF
A:
You can perform your action at SimpleController#doSomething
#ManagedBean
public class SimpleController {
public String doSomething() {
...
}
}
And SimpleController does not extend any controller, #ManagedBean helps to make it look like a controller.
Q: Why Spring MVC
A:
You can perform your action at "..../doSomething"
#Controller
public class SimpleController {
#RequestMapping("/doSomething")
public String doSomething() {
...
}
}
SimpleController does not extend any controller.
#Controller helps to make the class a controller
#RequestMapping bind its url to "/doSomething"
[sorry for bold url, i can only post maximum one url in an answer as a newbie :-S]
Struts 2 also lets you do this. All the actions are simple java beans.

Resources