Package jodd.madvoc

Source Code of jodd.madvoc.ActionRequest

// Copyright (c) 2003-2014, Jodd Team (jodd.org). All Rights Reserved.

package jodd.madvoc;

import jodd.madvoc.component.MadvocController;
import jodd.madvoc.filter.ActionFilter;
import jodd.madvoc.injector.Target;
import jodd.madvoc.interceptor.ActionInterceptor;
import jodd.exception.ExceptionUtil;
import jodd.madvoc.meta.Out;
import jodd.madvoc.result.Result;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;


/**
* Encapsulates single action invocation and acts as an action proxy.
* It invokes all assigned action interceptors during action invocation and
* specifies the result after action method invocation.
*/
public class ActionRequest {

  protected final MadvocController madvocController;
  protected final ActionConfig actionConfig;
  protected final String actionPath;
  protected HttpServletRequest servletRequest;
  protected HttpServletResponse servletResponse;
  protected Result result;

  protected final Target[] targets;
  protected final int totalInterceptors;
  protected int interceptorIndex;
  protected int filterIndex;
  protected int totalFilters;

  protected int execState;    // execution state

  protected Object action;
  protected Object actionResult;

  protected String nextActionPath;
  protected ActionRequest previousActionRequest;

  // ---------------------------------------------------------------- accessors

  /**
   * Returns servlet request.
   */
  public HttpServletRequest getHttpServletRequest() {
    return servletRequest;
  }

  /**
   * Specifies new servlet request, in case of wrapping it.
   */
  public void setHttpServletRequest(HttpServletRequest request) {
    this.servletRequest = request;
  }

  /**
   * Returns servlet response.
   */
  public HttpServletResponse getHttpServletResponse() {
    return servletResponse;
  }

  /**
   * Specifies new servlet response, in case of wrapping it.
   */
  public void setHttpServletResponse(HttpServletResponse response) {
    this.servletResponse = response;
  }

  /**
   * Returns {@link ActionConfig action configuration}.
   */
  public ActionConfig getActionConfig() {
    return actionConfig;
  }

  /**
   * Returns action object.
   */
  public Object getAction() {
    return action;
  }

  /**
   * Returns action path.
   */
  public String getActionPath() {
    return actionPath;
  }

  /**
   * Returns next request string for action chaining.
   */
  public String getNextActionPath() {
    return nextActionPath;
  }

  /**
   * Specifies the next action path, that will be chained to current action request.
   */
  public void setNextActionPath(String nextActionPath) {
    this.nextActionPath = nextActionPath;
  }

  /**
   * Returns previous action request in chain, if there was one.
   */
  public ActionRequest getPreviousActionRequest() {
    return previousActionRequest;
  }

  /**
   * Sets previous action request in chain.
   */
  public void setPreviousActionRequest(ActionRequest previousActionRequest) {
    this.previousActionRequest = previousActionRequest;
  }

  /**
   * Returns result object if exist in action, otherwise returns <code>null</code>.
   */
  public Result getResult() {
    return result;
  }

  /**
   * Returns all injection targets.
   */
  public Target[] getTargets() {
    return targets;
  }

  /**
   * Returns action result object.
   */
  public Object getActionResult() {
    return actionResult;
  }

  /**
   * Sets action result object.
   */
  public void setActionResult(Object actionResult) {
    this.actionResult = actionResult;
  }

  // ---------------------------------------------------------------- ctor

  /**
   * Creates new action request and initializes it.
   */
  public ActionRequest(
      MadvocController madvocController,
      String actionPath,
      ActionConfig actionConfig,
      Object action,
      HttpServletRequest servletRequest,
      HttpServletResponse servletResponse) {

    this.madvocController = madvocController;
    this.actionPath = actionPath;
    this.actionConfig = actionConfig;
    this.servletRequest = servletRequest;
    this.servletResponse = servletResponse;
    totalInterceptors = (this.actionConfig.interceptors != null ? this.actionConfig.interceptors.length : 0);
    interceptorIndex = 0;
    totalFilters = (this.actionConfig.filters != null ? this.actionConfig.filters.length : 0);
    filterIndex = 0;
    execState = 0;
    this.action = action;
    this.result = findResult();
    this.targets = makeTargets();
  }

  /**
   * Returns result field value if such exist. If field exists
   * and it's value is <code>null</code> it will be created.
   */
  protected Result findResult() {
    Field resultField = actionConfig.resultField;
    if (resultField != null) {
      try {
        Result result = (Result) resultField.get(action);

        if (result == null) {
          result = (Result) resultField.getType().newInstance();
          resultField.set(action, result);
        }

        return result;
      } catch (Exception ignore) {
        return null;
      }
    }
    return null;
  }

