package org.apache.struts.beanaction;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
/**
* BeanAction is an extension to the typical Struts Action class that
* <p/>
* enables mappings to bean methods. This allows for a more typical
* <p/>
* Object Oriented design where each object has behaviour as part of
* <p/>
* its definition. Instead of writing separate Actions and Forms,
* <p/>
* BeanAction allows you to simply have a Bean, which models both
* <p/>
* the state and the methods that operate on that state.
* <p/>
* <p/>
* <p/>
* In addition to the simpler packaging, BeanAction also simplifies the
* <p/>
* Struts progamming paradigm and reduces dependency on Struts. Using
* <p/>
* this pattern could allow easier migration to newer frameworks like JSF.
* <p/>
* <p/>
* <p/>
* The method signatures are greatly simplified to the following
* <p/>
*
* <pre>
* <p/>
* public String myActionMethod() {
* <p/>
* //..work
* <p/>
* return "success";
* <p/>
* }
* <p/>
* </pre>
* <p/>
* The return parameter becomes simply the name of the forward (as defined
* <p/>
* in the config file as usual). Form parameters, request, response, session,
* <p/>
* attributes, and cookies are all accessed via the ActionContext class (see the
* <p/>
* ActionContext javadocs for more).
* <p/>
* <p/>
* <p/>
* The forms that you map to a BaseAction mapping must be a subclass of the
* <p/>
* BaseBean class. BaseBean continues to simplify the validation and
* <p/>
* reset methods by removing the parameters from the signature as was done with
* <p/>
* the above action method example.
* <p/>
* <p/>
* <p/>
* There are 3 ways to map a BeanAction in the struts configuration file.
* <p/>
* They are as follows.
* <p/>
* <p/>
* <p/>
* <B>URL Pattern</B>
* <p/>
* <p/>
* <p/>
* This approach uses the end of the action definition to determine which
* <p/>
* method to call on the Bean. For example if you request the URL:
* <p/>
* <p/>
* <p/>
* http://localhost/jpetstore4/shop/viewOrder.do
* <p/>
* <p/>
* <p/>
* Then the method called would be "viewOrder" (of the mapped bean as specified
* <p/>
* by the name="" parameter in the mapping below). The mapping used for this
* <p/>
* approach is as follows.
* <p/>
*
* <pre>
* <p/>
* <action path="/shop/<b>viewOrder</b>" type="org.apache.struts.beanaction.BeanAction"
* <p/>
* name="orderBean" scope="session"
* <p/>
* validate="false">
* <p/>
* <forward name="success" path="/order/ViewOrder.jsp"/>
* <p/>
* </action>
* <p/>
* </pre>
* <p/>
* <p/>
* <p/>
* <B>Method Parameter</B>
* <p/>
* <p/>
* <p/>
* This approach uses the Struts action parameter within the mapping
* <p/>
* to determine the method to call on the Bean. For example the
* <p/>
* following action mapping would cause the "viewOrder" method to
* <p/>
* be called on the bean ("orderBean"). The mapping used for this
* <p/>
* approach is as follows.
* <p/>
*
* <pre>
* <p/>
* <action path="/shop/viewOrder" type="org.apache.struts.beanaction.BeanAction"
* <p/>
* <b>name="orderBean" parameter="viewOrder"</b> scope="session"
* <p/>
* validate="false">
* <p/>
* <forward name="success" path="/order/ViewOrder.jsp"/>
* <p/>
* </action>
* <p/>
* </pre>
* <p/>
* <B>No Method call</B>
* <p/>
* <p/>
* <p/>
* BeanAction will ignore any Struts action mappings without beans associated
* <p/>
* to them (i.e. no name="" attribute in the mapping). If you do want to associate
* <p/>
* a bean to the action mapping, but do not want a method to be called, simply
* <p/>
* set the parameter to an asterisk ("*"). The mapping used for this approach
* <p/>
* is as follows (no method will be called).
* <p/>
*
* <pre>
* <p/>
* <action path="/shop/viewOrder" type="org.apache.struts.beanaction.BeanAction"
* <p/>
* <b>name="orderBean" parameter="*"</b> scope="session"
* <p/>
* validate="false">
* <p/>
* <forward name="success" path="/order/ViewOrder.jsp"/>
* <p/>
* </action>
* <p/>
* </pre>
* <p/>
* <p/>
* <p/>
* <p/>
* <p/>
* TO-DO List
* <p/>
* <ul>
* <p/>
* <li>Ignore mappings to methods that don't exist.
* <p/>
* </ul>
* <p/>
* </p>
* <p/>
* <B>A WORK IN PROGRESS</B>
* <p/>
* <p/>
* <p/>
* <i>The BeanAction Struts extension is a work in progress. While it demonstrates
* <p/>
* good patterns for application development, the framework itself is very new and
* <p/>
* should not be considered stable. Your comments and suggestions are welcome.
* <p/>
* Please visit <a href="http://www.ibatis.com">http://www.ibatis.com</a> for contact information.</i>
* <p/>
* <p/>
* <p/>
* Date: Mar 11, 2004 10:03:56 PM
*
* @author Clinton Begin
* @see BaseBean
* @see org.apache.struts.beanaction.ActionContext
*/
public class BeanAction extends Action {
private static final String NO_METHOD_CALL = "*";
private static final String SUCCESS_FORWARD = "success";
public final ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception {
String forward = SUCCESS_FORWARD;
try {
if (!(form instanceof BaseBean)) {
if (form != null) {
throw new BeanActionException("The form for mapping '" + mapping.getPath() + "' named '"
+ mapping.getName()
+ "' was not an instance of BaseBean. BeanAction requires an BaseBean instance.");
} else {
throw new BeanActionException("The form for mapping '" + mapping.getPath() + "' named '"
+ mapping.getName() + "' was null. BeanAction requires an BaseBean instance.");
}
}
BaseBean bean = (BaseBean) form;
ActionContext.initCurrentContext(request, response);
if (bean != null) {
// Explicit Method Mapping
Method method = null;
String methodName = mapping.getParameter();
if (methodName != null && !NO_METHOD_CALL.equals(methodName)) {
try {
method = bean.getClass().getMethod(methodName, (Class[]) null);
synchronized (bean) {
forward = bean.getInterceptor().intercept(new ActionInvoker(bean, method));
}
} catch (Exception e) {
throw new BeanActionException("Error dispatching bean action via method parameter ('" + methodName
+ "'). Cause: " + e, e);
}
}
// Path Based Method Mapping
if (method == null && !NO_METHOD_CALL.equals(methodName)) {
methodName = mapping.getPath();
if (methodName.length() > 1) {
int slash = methodName.lastIndexOf("/") + 1;
methodName = methodName.substring(slash);
if (methodName.length() > 0) {
try {
method = bean.getClass().getMethod(methodName, (Class[]) null);
synchronized (bean) {
forward = bean.getInterceptor().intercept(new ActionInvoker(bean, method));
}
} catch (Exception e) {
throw new BeanActionException("Error dispatching bean action via URL pattern ('" + methodName
+ "'). Cause: " + e, e);
}
}
}
}
}
} catch (Exception e) {
forward = "error";
request.setAttribute("BeanActionException", e);
}
return mapping.findForward(forward);
}
}