While working on various web projects many times I felt guilty for writing controllers that perform one-two rows of the code.
It must be easier way! And when I read and tried Spring WebFlow I really liked their approach for defining how to call methods on any exposed bean.
It is what I was looking for. Simple things made easy inside the xml config:
<bean-action bean="cartController" method="changeQuantities"> <method-arguments><argument expression="flowScope.viewCommand"/></method-arguments> <method-result name="viewCommand" scope="flow"/> </bean-action>
However Spring WebFlow has many aspects that would stop you from using it for entire site. Click-around navigation and SWF do not play well together.
So what I need is ability to define OGNL expression in Spring coonfig without using SWF. What looked scarry on first sight turned to be extremely easy task.
I did some prototyping of idea and here is what I got in spring config file:
<bean id="profileView" class="com.cochlear.mcc.web.GenericFormController"> <property name="formView" value="profileView"/> <property name="manager"><ref bean="profileManager"/></property> <property name="viewScript"><value>manager.getProfile()</value></property> </bean> <bean id="profileForm" class="com.cochlear.mcc.web.GenericFormController"> <property name="formView" value="profileForm"/> <property name="manager"><ref bean="profileManager"/></property> <property name="modelClass"><value>com.cochlear.mcc.profile.model.ProfileDTO</value></property> <property name="viewScript"><value>manager.getProfile()</value></property> <property name="postScript"><value>manager.updateProfile(#object)</value></property> <property name="successView" value="redirect:profile.view"/> </bean>
Not as clear as in SWF maybe but enough to prove the idea. It is concise, it does the job. You do not have to write FormActions for trivial things.
Here is what I have in GenericFormController:
public class GenericFormController extends SimpleFormController{ protected Object manager; protected String viewScript; protected String postScript; protected Object formBackingObject(HttpServletRequest request) throws ServletException, InstantiationException, IllegalAccessException { try { if(StringUtils.hasText(viewScript)){ OgnlContext context= new OgnlContext(); populateOgnlContextCommon(context, request); Object expr = Ognl.parseExpression(viewScript); Object res=Ognl.getValue(expr, context, this); return res; }else{ return getCommandClass().newInstance(); } } catch (OgnlException e) { e.printStackTrace(); throw new ServletException(e); } } protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object commandObject, BindException errors) throws Exception { Object command = getCommandClass().cast(commandObject); try { if(StringUtils.hasText(postScript)){ OgnlContext context= new OgnlContext(); context.put("command", command); populateOgnlContextCommon(context, request); Object expr = Ognl.parseExpression(postScript); Ognl.getValue(expr, context, this); }else{ throw new ServletException("No postScript specified"); } } catch (Exception e) { errors.reject(null, "Error:"+e.getMessage()); return showForm(request, errors, getFormView()); } return new ModelAndView(getSuccessView()); } protected void populateOgnlContextCommon(OgnlContext context, HttpServletRequest request){ context.put("request", request); context.put("session", request.getSession()); context.put("user", SecurityContextHolder.getContext().getAuthentication().getPrincipal()); } // Getters and Setters here .. // ... }
When I have time I will do something more production grade. For now I just glad to know that it is achievable and quite simple actualy.