  /**
   * Joins action and parameters into one array of Targets.
   */
  protected Target[] makeTargets() {
    if (actionConfig.hasArguments == false) {
      return new Target[] {new Target(action)};
    }

    ActionConfig.MethodParam[] methodParams = actionConfig.getMethodParams();
    Target[] target = new Target[methodParams.length + 1];

    target[0] = new Target(action);

    for (int i = 0; i < methodParams.length; i++) {
      ActionConfig.MethodParam mp = methodParams[i];

      Class type = mp.getType();

      Target t;

      if (mp.getAnnotationType() == null) {
        // parameter is NOT annotated
        t = new Target(createActionMethodArgument(type));
      }
      else if (mp.getAnnotationType() == Out.class) {
        // parameter is annotated with *only* OUT annotation
        // we need to create the output AND to save the type
        t = new Target(createActionMethodArgument(type), type);
      }
      else {
        // parameter is annotated with any IN annotation
        t = new Target(type) {
          @Override
          protected void createValueInstance() {
            value = createActionMethodArgument(type);
          }
        };
      }

      target[i + 1] = t;
    }
    return target;
  }

  /**
   * Creates action method arguments.
   */
  protected Object createActionMethodArgument(Class type) {
    try {
      if (type.getEnclosingClass() == null || Modifier.isStatic(type.getModifiers())) {
        // regular or static class
        Constructor ctor = type.getDeclaredConstructor(null);
        ctor.setAccessible(true);
        return ctor.newInstance();
      } else {
        // member class
        Constructor ctor = type.getDeclaredConstructor(type.getDeclaringClass());
        ctor.setAccessible(true);
        return ctor.newInstance(action);
      }
    } catch (Exception ex) {
      throw new MadvocException(ex);
    }
  }

  // ---------------------------------------------------------------- invoke

  /**
   * Invokes the action and returns action result value object.
   * Invokes all interceptors before and after action invocation.
   */
  public Object invoke() throws Exception {
    if (execState >= 2) {
      throw new MadvocException("Action already invoked: " + actionConfig.actionPath);
    }

    if (execState == 0) {
      // filters
      if (filterIndex < totalFilters) {
        ActionFilter filter = actionConfig.filters[filterIndex];
        filterIndex++;
        return filter.invoke(this);
      }
    }

    execState = 1;

    Object actionResult;
    try {
      actionResult = invokeAction();
    } catch (Exception ex) {
      interceptorIndex--;
      throw ex;
    }

    if (execState == 2) {
      // all interceptor finished the job, action was invoked
      if (interceptorIndex > 0) {
        interceptorIndex--;
      } else {
        madvocController.render(this, actionResult);
        execState = 3;
      }
    } else if (execState == 1) {
      // some interceptor interrupted the flow, action was not invoked
      if (interceptorIndex > 1) {
        interceptorIndex--;
      } else {
        madvocController.render(this, actionResult);
        execState = 3;
      }
    }

    return actionResult;
  }

  /**
   * Invokes all {@link jodd.madvoc.interceptor.ActionInterceptor action interceptors}
   * and the action method, returns action result object.
   */
  protected Object invokeAction() throws Exception {
    // interceptors
    if (interceptorIndex < totalInterceptors) {
      ActionInterceptor interceptor = actionConfig.interceptors[interceptorIndex];
      interceptorIndex++;
      return interceptor.invoke(this);
    }

    // action
    execState = 2;

    return invokeActionMethod();
  }

  /**
   * Invokes action method after starting all interceptors.
   * After method invocation, all interceptors will finish, in opposite order.
   */
  protected Object invokeActionMethod() throws Exception {
    Object[] params = extractParametersFromTargets();
    try {
      return actionConfig.actionClassMethod.invoke(action, params);
    } catch(InvocationTargetException itex) {
      throw ExceptionUtil.extractTargetException(itex);
    }
  }

  /**
   * Collects all parameters from target into an array.
   */
  protected Object[] extractParametersFromTargets() {
    if (targets == null) {
      return null;
    }

    Object[] values = new Object[targets.length - 1];

    for (int i = 1; i < targets.length; i++) {
      values[i - 1] = targets[i].getValue();
    }

    return values;
  }

}
TOP

Related Classes of jodd.madvoc.ActionRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.