Spring HTTP cache management - spring

I've seen that you can control cache http headers with the AnnotationMethodHandlerAdapter bean.
My problem is that I need to have a fine grane control on the cache (at method level).
The best think would be to have something like an annotation like "#RequestCache(expire=60)".
Is there anything like this?
What is the best way to accomplish this task?
Thanks,
Andrea
Update:
pap suggest to use an HandlerInterceptor, but I've seen multiple forum's post saying that it's not possible to get the target method inside an HandlerInterceptor and suggest to use regular AOP instead (not specifically for caching).
The problem is that I don't want to add the request parameter to all my methods, only to make it accessible to the aspect. Is there a way to avoid this?

You can use the following approach described in
Spring mvc reference manual
Support for the 'Last-Modified' Response Header To Facilitate Content Caching
#RequestMapping(value = "/modified")
#ResponseBody
public String getLastModified(WebRequest request) {
if (request.checkNotModified(lastModified.getTime())) {
logger.error("Was not modified.");
return null;
}
logger.error("Was modified.");
//processing
return "viewName";
}

One way (that I have used myself) is to create your own HandlerInterceptor.
public class CacheInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
Class<?> o = AopUtils.getTargetClass(handler);
if (o.isAnnotationPresent(RequestCache.class)) {
response.setDateHeader("Expires", o.getAnnotation(RequestCache.class).expire());
}
return true;
}
...
}
and then
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<array>
<bean class="bla.bla.CacheInterceptor " />
</array>
</property>
</bean>

Related

How to parse spring security expressions programmatically (e.g. in some controller)

How do you parse spring (web) security expressions like hasRole('admin') programmatically (without using tags, annotations or ...)? (reference doc)
I've found Spring: What parser to use to parse security expressions - but I don't know how to find or build the EvaluationContext e.g. inside a spring controller.
Without providing an EvaluationContext gives
org.springframework.expression.spel.SpelEvaluationException: EL1011E:(pos 0): Method call: Attempted to call method hasRole(java.lang.String) on null context object
you need to add several things in order to get this thing working. You have to plug into the Spring's security API. Here's how I did it and it is working fine with Spring 3.2.
First as it was stated before you must have similar configuration in your spring-context.xml:
<security:http access-decision-manager-ref="customAccessDecisionManagerBean">
<security:http/>
<bean id="customWebSecurityExpressionHandler"
class="com.boyan.security.CustomWebSecurityExpressionHandler"/>
<bean id="customAccessDecisionManagerBean"
class="org.springframework.security.access.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler" ref="customWebSecurityExpressionHandler" />
</bean>
</list>
</property>
</bean>
This defines a new expressionHandler to override the default one for the WebExpressionVoter. Then we add this new decision voter to the decision manager. CustomWebSecurityExpressionHandler's purpose it to control the creation of SecurityExpressionRoot. So far so good. The question is why do you need a CustomWebSecurityExpressionRoot and the answer is simple as that - you define your custom security methods there. Having this in mind we can write the following classes:
public class CustomWebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler {
#Override
protected SecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication, FilterInvocation fi) {
CustomWebSecurityExpressionRoot expressionRoot =
new CustomWebSecurityExpressionRoot(authentication, delegationEvaluator);
return expressionRoot;
}
}
}
public class CustomWebSecurityExpressionRoot extends WebSecurityExpressionRoot {
public CustomWebSecurityExpressionRoot(Authentication auth, FilterInvocation fi) {
super(auth, fi);
}
// in here you must define all of the methods you are going to invoke in #PreAuthorize
// for example if you have an expression with #PreAuthorize('isBoyan(John)')
// then you must have the following method defined here:
public boolean isBoyan(String value) {
//your logic goes in here
return "Boyan".equalsIgnoreCase(value);
}
}
If you want to get a reference to the ExpressionParser you can use the following method AbstractSecurityExpressionHandler.getExpressionParser(). It is accessible through CustomWebSecurityExpressionHandler. Also you can take a look at its API if you want to do something more specific.
I hope this answers you question.

cxf out interceptor - reference to response pojo object

