package org.eweb4j.mvc.action;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import org.eweb4j.cache.ActionConfigBeanCache;
import org.eweb4j.cache.SingleBeanCache;
import org.eweb4j.config.LogFactory;
import org.eweb4j.ioc.IOC;
import org.eweb4j.mvc.Context;
import org.eweb4j.mvc.MIMEType;
import org.eweb4j.mvc.ParamUtil;
import org.eweb4j.mvc.action.annotation.DateFormat;
import org.eweb4j.mvc.action.annotation.Ioc;
import org.eweb4j.mvc.action.annotation.Singleton;
import org.eweb4j.mvc.config.ActionClassCache;
import org.eweb4j.mvc.config.MVCConfigConstant;
import org.eweb4j.mvc.config.bean.ActionConfigBean;
import org.eweb4j.mvc.config.bean.ResultConfigBean;
import org.eweb4j.mvc.interceptor.InterExecution;
import org.eweb4j.mvc.validator.ValidateExecution;
import org.eweb4j.util.ClassUtil;
import org.eweb4j.util.JsonConverter;
import org.eweb4j.util.ReflectUtil;
import org.eweb4j.util.StringUtil;
/**
* Action 执行器
*
* @author weiwei
* @since 1.b.8
*
*/
public class ActionExecution {
private Context context;
private Object actionObject;
private Class<?> clazz;
private Object retn;
private ReflectUtil ru;
// 验证器
private boolean handleValidator() throws Exception {
Map<String, String> error = ValidateExecution.checkValidate(context
.getMvcBean().getValidator(), context.getQueryParamMap(),
context.getRequest());
if (error != null && !error.isEmpty()) {
ValidateExecution.showValidateError(context.getMvcBean()
.getShowValErrorType(), error, context.getRequest(),
context.getResponse());
StringBuilder sb = new StringBuilder();
sb.append("MVC:验证器发现了错误").append(error);
LogFactory.getMVCLogger("INFO").write(sb.toString());
return false;
}
return true;
}
public ActionExecution(String uri, String httpMethod, Context context) {
this.context = context;
this.context.setUri(uri);
this.context.setHttpMethod(httpMethod);
this.context.setQueryParamMap(new HashMap<String, String[]>());
this.context.setPathParamMap(new HashMap<String, String[]>());
}
public boolean findAction() throws Exception {
// URL参数
Map<String, List<?>> pathParams = null;
if (ActionConfigBeanCache.containsKey(this.context.getUri())
|| (pathParams = ActionConfigBeanCache.getByMatches(
this.context.getUri(), this.context.getHttpMethod())) != null) {
// 处理形如" /xxx/{id}/{name} "的URI
if (pathParams != null && pathParams.containsKey("mvcBean")) {
// 根据Url配置的UrlParam获取参数值
this.context.setMvcBean((ActionConfigBean) pathParams.get(
"mvcBean").get(0));
this.context.getPathParamMap().putAll(
ParamUtil.getPathParamMap(pathParams));
this.context.getQueryParamMap().putAll(
this.context.getPathParamMap());
} else
this.context.setMvcBean(ActionConfigBeanCache.get(this.context
.getUri()));
// 将request的请求参数转到另外一个map中去
this.context.getQueryParamMap().putAll(
ParamUtil.copyReqParams(this.context.getRequest()));
if (this.context.getMvcBean() != null)
return true;
}
return false;
}
private Object initPojo() throws Exception {
clazz = ActionClassCache.get(this.context.getMvcBean().getClazz());
Annotation singletonAnn = clazz.getAnnotation(Singleton.class);
if (singletonAnn != null) {
this.actionObject = SingleBeanCache.get(clazz);
if (this.actionObject == null) {
this.actionObject = clazz.newInstance();
SingleBeanCache.add(clazz, this.actionObject);
}
} else
this.actionObject = clazz.newInstance();
ru = new ReflectUtil(this.actionObject);
return this.actionObject;
}
// IOC,注入对象到pojo
private void injectIocBean() throws Exception {
Field[] fields = ru.getFields();
if (fields == null)
return;
for (Field f : fields) {
Class<?> type = f.getType();
Ioc ioc = f.getAnnotation(Ioc.class);
if (ioc == null)
continue;
String beanId = "";
if (ioc.value().trim().length() == 0)
beanId = type.getSimpleName();
else
beanId = StringUtil.parsePropValue(ioc.value());
Method setter = ru.getSetter(f.getName());
if (setter == null)
continue;
setter.invoke(this.actionObject, IOC.getBean(beanId));
}
}
private void exeActionLog() {
StringBuilder sb = new StringBuilder();
sb.append("MVC:执行")
.append(this.context.getUri() + "@"
+ this.context.getHttpMethod()).append("[Action];");
LogFactory.getMVCLogger("INFO").write(sb.toString());
}
private Object[] assemParams(Class<?>[] paramTypes, Annotation[][] paramAnns)
throws Exception {
Object[] params = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; ++i) {
Annotation[] anns = paramAnns[i];
Class<?> paramClass = paramTypes[i];
String[] paramValue = null;
// ------------------------------------------------------
// 通过给定class 获取对应的ActionContextObj
if (HttpServletRequest.class.isAssignableFrom(paramClass)) {
params[i] = this.context.getRequest();
continue;
}
if (HttpServletResponse.class.isAssignableFrom(paramClass)) {
params[i] = this.context.getResponse();
continue;
}
if (PrintWriter.class.isAssignableFrom(paramClass)) {
params[i] = this.context.getWriter();
continue;
}
if (ServletOutputStream.class.isAssignableFrom(paramClass)) {
params[i] = this.context.getOut();
continue;
}
if (HttpSession.class.isAssignableFrom(paramClass)) {
params[i] = this.context.getSession();
continue;
}
if (ActionProp.class.isAssignableFrom(paramClass)) {
if (this.context.getActionProp() == null)
this.context.setActionProp(new ActionProp(clazz.getName()));
params[i] = this.context.getActionProp();
continue;
}
if (QueryParams.class.isAssignableFrom(paramClass)) {
params[i] = this.context.getQueryParams();
continue;
}
if (ClassUtil.isPojo(paramClass)) {
params[i] = this.injectParam2Pojo(paramClass);
continue;
}
PathParam pathParamAnn = this.getPathParamAnn(anns);
if (pathParamAnn != null) {
paramValue = this.getPathParamValue(pathParamAnn);
params[i] = ClassUtil.getParamVal(paramClass, paramValue[0]);
continue;
}
QueryParam queryParamAnn = this.getQueryParamAnn(anns);
if (queryParamAnn == null)
continue;
paramValue = this.getQueryParamValue(queryParamAnn);
if (java.util.Date.class.isAssignableFrom(paramClass)) {
params[i] = this.getDateParam(anns, paramValue[0]);
continue;
}
if (paramClass.isArray())
params[i] = ClassUtil.getParamVals(paramClass, paramValue);
else
params[i] = ClassUtil.getParamVal(paramClass, paramValue[0]);
}
return params;
}
private Method getFirstMethd(Method[] methods) {
Method m = methods[0];
if (methods.length == 1)
return m;
// 如果含有两个或以上同名的方法,优先拿到被@Path注解的第一个方法
for (Method mm : methods) {
Path p = mm.getAnnotation(Path.class);
if (p == null)
continue;
m = mm;
break;
}
return m;
}
private String[] getQueryParamValue(QueryParam paramAnn) {
String[] paramValue = this.context.getQueryParamMap().get(
paramAnn.value());
return getParamValue(paramValue);
}
private String[] getPathParamValue(PathParam paramAnn) {
String[] paramValue = this.context.getPathParamMap().get(
paramAnn.value());
return getParamValue(paramValue);
}
private String[] getParamValue(String[] paramValue) {
if (paramValue == null || paramValue.length == 0
|| paramValue[0] == null) {
paramValue = new String[] { "" };
}
return paramValue;
}
private Date getDateParam(Annotation[] anns, String paramValue)
throws Exception {
DateFormat dateAnn = null;
for (Annotation a : anns) {
if (a == null)
continue;
if (!a.annotationType().isAssignableFrom(DateFormat.class))
continue;
dateAnn = (DateFormat) a;
break;
}
String pattern = "yyyy-MM-dd HH:mm:ss";
if (dateAnn != null)
pattern = dateAnn.value();
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return sdf.parse(paramValue);
}
private Object injectParam2Pojo(Class<?> paramClass) throws Exception {
Object paramObj = paramClass.newInstance();
this.injectActionCxt2Pojo(paramObj);
// 注入mvc action 请求参数
ParamUtil.injectParam(this.context.getQueryParamMap(), paramObj);
return paramObj;
}
private PathParam getPathParamAnn(Annotation[] anns) {
for (Annotation a : anns) {
if (a == null)
continue;
if (!a.annotationType().isAssignableFrom(PathParam.class))
continue;
return (PathParam) a;
}
return null;
}
private QueryParam getQueryParamAnn(Annotation[] anns) {
for (Annotation a : anns) {
if (a == null)
continue;
if (!a.annotationType().isAssignableFrom(QueryParam.class))
continue;
return (QueryParam) a;
}
return null;
}
private void handleResult() throws Exception {
this.exeActionLog();
if (retn == null)
return;
String re = null;
String mimeType = null;
List<String> produces = this.context.getMvcBean().getProduces();
if (produces != null && produces.size() > 0)
for (String produce : produces) {
mimeType = produce;
break;
}
if (!String.class.isAssignableFrom(retn.getClass())) {
if (this.context.getWriter() == null)
this.context.setWriter(this.context.getResponse().getWriter());
if (MIMEType.JSON.equals(mimeType)
|| "json".equalsIgnoreCase(mimeType)) {
this.context.getWriter().print(JsonConverter.convert(retn));
this.context.getResponse().setContentType(MIMEType.JSON);
} else {
this.context.getWriter().print("暂时不支持JSON以外的视图渲染技术");
}
this.context.getWriter().flush();
return;
}
re = String.valueOf(retn);
// String contextPath = this.context.getRequest().getContextPath();
String baseUrl = (String) this.context.getServletContext()
.getAttribute(MVCConfigConstant.BASE_URL_KEY);
List<ResultConfigBean> results = this.context.getMvcBean().getResult();
if (results == null || results.size() == 0) {
// 客户端重定向
if (re.startsWith("redirect:")) {
String url = re.substring("redirect:".length());
String location = url;
this.context.getResponse().sendRedirect(location);
} else if (re.startsWith("action:")) {
// ACTION 重定向
String path = re.substring("action:".length());
this.context.getResponse().sendRedirect(baseUrl + path);
} else if (re.startsWith("out:") || re.trim().length() == 0) {
String location = re.substring("out:".length());
this.context.getWriter().print(location);
this.context.getWriter().flush();
} else if (re.startsWith("forward:")) {
String location = re.substring("forward:".length());
HttpServletRequest request = this.context.getRequest();
request.setAttribute(MVCConfigConstant.REQ_PARAM_MAP_NAME,
this.context.getQueryParamMap());
// 服务端跳转
request.getRequestDispatcher(
MVCConfigConstant.FORWARD_BASE_PATH + location).forward(
request, this.context.getResponse());
} else {
this.context.getWriter().print(retn);
this.context.getWriter().flush();
}
} else {
for (ResultConfigBean r : results) {
if (!"_props_".equals(r.getName()) && !r.getName().equals(re)
&& !"".equals(re))
continue;
String type = r.getType();
String location = r.getLocation();
if (MVCConfigConstant.REDIRECT_TYPE.equalsIgnoreCase(type)) {
this.context.getResponse().sendRedirect(location);
} else if (MVCConfigConstant.FORWARD_TYPE
.equalsIgnoreCase(type)) {
HttpServletRequest request = this.context.getRequest();
request.setAttribute(MVCConfigConstant.REQ_PARAM_MAP_NAME,
this.context.getQueryParamMap());
// 服务端跳转
request.getRequestDispatcher(
MVCConfigConstant.FORWARD_BASE_PATH + location)
.forward(request, this.context.getResponse());
} else if (re != null && re.startsWith("action:")) {
// ACTION 重定向
String path = re.substring("action:".length());
this.context.getResponse().sendRedirect(baseUrl + path);
} else if (MVCConfigConstant.OUT_TYPE.equalsIgnoreCase(type)
|| location.trim().length() == 0) {
this.context.getWriter().print(location);
this.context.getWriter().flush();
}
}
}
}
/**
* 执行Action
*
* @param methodName
* @return
* @throws Exception
*/
private Object excuteMethod(String methodName) throws Exception {
// 要执行的Action方法
Method m = null;
// 拿到所有方法
Method[] methods = ru.getMethods(methodName);
if (methods == null || methods.length == 0)
return null;
m = this.getFirstMethd(methods);
if (m == null)
return null;
Class<?>[] paramTypes = m.getParameterTypes();
// 无参数运行方法
if (paramTypes == null || paramTypes.length == 0)
return m.invoke(actionObject);
Annotation[][] paramAnns = m.getParameterAnnotations();
// 拿到方法所需要的参数
Object[] params = assemParams(paramTypes, paramAnns);
// 带参数运行方法
return m.invoke(actionObject, params);
}
private void injectActionCxt2Pojo(Object pojo) throws Exception {
ReflectUtil ru = new ReflectUtil(pojo);
HttpServletRequest req = this.context.getRequest();
HttpServletResponse res = this.context.getResponse();
PrintWriter out = this.context.getWriter();
// ServletOutputStream sos = this.context.getOut();
HttpSession session = this.context.getSession();
ActionProp actionProp = this.context.getActionProp();
QueryParams queryParams = this.context.getQueryParams();
for (String n : ru.getFieldsName()) {
Method m = ru.getSetter(n);
if (m == null)
continue;
Class<?> clazz = m.getParameterTypes()[0];
if (HttpServletRequest.class.isAssignableFrom(clazz)) {
m.invoke(pojo, req);
} else if (HttpServletResponse.class.isAssignableFrom(clazz)) {
m.invoke(pojo, res);
} else if (PrintWriter.class.isAssignableFrom(clazz)) {
m.invoke(pojo, out);
} else if (ServletOutputStream.class.isAssignableFrom(clazz)) {
m.invoke(pojo, this.context.getOut());
} else if (HttpSession.class.isAssignableFrom(clazz)) {
m.invoke(pojo, session);
} else if (ActionProp.class.isAssignableFrom(clazz)) {
if (actionProp == null)
actionProp = new ActionProp(clazz.getName());
this.context.setActionProp(actionProp);
m.invoke(pojo, actionProp);
} else if (QueryParams.class.isAssignableFrom(clazz)) {
m.invoke(pojo, queryParams);
}
}
}
/**
* 执行Action
*
* @throws Exception
*/
public void execute() throws Exception {
// 验证参数
if (!this.handleValidator()) {
return;
}
// 实例化pojo
initPojo();
// IOC注入对象到pojo中
injectIocBean();
// 注入框架mvc action 上下文环境
this.injectActionCxt2Pojo(actionObject);
// 注入mvc action 请求参数
ParamUtil.injectParam(this.context.getQueryParamMap(), actionObject);
String methodName = this.context.getMvcBean().getMethod();
if (methodName != null && methodName.length() > 0)
// 执行方法
retn = excuteMethod(methodName);
else if (IAction.class.isAssignableFrom(clazz)) {
// struts2风格
IAction action = (IAction) actionObject;
action.init(this.context);
retn = action.execute();
}
InterExecution after_interExe = new InterExecution("after",
this.context);// 7.后置拦截器
if (after_interExe.findAndExecuteInter()) {
after_interExe.showErr();
return;
}
// 对Action执行返回结果的处理
this.handleResult();
}
}