Best way to dynamically load an action controller?

Say all requests go through a front controller. The front controller delegates that request to a particular action controller. The name of the controller, and the name of the action comes from the URL. What's the cleanest way to get an instance of the action controller based on its name, then run a method in it, alo based on its name. Example:

You've got action controllers for two different areas of your application, "forum" and "store". Each of these controllers will deal with different requests, but the front controller is the component which determines the class and the method to invoke.

The controllers have a basic skeleton layout like so:

org.yoursite.controller.actions.ForumActions;

publicclass ForumActionsextends ActionController{

publicvoid executeCreatePost(){

//deal with the "CreatePost" action

}

publicvoid executeDeleteThread(){

//you get the idea

}

}

org.yoursite.controller.actions.StoreActions;

publicclass StoreActionsextends ActionController{

publicvoid executeViewCart(){

//

}

publicvoid executeAddItem(){

//

}

}

You make a request to http://site.tld/servlet/FrontController?module=store&action=viewCart

The FrontController has enough information to know what you want to do:

StoreController ctl =new StoreController();

ctl.executeViewCart();

But what's the cleanest way to actually do that locating of the class, instantiating it and then running the correct method? Reflection? Can anyone give an example of how relfection can be used? I've got this but it seems to to sporadically throw "MethodInvokationException" errors if the servlet container has not been used for a while, then it starts working again after the first rquest has erred.

My code so far... not too happy with it:

/**

* Get an instance of the action controller for this request.

* @return ActionController

*/

public ActionController getActionController()throws ActionNotFoundException{

if (actionController ==null){

try{

Class actionClass = Class.forName("org.w3style.controllers.actions." + getModuleName() +"Actions");

actionController = (ActionController) actionClass.newInstance();

}catch (Exception e){

thrownew ActionNotFoundException("Action controller class for " + getModuleName() +" cannot be found." + e);

}

}

return actionController;

}

/**

* Run the requested action from the cotroller.

*/

protectedvoid doExecuteAction()throws ActionNotFoundException{

ActionController ac = getActionController();

try{

Class acClass = ac.getClass();

Class[] params ={};

Object[] args ={};

Method acMethod = acClass.getMethod("execute" + getActionName(), params);

acMethod.invoke(ac, args);

}catch (Exception e){

thrownew ActionNotFoundException("Action " + getActionName() +" cannot be found." + e);

}

}

[5265 byte] By [d11wtqa] at [2007-11-27 10:00:18]
# 1

Hmmm, I think my struggle to handle this indicated a design smell so I've refactored to something more modular. Now all "ActionController" classes extend an abstract so that their vital method signatures are already known. Now I have:

org.w3style.controller.actions.forum.CreatePostAction

public class CreatePostAction extends ActionController {

public void execute() {

//

}

}

org.w3style.controller.actions.forum.DeleteThreadAction

public class DeleteThreadAction extends ActionController {

public void execute() {

//

}

}

This works much better and it's still laid out well enough for components of the same module to be easily accessible. It's fixed my MethodInvocationTargetException too. I guess I can throw in validate() and handleError() methods now too :)

d11wtqa at 2007-7-13 0:31:36 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...