I need to get the response Java object that is returned from my service for some processing of the data. I don't want to write the code to process this data in the ServiceImpl class itself since I want to keep it configuration based. I have written the out interceptor.
As per answer to this question , the POJO object should be available in the out interceptor, however I see that the object is actually an intermediate class of response. I get a ClassCastException with the code mentioned in above link.
Am I missing something? Can the same POJO object returned by Service class be available in the Out interceptor?
Any other approach to accomplish this is also welcome.
MyOutInterceptor.java:
public class MyOutInterceptor extends AbstractPhaseInterceptor<Message> {
public MyOutInterceptor() {
super(Phase.MARSHAL); // Tried Phase.PRE_LOGICAL as well
}
public void handleMessage(Message message) throws Fault {
MessageContentsList objs = MessageContentsList.getContentsList(message);
if (objs != null && objs.size() == 1) {
Object responseObj = objs.get(0);
MyData data = (MyData) responseObj; // fails here with ClassCastException
...
}
applicationContext.xml
<bean class="com.xyz.interceptor.MyOutInterceptor" id="outInterceptor" />
<jaxws:endpoint id="dataService" implementor="#masterDataService" address="/MasterDataService">
...
<jaxws:outInterceptors>
<ref bean="outInterceptor" />
</jaxws:outInterceptors>
</jaxws:endpoint>
Pre-logical phase will work, but you would need to do an:
addBefore(WrapperClassOutInterceptor.class.getName());
to make sure it's run before that interceptor.

How do I use Spring/JUnit to verify a controller is not sending me to a non-existent view?

I'm using Spring 3.1.1.RELEASE and JUnit 4.11. I setup my JUnit tests like so
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-context.xml" })
public class MySpringTest
{
protected MockHttpServletRequest request;
protected MockHttpServletResponse response;
protected MockHttpSession session;
#Autowired
protected RequestMappingHandlerAdapter handlerAdapter;
#Autowired
protected RequestMappingHandlerMapping handlerMapping;
When testing controllers, I have this line to verify that the view the controller's method is returning is the right view …
import static org.springframework.test.web.ModelAndViewAssert.assertViewName;
...
final ModelAndView mav = submitMyForm(…);
assertViewName(mav, "folder/myView");
...
protected ModelAndView submitMyForm(… params ...) throws Exception {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
request.setRequestURI("/myurl");
request.setMethod("POST");
request.addParameter("param1", param1);
...
final Object handler = handlerMapping.getHandler(request).getHandler();
return handlerAdapter.handle(request, response, handler);
}
My question is, once I verify the view returned my the controller is the expected view, how do I verify it won't result in a 404? The main problem I'm gaving now is testing whether or not the view actually maps to an underlying page in my WAR file.
why don't use spring-mvc-test and do something like this ?
#Autowired
private ViewResolver viewResolver;
// code
View view = viewResolver.resolveViewName(viewName, locale);
//assert view not null
or something like this, in wich you can check both if the view is ok and the returned status (is status 200/404?)
(more code here: http://goo.gl/fMqBsl)
#Test
public void indexTest() throws Exception {
mockMvc.perform(get("/")).andDo(print())
.andExpect(handler().handlerType(MainController.class))
.andExpect(handler().methodName("index"))
.andExpect(view().name("index"))
.andExpect(forwardedUrl("/WEB-INF/tiles/template.jsp"))
.andExpect(status().isOk());
}
i am using standard jsp view
basically, you need to know the view resolver(s). can a specific view be resolved? that means, if you DON'T have a file called abc.xml, it might still be a valid view.
for simplicity sake, lets assume that we have only one view resolver, and, its
"org.springframework.web.servlet.view.UrlBasedViewResolver"
and here is the bean definition
spring 3.2.4 documentation pdf, page 477
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
eg: the view name "page1" => /WEB-INF/jsp/page1.jsp and "admin/page2" => /WEB-INF/jsp/admin/page2.jsp
using this, you can Inject the view resolved to your junit test using #Autowired and/or #Qualifier
then read the "prefix" and suffix value and find the full path like "src/main/webapp/" + prefix + viewname + suffix
and check if the file exists.
you may have multiple view resolvers, so you may want to inject the context and handle the view => filename resolution using a strategy pattern.
something like
foreach resolver
{
if i can resolve the view to a file (resolver type, viewname)
return the physical filename
else
try next resolver
}

How to forward to a specific annotated handler from a spring interceptor?

I wrote an Spring request interceptor for authentication purposes, it extends the HandlerInterceptorAdapter. I've set it with this line in my servlet-context:
<mvc:interceptors>
<bean class = "it.jsoftware.jacciseweb.controllers.AuthInterceptor">
<property name="manServ" ref = "acciseService"></property>
</bean>
</mvc:interceptors>
and the pre handle method is
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
HttpSession sess = request.getSession();
String path = request.getPathTranslated();
boolean autenticated = maincont.isAuthenticated(sess);
if (!autenticated){
response.sendRedirect("accise?action=login");
return false;
}
return super.preHandle(request, response, handler);
}
like this anyway it will generate a redirect loop, because it will never reach the redirect page due to the interception and redirect.
There is many ways to solve this, but I don't know how to achieve them:
Detect the url of the request (but I don't know how) and don't check for authentication for the login page. Moreover I'd like to make this solution more flexible.
Select the login handler directly on the controller. How do I do that? Is it possible?
I've seen that in examples people specifies interceptor mapping using org.springframework.web.servlet.handler.SimpleUrlHandlerMapping, anyway I'm using annotations. Is there a way, using annotations, to specify a different mapping for the interceptor so that it doesn't fire with the above address (accise?action=login)? Or maybe to chain different mapping schemes?
Is there a specific reason for not using spring-security?
IMHO is simple, powerful and deeply tested.
You can simply implement and inject your custom authenticator, spring-security will handle the redirect.

Keeping request parameters on Spring SimpleFormController with Validator

I hope I'll be able to explain this properly. I'm developing a portlet for Liferay by using Spring. It's a pinboard system. So I have a view (Jsp) which shows the detail of a certain pinboard entry, given its id. Furthermore there is a link which goes to an AddCommentController for adding a new comment to the pinboard entry the user is currently watching at. The AddCommentController extends Spring's SimpleFormController and has also a validator attached to it:
<bean id="addCommentController" class="com.lifepin.controllers.AddCommentController" parent="lifePinControllerTemplate">
<property name="formView" value="addComment" />
<property name="successView" value="viewEntryDetail" />
<property name="validator" ref="commentValidator"/>
</bean>
The validator is really simple and looks as follows:
public class CommentValidator implements Validator {
public boolean supports(Class clazz) {
return clazz.equals(Comment.class);
}
public void validate(Object obj, Errors validationError) {
ValidationUtils.rejectIfEmptyOrWhitespace(validationError, "content", "err.content.empty", "This value is required");
}
}
Now the view where the user can enter his comment has two buttons, Save and cancel. Here are the two generators for the according urls.
<portlet:actionURL var="actionUrl">
<portlet:param name="action" value="addComment"/>
<portlet:param name="pinboardEntryId" value="${param.pinboardEntryId}"/>
</portlet:actionURL>
<portlet:renderURL var="cancelUrl">
<portlet:param name="action" value="viewPinboardEntry"/>
<portlet:param name="pinboardEntryId" value="${param.pinboardEntryId}"/>
</portlet:renderURL>
In the onSubmitAction of the AddCommentController I read out the parameter (see the 1st actionURL above) and pass it to the ActionResponse s.t. in the detail view of the pinboard entry I can again load the entry and display it.
public class AddCommentController extends SimpleFormController{
...
#Override
protected void onSubmitAction(ActionRequest request, ActionResponse response, Object command, BindException bindException)
throws Exception {
long pinboardEntryId = PortletRequestUtils.getLongParameter(request, ParameterNameConstants.PINBOARDENTRY_ID, -1);
...
}
...
}
This all works fine, except when a validation error occurs. In that case I loose the "pinboardEntryId" parameter from the URL, and I don't have any way to read that parameter in the CommentValidator to pass it to the response again since I don't have any PortletRequest or response.
For now I solved this problem by storing the id on the session and by retrieving it from there. I wanted to ask however if some of you has an alternative solution without having to use the session. I'm quite sure there is one.
Thanks,
Juri
An even easier solution is to set the renderParameters property. The renderParameters property is an array of parameter names that SimpleFormController will always forward. For example:
<bean id="addCommentController" class="...">
....
<property name="renderParameters">
<list>
<value>pinboardEntryId</value>
</list>
</property>
</bean>
This will cause the 'pinboardEntryId' parameter to be passed every time without any additional code.
On validation errors showForm(..) gets called again. You could overwrite this method and manipulate Request and Response as you like.
I first tried the showForm(..) approach suggested by Oliver Gierke (thanks for the feedback) but that didn't work out as expected. The showForm(..) wants to return a new ModelAndView which I don't want to care since that should be done by the onSubmitAction(...).
The right approach is to override the
#Override
protected void processFormSubmission(ActionRequest request, ActionResponse response, Object command, BindException errors){
...
}
There, all the needed information is available. I can check now the BindingException whether there have been validation errors by using errors.hasErrors(). If that's the case, I can read the needed parameters and forward them to the response to have them on the form again. Otherwise I just call the onSubmitAction(..), passing the needed parameters such as the request, reponse etc.. which I have available in the processFormSubmission.

Resources