Spring Boot disable #Cacheable per request - java

I'm trying to disable a Cacheable function in Spring Boot based on a URL parameter. For example http://myapplication.com/some/request?cache=false
I tried the condition with SpEL but I haven't been able to get it work
#Cacheable(value = "value", keyGenerator = "keygenerator", condition = "#{someComponent.isCacheEnabled()}")
Any suggestions? Thanks in advance!

user4843-
I am assuming your "arbitrary component" (i.e. someComponent) is a Spring managed bean in your Spring ApplicationContext?
If so, then you have not properly referenced this Spring managed bean using the SpEL expression specified on the condition attribute inside your #Cacheable annotation declaration. It should read...
condition="#{#someComponent.isCacheEnabled()}"
Notice the use of the # symbol on someComponent, which is explained in more detail here. And, although the Spring documentation is less than clear on the matter, it does imply the SpEL evaluation context created and used inside Spring's Cache Abstraction when processing the caching annotations uses "the built-in parameters", or SpEL conventions, in addition to the ones listed in Table 36.1 - Cache SpEL available metadata. See examples here.
I would also caution you on your use of some arbitrary component, or Spring managed bean to manage caching state for your entire application like this.
First, if someComponent is a Singleton bean (the default in the Spring container) then this bean would need to be Thread-safe, particularly as its state is changing, possibly and seemingly with every HTTP request, which leads me to...
I am not sure you want a Spring managed bean managing the "cacheable state" of your application service/repository methods if the caching state is conditional on each and every individual HTTP request, particularly since most HTTP containers process each HTTP request in a separate Thread. You are most certainly going to run into race conditions. In this case, I'd rather use an additional cacheable service method parameter to determine the cacheable state of that particular service method invocation, stemming from the HTTP request.
Example:
#Cacheable(value = "value", keyGenerator="keyGenerator" conditional="#enableCaching")
public <return-type> someCacheableServiceMethod(..., boolean enableCaching) {
...
}
In fact, I'd even suggest using more intelligible business-oriented rules to determine the cacheable state of your service methods.
Hope this helps!
-John

Related

Is there a possibility to add custom spring boot metrics tags at runtime?

I'm using Spring Boot with micrometer-registry-prometheus, trying to store custom tags from http headers, security context, etc.
So I found MeterFilter iterface and tried to use it to store the data I need.
But MeterFilter method works after request is completed, so at that point I don't have any information about request.
Seems like the case is pretty common, is there any possibility to achieve it?
If you're using Spring MVC you can define a bean that implements WebMvcTagsProvider to take complete control over the tags that are added to metrics for request-response exchanges. Alternatively, you can define a bean that implements WebMvcTagsContributor to add to the default tags. Similarly, if you're using Spring WebFlux you can define beans that implements WebFluxTagsProvider and WebFluxTagsContributor to take complete control over the tags and contribute additional tags respectively.

Spring security new method level annotation

I'm working on securing a REST Service endpoints with Spring Security. I basically need to check if user has given Authority, if the user can invoke the function with the given parameters, and lastly, I filter the output, so that the user cannot see things that it shouldn't.
For this, I have this set of annotations:
#PostFilter("#canViewOwnAssignment.canView(filterObject) or #canViewAllAssignments.canView(filterObject)")
#PreAuthorize("hasAnyAuthority('canViewOwnAssignment', 'canViewAllAssignments') and (#canViewOwnAssignment.canEnter(userId) or #canViewAllAssignments.canEnter(userId))")
...for all the methods. The only thing changing from the above snippet is canViewOwnAssignment and the parameter(s) of .canEnter().
I'd like to simplify this, so that I can have an annotation looking sg like this:#MyAnnotation(bean = CanViewAssignment.class, args = {"userId"})
How could I make this happen?
I tried extending PrePostAnnotationSecurityMetadataSource.class, since that's the one parsing the annotations, however I can't just use #Primary to override it, since the bean instantiation is baked into the GlobalMethodSecurityDefinitionParser.class
If I don't need to, I'd rather not start rewriting half of the Spring Security, only to have one overriden method.

Spring MVC: Log Controller paths without params

