/*
* Copyright 2008-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.hasor.web.invoker;
import net.hasor.core.convert.ConverterUtils;
import net.hasor.core.future.BasicFuture;
import net.hasor.core.utils.BeanUtils;
import net.hasor.core.utils.StringUtils;
import net.hasor.web.*;
import net.hasor.web.annotation.*;
import net.hasor.web.definition.AbstractDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.AsyncContext;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 负责解析参数并执行调用。
* @version : 2014年8月27日
* @author 赵永春(zyc@hasor.net)
*/
class InvokerCaller implements ExceuteCaller {
protected Logger logger = LoggerFactory.getLogger(getClass());
private InMapping mappingToDefine = null;
private AbstractDefinition[] filterArrays = null;
private WebPluginCaller pluginCaller = null;
private Map<String, List<String>> queryParamLocal = null;
private Map<String, Object> pathParamsLocal = null;
//
public InvokerCaller(InMapping mappingToDefine, AbstractDefinition[] filterArrays, WebPluginCaller pluginCaller) {
this.mappingToDefine = mappingToDefine;
this.filterArrays = (filterArrays == null) ? new AbstractDefinition[0] : filterArrays;
this.pluginCaller = pluginCaller;
}
/**
* 调用目标
* @throws Throwable 异常抛出
*/
public Future<Object> invoke(final Invoker invoker, final FilterChain chain) throws Throwable {
final BasicFuture<Object> future = new BasicFuture<Object>();
Method targetMethod = this.mappingToDefine.findMethod(invoker);
if (targetMethod == null) {
chain.doFilter(invoker.getHttpRequest(), invoker.getHttpResponse());
future.completed(null);
return future;
}
//
// .异步调用
boolean needAsync = this.mappingToDefine.isAsync(invoker);
ServletVersion version = invoker.getAppContext().getInstance(ServletVersion.class);
if (version.ge(ServletVersion.V3_0) && needAsync) {
// .必须满足: Servlet3.x、环境支持异步Servlet、目标开启了Servlet3
try {
AsyncContext asyncContext = invoker.getHttpRequest().startAsync();
asyncContext.start(new AsyncInvocationWorker(asyncContext, targetMethod) {
public void doWork(Method targetMethod) throws Throwable {
try {
Object invoke = invoke(targetMethod, invoker);
future.completed(invoke);
} catch (Throwable e) {
future.failed(e);
}
}
});
return future;
} catch (Throwable e) { /* 不支持异步 */ }
}
//
// .同步调用
try {
Object invoke = invoke(targetMethod, invoker);
future.completed(invoke);
} catch (Throwable e) {
future.failed(e);
}
return future;
}
/** 执行调用 */
private Object invoke(final Method targetMethod, Invoker invoker) throws Throwable {
//
// .初始化WebController
final Object targetObject = this.mappingToDefine.newInstance(invoker);
if (targetObject != null && targetObject instanceof Controller) {
((Controller) targetObject).initController(invoker);
}
//
// .准备过滤器链
final ArrayList<Object[]> resolveParams = new ArrayList<Object[]>(1);
InvokerChain invokerChain = new InvokerChain() {
@Override
public void doNext(Invoker invoker) throws Throwable {
Object result = targetMethod.invoke(targetObject, resolveParams.get(0));
invoker.put(Invoker.RETURN_DATA_KEY, result);
}
};
InvokerData invokerData = new InvokerData() {
@Override
public Method targetMethod() {
return targetMethod;
}
@Override
public Object[] getParameters() {
return resolveParams.isEmpty() ? new Object[0] : resolveParams.get(0);
}
@Override
public MappingData getMappingTo() {
return mappingToDefine;
}
};
//
// .执行Filters
try {
final Object[] resolveParamsArrays = this.resolveParams(invoker, targetMethod);
resolveParams.add(0, resolveParamsArrays);
//
this.pluginCaller.beforeFilter(invoker, invokerData);
new InvokerChainInvocation(this.filterArrays, invokerChain).doNext(invoker);
} finally {
this.pluginCaller.afterFilter(invoker, invokerData);
}
//
return invoker.get(Invoker.RETURN_DATA_KEY);
}
//
/**/
private Object[] resolveParams(Invoker invoker, Method targetMethod) throws Throwable {
//
Class<?>[] targetParamClass = targetMethod.getParameterTypes();
Annotation[][] targetParamAnno = targetMethod.getParameterAnnotations();
targetParamClass = (targetParamClass == null) ? new Class<?>[0] : targetParamClass;
targetParamAnno = (targetParamAnno == null) ? new Annotation[0][0] : targetParamAnno;
ArrayList<Object> paramsArray = new ArrayList<Object>();
/*准备参数*/
for (int i = 0; i < targetParamClass.length; i++) {
Class<?> paramClass = targetParamClass[i];
Object paramObject = this.resolveParam(invoker, paramClass, targetParamAnno[i]);//获取参数
paramsArray.add(paramObject);
}
Object[] invokeParams = paramsArray.toArray();
return invokeParams;
}
private Object resolveParam(Invoker invoker, Class<?> paramClass, Annotation[] paramAnno) {
// .特殊类型参数
Object specialParam = resolveSpecialParam(invoker, paramClass);
if (specialParam != null) {
return specialParam;
}
// .注解解析
for (Annotation pAnno : paramAnno) {
Object finalValue = resolveParam(invoker, paramClass, pAnno);
finalValue = ConverterUtils.convert(paramClass, finalValue);
if (finalValue != null) {
return finalValue;
}
}
return BeanUtils.getDefaultValue(paramClass);
}
private Object resolveSpecialParam(Invoker invoker, Class<?> paramClass) {
if (!paramClass.isInterface()) {
return null;
}
if (paramClass == ServletRequest.class || paramClass == HttpServletRequest.class) {
return invoker.getHttpRequest();
}
if (paramClass == ServletResponse.class || paramClass == HttpServletResponse.class) {
return invoker.getHttpResponse();
}
if (paramClass == HttpSession.class) {
return invoker.getHttpRequest().getSession(true);
}
//
if (paramClass == Invoker.class) {
return invoker;
}
if (paramClass.isInterface() && paramClass.isInstance(invoker)) {
return invoker;
}
//
return invoker.getAppContext().getInstance(paramClass);
}
private Object resolveParam(Invoker invoker, Class<?> paramClass, Annotation pAnno) {
Object atData = null;
//
if (pAnno instanceof AttributeParam) {
atData = this.getAttributeParam(invoker, (AttributeParam) pAnno);
} else if (pAnno instanceof CookieParam) {
atData = this.getCookieParam(invoker, (CookieParam) pAnno);
} else if (pAnno instanceof HeaderParam) {
atData = this.getHeaderParam(invoker, (HeaderParam) pAnno);
} else if (pAnno instanceof QueryParam) {
atData = this.getQueryParam(invoker, (QueryParam) pAnno);
} else if (pAnno instanceof PathParam) {
atData = this.getPathParam(invoker, (PathParam) pAnno);
} else if (pAnno instanceof ReqParam) {
atData = invoker.getHttpRequest().getParameterValues(((ReqParam) pAnno).value());
} else if (pAnno instanceof Params) {
atData = this.getParamsParam(invoker, paramClass);
}
//
return atData;
}
/**/
private Object getParamsParam(Invoker invoker, Class<?> paramClass) {
Object paramObject = null;
try {
paramObject = paramClass.newInstance();
} catch (Throwable e) {
logger.error(paramClass.getName() + "newInstance error.", e.getMessage());
return paramObject;
}
List<Field> fieldList = BeanUtils.findALLFields(paramClass);
if (fieldList == null || fieldList.isEmpty()) {
return paramObject;
}
for (Field field : fieldList) {
if (field.isAnnotationPresent(IgnoreParam.class)) {
if (logger.isDebugEnabled()) {
logger.debug(field + " -> Ignore.");
}
continue;
}
try {
Object fieldValue = null;
Annotation[] annos = field.getAnnotations();
if (annos == null || annos.length == 0) {
fieldValue = invoker.getHttpRequest().getParameterValues(field.getName());
} else {
fieldValue = resolveParam(invoker, field.getType(), annos);
}
if (fieldValue == null) {
fieldValue = BeanUtils.getDefaultValue(field.getType());
}
fieldValue = ConverterUtils.convert(field.getType(), fieldValue);
field.setAccessible(true);
field.set(paramObject, fieldValue);
} catch (Exception e) {
logger.error(field + "set new Value error.", e.getMessage());
}
}
return paramObject;
}
/**/
private Object getPathParam(Invoker invoker, PathParam pAnno) {
String paramName = pAnno.value();
return StringUtils.isBlank(paramName) ? null : this.getPathParamMap(invoker).get(paramName);
}
/**/
private Object getQueryParam(Invoker invoker, QueryParam pAnno) {
String paramName = pAnno.value();
return StringUtils.isBlank(paramName) ? null : this.getQueryParamMap(invoker).get(paramName);
}
/**/
private Object getHeaderParam(Invoker invoker, HeaderParam pAnno) {
String paramName = pAnno.value();
if (StringUtils.isBlank(paramName)) {
return null;
}
//
HttpServletRequest httpRequest = invoker.getHttpRequest();
Enumeration<?> e = httpRequest.getHeaderNames();
while (e.hasMoreElements()) {
String name = e.nextElement().toString();
if (name.equalsIgnoreCase(paramName)) {
ArrayList<Object> headerList = new ArrayList<Object>();
Enumeration<?> v = httpRequest.getHeaders(paramName);
while (v.hasMoreElements()) {
headerList.add(v.nextElement());
}
return headerList;
}
}
return null;
}
/**/
private Object getCookieParam(Invoker invoker, CookieParam pAnno) {
String paramName = pAnno.value();
if (StringUtils.isBlank(paramName)) {
return null;
}
//
HttpServletRequest httpRequest = invoker.getHttpRequest();
Cookie[] cookies = httpRequest.getCookies();
ArrayList<String> cookieList = new ArrayList<String>();
if (cookies != null) {
for (Cookie cookie : cookies) {
String cookieName = cookie.getName();
if (cookieName == null) {
continue;
}
if (cookieName.equalsIgnoreCase(paramName)) {
cookieList.add(cookie.getValue());
}
}
}
return cookieList;
}
/**/
private Object getAttributeParam(Invoker invoker, AttributeParam pAnno) {
String paramName = pAnno.value();
if (StringUtils.isBlank(paramName)) {
return null;
}
HttpServletRequest httpRequest = invoker.getHttpRequest();
Enumeration<?> e = httpRequest.getAttributeNames();
while (e.hasMoreElements()) {
String name = e.nextElement().toString();
if (name.equalsIgnoreCase(paramName)) {
return httpRequest.getAttribute(paramName);
}
}
return null;
}
/**/
private Map<String, List<String>> getQueryParamMap(Invoker invoker) {
if (this.queryParamLocal != null) {
return this.queryParamLocal;
}
//
HttpServletRequest httpRequest = invoker.getHttpRequest();
String queryString = httpRequest.getQueryString();
if (StringUtils.isBlank(queryString)) {
return Collections.EMPTY_MAP;
}
//
this.queryParamLocal = new HashMap<String, List<String>>();
String[] params = queryString.split("&");
for (String pData : params) {
String oriData = pData;
String encoding = httpRequest.getCharacterEncoding();
try {
if (encoding != null) {
oriData = URLDecoder.decode(pData, encoding);
}
} catch (Exception e) {
logger.warn("use ‘{}’ decode ‘{}’ error.", encoding, pData);
continue;
}
String[] kv = oriData.split("=");
if (kv.length < 2) {
continue;
}
String k = kv[0].trim();
String v = kv[1];
//
List<String> pArray = this.queryParamLocal.get(k);
pArray = pArray == null ? new ArrayList<String>() : pArray;
if (!pArray.contains(v)) {
pArray.add(v);
}
this.queryParamLocal.put(k, pArray);
}
return this.queryParamLocal;
}
/**/
private Map<String, Object> getPathParamMap(Invoker invoker) {
if (this.pathParamsLocal != null) {
return this.pathParamsLocal;
}
//
HttpServletRequest httpRequest = invoker.getHttpRequest();
String requestPath = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
String matchVar = this.mappingToDefine.getMappingToMatches();
String matchKey = "(?:\\{(\\w+)\\}){1,}";// (?:\{(\w+)\}){1,}
Matcher keyM = Pattern.compile(matchKey).matcher(this.mappingToDefine.getMappingTo());
Matcher varM = Pattern.compile(matchVar).matcher(requestPath);
ArrayList<String> keyArray = new ArrayList<String>();
ArrayList<String> varArray = new ArrayList<String>();
while (keyM.find()) {
keyArray.add(keyM.group(1));
}
varM.find();
for (int i = 1; i <= varM.groupCount(); i++) {
varArray.add(varM.group(i));
}
//
Map<String, List<String>> uriParams = new HashMap<String, List<String>>();
for (int i = 0; i < keyArray.size(); i++) {
String k = keyArray.get(i);
String v = varArray.get(i);
List<String> pArray = uriParams.get(k);
pArray = pArray == null ? new ArrayList<String>() : pArray;
if (!pArray.contains(v)) {
pArray.add(v);
}
uriParams.put(k, pArray);
}
this.pathParamsLocal = new HashMap<String, Object>();
for (Entry<String, List<String>> ent : uriParams.entrySet()) {
String k = ent.getKey();
List<String> v = ent.getValue();
this.pathParamsLocal.put(k, v.toArray(new String[v.size()]));
}
return this.pathParamsLocal;
}
}