Announcement Announcement Module
Collapse
No announcement yet.
Turbine style controller Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Turbine style controller

    Hi,

    At my job, Turbine is used as framework and my collegue likes to call actions and views like it is done in Turbine.

    So I got the idea to make a controller in Spring MVC that does this.

    I have included the source of this controller below.

    In a url that maps to this controller you can define an action and a view. If an action is defined you also have to define a method of that action.

    A view does not need to be defined, because the action can define a view in the method, however if the method does not do this, a view needs to be defined in the url if the method returns a ModelAndView object while this ModelAndView object does not have a view. If the method returns null, the view defined in the url will be used, if this view is not present, it is assumed that the method itself writes to the httpResponse object.

    For example:

    <a href="#springUrl("/vam?action=TestAction3&method=signOffAction")">sig n off</a>

    A view defined in the action overrules a view defined in the url.
    It is also possible to just define a view in the url, without defining an action and a method, like:

    <a href="#springUrl("/vam?view=main")">back</a>

    I think using this controller for calling actions has pros and cons. Looking at a link you know what action and method will be called and you do not have to look in a configuration file for finding a mapping.
    On the other hand if for example a method name changes, you have to change the name of the method in all the velocity or jsp files that link to this method and action.

    The action class can be defined as a bean but this is not necessary. If the class is not defined as a bean, a package needs to be defined as a property of the controller. This package will be used for finding the class. If a bean is used the id of the bean needs to be the same as the action defined in the url.

    A method can have four arguments of type: HttpServletRequest, HttpServletResponse, ServletContext, WebApplicationContext,
    but can also have less or even zero arguments.

    This is the code for the controller:

    Code:
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    import javax.servlet.ServletContext;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.View;
    import org.springframework.web.servlet.mvc.AbstractController;
    import org.springframework.web.servlet.view.AbstractUrlBasedView;
    
    import exceptions.ViewActionMethodException;
    
    public class ViewActionMethodController extends AbstractController {
    
    	private String actionPath;
    
    	public String getActionPath() {
    		return actionPath;
    	}
    
    	public void setActionPath(String actionPath) {
    		this.actionPath = actionPath;
    	}
    
    	protected ModelAndView handleRequestInternal(HttpServletRequest request,
    			HttpServletResponse response) throws ViewActionMethodException {
    		ServletContext servletContext = getServletContext();
    		String actionString = request.getParameter("action");
    		String viewString = request.getParameter("view");
    		String methodString = request.getParameter("method");
    
    		ModelAndView mav = null;
    		String viewInMav = null;
    		Class actionClass = null;
    		Object actionObject = null;
    		String url = null;
    		View view = null;
    		boolean parameterStatus = true;
    		
    		boolean asBean = true;
    		if ((actionString != null) && (!actionString.equals(""))) {
    			try {
    			WebApplicationContext webContext = getWebApplicationContext();
    			actionObject = webContext.getBean(actionString);
    			} catch(NoSuchBeanDefinitionException ex) {
    				asBean = false;
    			}
    			if (asBean) {
    			actionClass = actionObject.getClass();
    			} else {
    			try {
    				actionClass = Class.forName(actionPath + actionString);
    			} catch (ClassNotFoundException ex) {
    				throw (new ViewActionMethodException(actionPath + actionString
    						+ " Class not found and no bean with id = " + actionString + " found."));
    				}
    			}
    
    			// method code
    			if ((methodString != null) && (!methodString.equals(""))) {
    				Method[] methods = actionClass.getMethods();
    
    				Method calledMethod = null;
    				for (int i = 0; i < methods.length; i++) {
    					Method method = methods[i];
    					if (methodString.equals(method.getName())) {
    						calledMethod = method;
    						break;
    					}
    				}
    				if (calledMethod == null) {
    					if (asBean) {
    					throw (new ViewActionMethodException(
    							"No method found in the class defined in the bean with id: " 
    									+ actionClass.getSimpleName()
    									+ " with the name: " + methodString));
    					} else {
    						throw (new ViewActionMethodException(
    								"No method found in the class: " + getActionPath()
    										+ actionClass.getSimpleName()
    										+ " with the name: " + methodString));
    					}
    				}
    				if (calledMethod.getReturnType().equals(ModelAndView.class)) {
    					Class[] params = calledMethod.getParameterTypes();
    
    					Object[] paramObjects = new Object[params.length];
    					if (paramObjects.length > 4 ) {
    						parameterStatus = false;
    					} else {
    						int count = 0;
    						boolean instanceStatus = false;
    						for (int j = 0; j < params.length; j++) {
    							if (params[j].equals(HttpServletRequest.class)) {
    								paramObjects[j] = request;
    								instanceStatus = true;
    							}
    							if (params[j].equals(HttpServletResponse.class)) {
    								paramObjects[j] = response;
    								instanceStatus = true;
    							}
    							if (params[j].equals(ServletContext.class)) {
    								paramObjects[j] = servletContext;
    								instanceStatus = true;
    							}
    							if (params[j].equals(WebApplicationContext.class)) {
    								paramObjects[j] = getWebApplicationContext();
    								instanceStatus = true;
    							}
    							if (!instanceStatus) {
    								parameterStatus = false;
    							}
    							instanceStatus = false;
    						}
    					}
    					if (!parameterStatus) {
    						if (asBean) {
    							throw (new ViewActionMethodException(
    									"The " + methodString
    											+ " method of the class defined in the bean with id: "
    											+ actionClass.getSimpleName() + " contains parameters of the wrong type " +
    											"It can only have upto 4 parameters of type " +
    											"HttpServletRequest, HttpServletResponse, ServletContext, WebApplicationContext." ));
    						} else {
    							throw (new ViewActionMethodException(
    									"The " + methodString
    											+ " method of the class: "
    											+ getActionPath()
    											+ actionClass.getSimpleName()+ " contains parameters of the wrong type " +
    											"It can only have upto 4 parameters of type " +
    							"HttpServletRequest, HttpServletResponse, ServletContext, WebApplicationContext." ));
    						}
    					}
    
    					try {
    						if ((actionObject != null)) {
    							mav = (ModelAndView) calledMethod.invoke(actionObject, paramObjects);
    						} else {
    						mav = (ModelAndView) calledMethod.invoke(actionClass
    								.newInstance(), paramObjects);
    						}
    					} catch (Exception ex) {
    						ex.printStackTrace();
    						if (asBean) {
    							throw (new ViewActionMethodException(
    									"Problems while invoking " + methodString
    											+ " method of the class defined in the bean with id: "
    											+ actionClass.getSimpleName()));
    						} else {
    							throw (new ViewActionMethodException(
    									"Problems while invoking " + methodString
    											+ " method of the class: "
    											+ getActionPath()
    											+ actionClass.getSimpleName()));
    						}
    					}
    				} else {
    					if (asBean) {
    						throw (new ViewActionMethodException(methodString
    								+ " method of the class defined in the bean with id: "
    								+ actionClass.getSimpleName()
    								+ " does not return an object of type ModelAndView"));
    						} else {
    						throw (new ViewActionMethodException(methodString
    								+ " method of the class: " + getActionPath()
    								+ actionClass.getSimpleName()
    								+ " does not return an object of type ModelAndView"));
    						}
    					}
    
    				if (mav != null) {
    					viewInMav = mav.getViewName();
    					
    					view = mav.getView();
    					if (view != null && (mav.getView() instanceof AbstractUrlBasedView)) {
    						AbstractUrlBasedView abstractUrlBasedView = (AbstractUrlBasedView) mav.getView();
    						url = abstractUrlBasedView.getUrl();
    					}
    
    					if ((viewInMav == null) && (url == null)) {
    						if ((viewString != null) && (!viewString.equals(""))) {
    							mav.setViewName(viewString);
    						} else {
    							throw (new ViewActionMethodException(
    									"No view defined in url or ModelAndView object returned from action method"));
    						}
    
    					}
    				} else {
    					if ((viewString != null) && (!viewString.equals(""))) {
    						mav = new ModelAndView();
    						mav.setViewName(viewString);
    					} /*
    						 * action can generate the response by writing to
    						 * HttpResponse object so a view is not necessary when
    						 * mav = null but a view is necessary when returning a
    						 * mav from the action
    						 * 
    						 */
    				}
    			} // no method specified
    
    		} // no action specified
    		else {
    
    			if ((viewString != null) && (!viewString.equals(""))) {
    				mav = new ModelAndView();
    				mav.setViewName(viewString);
    			} else {
    				throw (new ViewActionMethodException(
    						"No action and view specified"));
    			}
    		}
    		return mav;
    	}
    Exception code

    Code:
    package exceptions;
    
    public class ViewActionMethodException  extends Exception {
    	public ViewActionMethodException(String message) {
    		super(message);
    	}
    	public ViewActionMethodException() {
    		
    	}
    }

    Bean definition of controller:

    Code:
    <bean id="viewActionMethodController"
    		class="controller.ViewActionMethodController">
    		<property name="actionPath">
    			<value>module.actions.</value>
    		</property>
    	</bean>
    The actionPath defines the package where the controller will look for the actions in case no bean is found.
    Last edited by Roland; Nov 19th, 2005, 05:27 AM.

  • #2
    Turbine style controller part2

    The controller can be mapped in the following way:

    Code:
    <bean id="simpleUrlMappingViewAndActionController"
    		class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    		<property name="urlMap">
    			<map>
    				<entry key="/vam">
    					<ref bean="viewActionMethodController"></ref>
    				</entry>
    			</map>
    		</property>
    </bean>
    Dealing with a ViewActionMethodException

    Code:
    <bean id="exceptionResolver"
    		class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    
    		<property name="exceptionMappings">
    			<props>
    				<prop key="ViewActionMethodException">vamErrorPage</prop>
    			</props>
    		</property>
    		<property name="exceptionAttribute">
    			<value>viewActionMethodException</value>
    		</property>
    		<property name="defaultErrorView">
    			<value>defaultErrorPage</value>
    		</property>
    </bean>
    vamErrorPage (velocity)

    Code:
    <html>
    <head><title>Error</title></head>
    <body>
    <p>The following error occurred:</p>
    <p>$viewActionMethodException.message</p>
    <a href="#springUrl("/vam?view=main")">back</a>
    </body>
    </html>
    Web.xml part:
    Code:
    <servlet>
    		<servlet-name>applicationServlet</servlet-name>
    		<servlet-class>
    			org.springframework.web.servlet.DispatcherServlet
    		</servlet-class>
    		<load-on-startup>2</load-on-startup>
    	</servlet>
    
    		<servlet-mapping>
    		<servlet-name>applicationServlet</servlet-name>
    		<url-pattern>/vam</url-pattern>
    	</servlet-mapping>


    An example for calling the controller using velocity is:
    <a href="#springUrl("/vam?action=TestAction3&method=signOffAction")">sig n off</a>

    This will call the method signOffAction() of the class defined as the bean with id: TestAction3 or of the class TestAction3 in case no bean with id TestAction3 is defined.

    Code:
    package module.actions;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.view.RedirectView;
    
    public class TestAction3 {
    
    	public ModelAndView signOffAction(HttpServletRequest request, HttpServletResponse response) throws Exception {	
    		HttpSession session = request.getSession();
    		session.invalidate();
    		return new ModelAndView(new RedirectView("/login.htm", true));
    	}
    }
    Bean definition of action

    Code:
    <bean id="TestAction3" class="module.actions.TestAction3" />

    Comment

    Working...
    X