I am trying to add some metric gathering to a Spring MVC app. Lets say I have a controller whose mapping is:
/User/{username}/Foobar
I want to gather metrics on all controller mapping invocations with the path. Right now I can create a handler/interceptor and look at the requests but that will give me:
/User/Charlie/Foobar
Which is not what I want. I want the controller mapping itself to log. and I don't want to have to add something to every controller. I'd also rather not use AOP if I can help it.
It turns out that Spring hangs the best matching controller pattern on the request itself. You can get this from within a handlerinterceptor like this:
(String)request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)
I can think of two choices:
It seems to me the results of the matching are obtained in the class org.springframework.web.servlet.handler.AbstractUrlHandlerMapping, which logs the patterns obtained (see line 266). I'd try enabling logging for that class and see if the output is helpful for your purposes.
(Complicated)
Extending org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping to override the lookupHandler method inherited from AbstractUrlHandlerMapping and logging/registering what you need. Accoding to this class documentation, you can register a different one so that the DispatcherServlet uses your version.
In Spring 3.2.x DefaultAnnotationHandlerMapping is deprecated so, a different class would have to be used.

Managed Bean method authorization using custom java annotations in JSF 2.0

I would like to create a custom annotation to decorate methods which would restrict access to method calls.
My annotation is defined below:
#Inherited
#Documented
#Retention(RetentionPolicy.RUNTIME)
public #interface Restrict {
public enum RoleType {All, ROLE_A, ROLE_B, ROLE_C, ROLE_D}
String roleLevel();
RoleType roleType();
}
Then using the annotation like the following. The annotation receives two parameters, one being the required minimum roleType, the other the required minimum role level.
#Restrict(roleType = RoleType.ALL, roleLevel="user")
String deleteSomething() {
// delete intended whatever
return success;
}
My intent is, when any call to a Managed Bean method that is decorated with this annotation, as in the method described above "deleteSomething()", occurs, this call would be intercepted and the parameters set on the method compared to the logged in users appropriate session values. If the logged in users session role values are high enough, the Managed Bean's method will be allowed to be invoked, otherwise the user is either redirected or an appropriate message is displayed.
My question is this, is there a way I can "hook" into what methods are bing called to then, through reflection, see if there is a #Restrict annotation on the method and then process said annotation. I've tried doing this in a PhaseListener class, but I'm not sure how to find out what Managed Bean is being called to perform refection on. I've read about a custome ElResolver, but I'm not sure if this is anything that will help me. I've also tried to find a way to simply create a listener that somehow knows when a method that is annotated with #Restrict has been invoked.
Environment Specifics:
Tomcat 6.0.35 (considering upgrading to Tomcat 7.0.27)
JSF version 2.1.7
RichFaces 4.1.0
I'm just looking for some guidance and some options available to me. Thank you to anyone who can help me with this!
You can achieve this by implementing a custom ActionListener which is been registered as a global <action-listener> in the faces-config.xml.
By coincidence, someone else asked and answered the same question this week: Custom Annotation JSF. Note that JAAS is not required for the particular purpose, just grab the User from the session by FacesContext the usual way.

How to set objects on Spring 3.1 MVC controller from an interceptor?

I'm having a couple of issues related to interceptors since upgrading to 3.1. In version 3.0.x and earlier I used the following pattern to intercept Spring MVC controllers:
Create an interface called something like RoleAware which defines one or more setters.
Have one or more controllers implement the interface
Register a new global interceptor which does a "handler instanceof RoleAware" check in the preHandle
If the interceptor is an instanceof RoleAware, then set one or more objects on the implementing controller
The first issue is that something changed in 3.1 so the instanceof check fails. I've fixed this by using the new explicit elements in my servlet context configuration. Not a big deal and a bit cleaner approach than the instanceof check.
The second issue is that when I attempt to cast the handler (Controller) to my RoleAware interface I get a ClassCastException.
I'd like a solution that will enable me to continue to arbitrarily set objects on the intercepted controllers. For example, set a Role object on any Controllers that are intercepted. Also, I'd like to know more about the changes in 3.1 that is causing this to break.
The reference doc says:
When using the RequestMappingHandlerMapping the actual handler is an
instance of HandlerMethod which identifies the specific controller
method that will be invoked.
So I guess that you should just cast the handler to HandlerMethod, call its getBean() method, and check is the returned bean is an instance of RoleAware.
Not tested though. You could try using a debugger to inspect the handler argument and see what it is if it is neither the handler itself, nor the HandlerMethod.

Categories