What's the recommended way to implement generic functionality on spring MVC? - spring

Let's say I want to do the same thing the Masterpage's code behind does on ASP.NET side. I'm currently learning Spring MVC and Im using JSP for my views. I know for the JSP side, everytime I create a page I include header.jsp and footer.jsp.
Lets say I have this var in my header.jsp ${ItemsQty} I would have to tho this in all controllers request mappings to get the value inserted.
model.addAttribute("ItemsQty", ItemsServices.count());
What's the correct way to set this value? adding the attribute in all controllers, all request methods?
Regards.

You could create an interceptor that adds the attribute to the modelmap. Interceptors can be mapped to any URL you like.
<mvc:annotation-driven>
<mvc:interceptors>
<mvc:mapping path="/items/**" />
<bean="my.package.items.ItemsInterceptor"/>
</mvc:interceptors>
</mvc:annotation-driven>
When the url matches mapping /items/** this interceptor will add the attribute to the modelmap after the handler is called.
class ItemsInterceptor extends HandlerInterceptorAdapter {
#Autowired
private ItemsServices service;
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView)
throws Exception {
if (modelAndView != null) {
modelAndView.addObject("ItemsQty", service.count());
}
}
}

What's the correct way to set this value? adding the attribute in all controllers, all request methods?
No. These attributes are session scoped attributes. Session scoped attributes are specified in Spring MVC using #SessionAttributes. So in your case it would be
#SessionAttributes({"ItemsQty"})
So the first time you add "ItemsQty" to the model, it will stay there (across multiple requests) until SessionStatus.setComplete() is called.

Related

Spring form commandName = session attribute

I have many pages with the same form where I bind my object using commandName. I try to put the object to session with name "myObject" and use it for form (commandName = "myObject"). But system throws exception (Neither BindingResult nor plain target object for bean name "myObject").
How can I bind the object to session for any controllers and requests?
That error is typical when your use a form:form tag that targets a command object that is not available in the request.
A good approach is to combine #ModelAttribute annotated method with #SessionAttributes on a Controller that intially forwards to the view that contains the form, something like
#Controller
#SessionAttributes("myObject")
public class FormController {
#ModelAttribute("myObject")
public MyObject createMyObjectBean() {
return new MyObject();
}
...
}
The initally createMyObjectBean will be called with whatever is the first request to the controller methods, but it won't be called on subsequent request as the myObject value will come from session due to #SessionAttributes
just note that for this approach to work, you must have a controller that forwards to the view that contains your form

Difference between #ModelAttribute and HttpServletRequest Attribute

I use spring mvc and I want to undestand some stuff.
At this code:
#RequestMapping(value="/enregistrerLostCard")
public #ResponseBody
void enregistrerLostCard(#ModelAttribute(value="auth") Auth auth2, HttpServletRequest request) {
Auth auth1 = (Auth) request.getAttribute("auth");
System.out.println(auth2.getLogin()); //print the right value
System.out.println(auth1.getLogin()); //i got nullpointer exception
}
#ModelAttribute(value="auth") and request.getAttribute("auth") isn't the same ?
HttpServletRequest is a Servlet container managed object. Its attribute store holds attributes that are useful in any part of the request handling by the Servlet container.
Model, ModelMap, ModelAndView, etc. are managed by Spring MVC (the DispatcherServlet stack). The attributes inside those are useful to the Spring side of the application.
In some cases, the Model attributes will be inserted into the HttpServletRequest attributes if needed. This typically happens when your handler method returns a String value as a view name. The model attributes will be pushed as HttpServletRequest attributes so that they can be used in the view, for example, in jsps.
Related:
how to pass variables from java to jsp in Spring

How to configure which controllers Spring #ControllerAdvice will be applied to?

I have two types of controllers in my spring application.
View controllers that forward to views to generate HTML
API controllers that return JSON directly from the controllers
Both the API and View controllers are part of the same spring dispatcher servlet. Spring 3.2 introduced the #ControllerAdvice annotation to allow for a global location to handle exception.
The documentation implies that #ControllerAdvice will be applied to every controller associated with a Dispatcher Servlet.
Is there a way to configure which controllers #ControllerAdvice will apply to?
For example in my scenario I want a #ControllerAdvice for my View Controllers and separate #ControllerAdvice for my API controllers.
For people that will still find this question:
As from Spring 4 ControlerAdvice's can be limited to Controler's with the specified annotations. Take a look at:
http://blog.codeleak.pl/2013/11/controlleradvice-improvements-in-spring.html
(second half of this article) for more details.
UPDATE
I am using spring 4. You can do one of 2 below options.
(1) You can add the packages you want. (Inside those packages you have controllers that you want to follow #ControllerAdvice).
Ex:
#ControllerAdvice(basePackages={"my.pkg.a", "my.pkg.b"})
(2) You can directly add the controller classes you want.
Ex:
#ControllerAdvice(basePackageClasses={MyControllerA.class, MyControllerB.class})
I do not think this is possible now. If you can make the API and View controllers throw different Exception types, then you could define two different #ExceptionHandlers and achieve what you want.
// For handling API Exceptions
#ExceptionHandler(APIException.class) // Single API Exception
#ExceptionHandler({APIException.class, ..., ,,,}) // Multiple API Exceptions
// For handling View Exceptions
#ExceptionHandler(ViewException.class) // Single View Exception
#ExceptionHandler({ViewException.class, ..., ...}) // Multiple View Exceptions
You could use aop to translate the Exceptions coming out of APIs to a standard APIException. See this thread on spring forums.
Hope it helps.
Your exceptions should not dictate the content-type of your response. Instead check the request's Accept header for what the browser expects.
#ExceptionHandler(Throwable.class)
public #ResponseBody String handleThrowable(HttpServletRequest request, HttpServletResponse response, Throwable ex) throws IOException {
...
String header = request.getHeader("Accept");
if(supportsJsonResponse(header)) {
//return response as JSON
response.setContentType(JSON_MEDIA_TYPE.toString());
return Json.stringify(responseMap);
} else {
//return as HTML
response.setContentType("text/html");
}
#ExceptionHandler(value=Exception.class)
public ModelAndView error(Exception ex) {
return new ModelAndView("redirect:/error/m");
}
...//ErrorController
#RequestMapping(value = "/m", produces="text/html")
public ModelAndView error()...
#RequestMapping(value = "/m", produces="application/json")
#ResponseBody
public Map errorJson()...

Struts2 and Spring integration thread safe

We are using Struts2-Spring integration and all my action classes implement SessionAware, sample code to action class and their spring definition is given below,
public class IRXxxxAction extends ActionSupport implements SessionAware {
private Map session;
public String execute()
{//}
public void setSession(Map<String, Object> session)
{
this.session = session;
}
}
Spring Configuration
<bean name="userAction" class="com.IRXxxxAction" >
<property name="adminDAO" ref="adminDAO" />
</bean>
If I understand correctly, each auto-wired property will be a singleton, so if the above is true, is there anyway that the session map get shared between two simultaneous requests?
Regards,
Ayush
You have asked same question on the user mailing list and as said if you are using Struts2-Spring plugin make sure to put bean scope as prototype.
Struts2 create new instance of action on each request, since action work as a model also and in order to make it thread safe a new object is being created on each request and placed on value stack.
Not proving scope will be treated by Spring as singleton and for ever request same action instance will be given back which can leads to a lot of issue from data corruption to weird behavior.

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.

Resources