package com.aggrepoint.winlet;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.aggrepoint.utils.StringUtils;
import com.aggrepoint.winlet.form.Form;
import com.aggrepoint.winlet.form.FormImpl;
import com.aggrepoint.winlet.spring.WinletRequestWrapper;
import com.aggrepoint.winlet.spring.def.ReturnDef;
import com.aggrepoint.winlet.spring.def.WinletDef;
import com.aggrepoint.winlet.utils.BufferedResponse;
/**
*
* @author Jiangming Yang (yangjm@gmail.com)
*/
public class ReqInfoImpl implements ReqConst, ReqInfo {
private static long REQUEST_ID = 0;
private long requestId;
private HttpServletRequest request;
// 遇到当执行了一定逻辑后,request.getSession(true)会不定期返回null
private HttpSession session;
private String requestPath;
private String path;
private String pageId;
private String pageUrl;
private String remoteDomain;
private String actionId;
private String validateFieldName;
private String validateFieldValue;
private String validateFieldId;
private boolean pageRefresh;
private boolean firstInclude;
private Form form;
private PageStorage ps;
private SharedPageStorage sps;
private ReturnDef rd;
private WinletDef winletDef;
private Object winlet;
private Method winletMethod;
private boolean noPreload;
private boolean isFromContainer;
/** 搜索引擎用_escaped_fargment_参数请求时为true */
private boolean hashEscaped;
/** 当hashEscaped = true时:hash中的参数 */
private HashMap<String, HashMap<String, String>> hashParams;
/** 当hashEscaped = true时:hash中顶层页面的URL */
private String topPageUrl;
/** 当hashEscaped = true时:hash中顶层页面的参数 */
private HashMap<String, String> topPageParams;
public ReqInfoImpl(HttpServletRequest request, String path) {
this.request = request;
this.session = request.getSession(true);
this.path = request.getContextPath() + path;
// 将IncludeTag需要获得的当前Winlet的RequestPath取出保存
// 发现如果在WinletDispatcherServlet中用HttpServletRequestWrapper的派生类将request封装,请求forward到JSP页面后
// ,用ReqInfoImpl中保存的request对象的getRequestURI()方法只能获得当前JSP的URI而不是期望的Winlet
// URI。如果不封装,或者封装类不从HttpServletRequestWrapper派生,则ReqInfoImpl中request.getRequestURI()可以正常工作。为了避免这个问题,这里直接将requestURI取出保存。
requestPath = request.getRequestURI();
int idx = requestPath.indexOf("/", 1);
if (idx > 0)
requestPath = requestPath.substring(idx);
requestId = REQUEST_ID++;
noPreload = getParameter(PARAM_NO_PRELOAD, null) != null;
isFromContainer = "y".equalsIgnoreCase(getParameter(
PARAM_REQ_FROM_CONTAINER, ""));
pageId = getParameter(PARAM_PAGE_PATH, null);
if (pageId == null)
pageId = request.getRequestURI();
pageUrl = getParameter(PARAM_PAGE_URL, null);
if (pageUrl == null)
pageUrl = request.getRequestURL().toString();
actionId = getParameter(PARAM_WIN_ACTION, null);
if (actionId == null) {
pageRefresh = "yes".equalsIgnoreCase(getParameter(
PARAM_PAGE_REFRESH, ""));
}
remoteDomain = getParameter(PARAM_WINLET_DOMAIN_NAME, null);
firstInclude = "yes".equalsIgnoreCase(getParameter(PARAM_FIRST_INCLUDE,
""));
validateFieldName = getParameter(PARAM_WIN_VALIDATE_FIELD, null);
if (validateFieldName != null) {
validateFieldValue = getParameter(PARAM_WIN_VALIDATE_FIELD_VALUE,
"");
validateFieldId = getParameter(PARAM_WIN_VALIDATE_FIELD_ID, "");
}
form = new FormImpl(this);
parseEscapedHash(getParameter(PARAM_ESCAPED_FRAGMENT, null));
ContextUtils.setReqInfo(this);
}
private HashMap<String, String> getHashParams(String group,
boolean createIfNotExist) {
if (hashParams == null) {
if (!createIfNotExist)
return null;
hashParams = new HashMap<String, HashMap<String, String>>();
}
if (!hashParams.containsKey(group)) {
if (!createIfNotExist)
return null;
hashParams.put(group, new HashMap<String, String>());
}
return hashParams.get(group);
}
// 普通winlet参数的名称,例如:3[page]
static final Pattern P_GROUP_PARAM = Pattern
.compile("^(\\w)\\[([^\\]]+)\\]$");
// 页面url,例如_p[1][u]
static final Pattern P_PAGE_URL = Pattern
.compile("^_p\\[(\\d+)\\]\\[u\\]$");
// 页面参数,例如_p[1][p][programId]
static final Pattern P_PAGE_PARAMETER = Pattern
.compile("^_p\\[(\\d+)\\]\\[p\\]\\[([^\\]]+)\\]$");
/**
* Example of hash value:
*
* <pre>
* type=10&_p%5B0%5D%5Bu%5D=11&_p%5B0%5D%5Bp%5D%5BprogramId%5D=14402&addr=Toronto
* 3%5Bpage%5D=3&type=10&addr=L4C+9H5
* </pre>
*
* @param hash
* @return
* @throws UnsupportedEncodingException
*/
private boolean parseEscapedHash(String hash) {
if (StringUtils.isEmpty(hash))
return false;
hashEscaped = true;
int maxPage = -1;
try {
for (String str : hash.split("&")) {
int idx = str.indexOf("=");
if (idx <= 0)
continue;
String name = URLDecoder.decode(str.substring(0, idx), "UTF-8");
String value = URLDecoder.decode(str.substring(idx + 1),
"UTF-8");
if (name.indexOf("[") == -1) {
getHashParams("root", true).put(name, value);
continue;
}
Matcher m = P_GROUP_PARAM.matcher(name);
if (m.find()) {
getHashParams(m.group(1), true).put(m.group(2), value);
continue;
}
m = P_PAGE_URL.matcher(name);
if (m.find()) {
int page = Integer.parseInt(m.group(1));
if (page > maxPage)
topPageParams = null;
if (page >= maxPage) {
maxPage = page;
topPageUrl = value;
continue;
}
}
m = P_PAGE_PARAMETER.matcher(name);
if (m.find()) {
int page = Integer.parseInt(m.group(1));
if (page > maxPage) {
topPageUrl = null;
topPageParams = null;
}
if (page >= maxPage) {
if (topPageParams == null)
topPageParams = new HashMap<String, String>();
topPageParams.put(m.group(2), value);
continue;
}
}
// 不支持的参数,忽略不处理
System.err.println("Unsupported escaped hash parameter name: "
+ name + ", value: " + value);
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
@Override
public String getParameter(String name, String def) {
String str;
str = request.getParameter(name);
if (str == null)
return def;
return str.trim();
}
@Override
public int getParameter(String name, int def) {
return Integer.parseInt(getParameter(name, Integer.toString(def)));
}
@Override
public long getParameter(String name, long def) {
return Long.parseLong(getParameter(name, Long.toString(def)));
}
public void setReturnDef(ReturnDef rd) {
this.rd = rd;
}
@Override
public HttpServletRequest getRequest() {
return request;
}
@Override
public HttpSession getSession() {
HttpSession s = request.getSession();
if (s != null)
session = s;
return session;
}
@Override
public HttpSession getSession(boolean create) {
HttpSession s = request.getSession(create);
if (s != null)
session = s;
return session;
}
@Override
public UserProfile getUser() {
return ContextUtils.getUser(request);
}
@Override
public String getPath() {
return path;
}
@Override
public long getRequestId() {
return requestId;
}
@Override
public String getPageId() {
return pageId;
}
@Override
public String getPageUrl() {
return pageUrl;
}
@Override
public String getRemoteDomain() {
return remoteDomain;
}
@Override
public boolean isCrossDomain() {
return StringUtils.notEmpty(remoteDomain);
}
@Override
public String getActionId() {
return actionId;
}
@Override
public Form getForm() {
return form;
}
@Override
public boolean isValidateField() {
return validateFieldName != null;
}
@Override
public String getValidateFieldName() {
return validateFieldName;
}
@Override
public String getValidateFieldValue() {
return validateFieldValue;
}
@Override
public String getValidateFieldId() {
return validateFieldId;
}
@Override
public boolean isPageRefresh() {
return pageRefresh;
}
@Override
public boolean isFirstInclude() {
return firstInclude;
}
@Override
public PageStorage getPageStorage() {
if (ps == null)
ps = new PageStorageImpl(this);
return ps;
}
@Override
public SharedPageStorage getSharedPageStorage() {
if (sps == null)
sps = new SharedPageStorageImpl(this);
return sps;
}
@Override
public ReturnDef getReturnDef() {
return rd;
}
@Override
public String getWindowUrl(WinletDef winletDef, String window) {
String requestPath = null;
if (window == null) { // 执行完action后获取window的内容时不指定window参数
requestPath = this.requestPath;
} else {
if (winletDef == null) // 执行当前winlet的其他window方法时不用指定winletDef参数
requestPath = "/" + this.winletDef.getName();
else
requestPath = "/" + winletDef.getName();
requestPath = requestPath + "/" + window;
}
return requestPath;
}
public static String updateScriptWinletReference(Long wid, String str) {
if (wid != null) {
str = str.replaceAll("win\\$\\.post\\s*\\(", "win\\$._post(" + wid
+ ", null, ");
str = str.replaceAll("win\\$\\.embed\\s*\\(", "win\\$._post(" + wid
+ ", ");
str = str.replaceAll("win\\$\\.include\\s*\\(", "win\\$._include("
+ wid + ", ");
str = str.replaceAll("win\\$\\.winlet\\s*\\(", "win\\$._winlet("
+ wid);
str = str.replaceAll("win\\$\\.ajax\\s*\\(", "win\\$._ajax(" + wid
+ ", ");
str = str.replaceAll("win\\$\\.get\\s*\\(", "win\\$._get(" + wid
+ ", ");
str = str.replaceAll("win\\$\\.toggle\\s*\\(", "win\\$._toggle("
+ wid + ", ");
str = str.replaceAll("win\\$\\.url\\s*\\(", "win\\$._url(" + wid
+ ", ");
str = str.replaceAll("win\\$\\.submit\\s*\\(", "win\\$._submit("
+ wid + ", ");
str = str.replaceAll("win\\$\\.find\\s*\\(", "win\\$._find(" + wid
+ ", ");
str = str.replaceAll("win\\$\\.wait\\s*\\(", "win\\$._wait(" + wid
+ ", ");
str = str.replaceAll("win\\$\\.aftersubmit\\s*\\(",
"win\\$._aftersubmit(" + wid + ", ");
}
return str;
}
@Override
public String getWindowContent(Long wid, String windowUrl,
Map<String, String> params, Map<String, Object> attributes,
Consumer<StaticUrlProvider> returnProvider) throws Exception {
LogInfoImpl log = ContextUtils.getLogInfo(request);
HashMap<String, String> reqParams = new HashMap<String, String>();
if (params != null) // 执行完action后获取window的内容时不指定params参数
reqParams.putAll(params);
reqParams.put(PARAM_WIN_ACTION, null);
if (windowUrl == null)
windowUrl = this.requestPath;
BufferedResponse response = new BufferedResponse();
// 注:
// 这里使用forward而不是include,因为使用include的情况下在被include对象
// 中使用getRequestURI()等方法获得的是当前的URI而不是被include对象的URI,
// 因此ADK无法正确判断被include的资源。使用forward则不存在这个问题。因为已经
// 使用了responseWrapper,因此用forward也是可行的。
try {
WinletRequestWrapper wrapper = new WinletRequestWrapper(request,
null, reqParams, attributes);
// 避免被包含的功能改变当前LogInfo
ContextUtils.setLogInfo(request, null);
request.getServletContext().getRequestDispatcher(windowUrl)
.forward(wrapper, response);
if (returnProvider != null)
returnProvider.accept((StaticUrlProvider) wrapper
.getAttribute(StaticUrlProvider.REQ_ATTR_KEY));
} finally {
// 恢复当前请求的ReqInfo
ContextUtils.setReqInfo(this);
// 恢复当前请求的LogInfo
ContextUtils.setLogInfo(request, log);
}
byte[] bytes = response.getBuffered();
String str = bytes == null ? "" : new String(bytes, "UTF-8");
return updateScriptWinletReference(wid, str);
}
@Override
public Object getWinlet() {
return winlet;
}
@Override
public void setWinletMethod(Method winletMethod) {
this.winletMethod = winletMethod;
}
@Override
public Method getWinletMethod() {
return winletMethod;
}
@Override
public void setWinlet(WinletDef def, Object winlet) {
this.winletDef = def;
this.winlet = winlet;
}
@Override
public WinletDef getWinletDef() {
return winletDef;
}
@Override
public boolean noPreload() {
return noPreload;
}
@Override
public boolean isFromContainer() {
return isFromContainer;
}
public boolean isHashEscaped() {
return hashEscaped;
}
public HashMap<String, String> getHashParams(String group) {
if (group == null || hashParams == null
|| !hashParams.containsKey(group))
return null;
return hashParams.get(group);
}
public String getTopPageUrl() {
return topPageUrl;
}
public HashMap<String, String> getTopPageParams() {
return topPageParams;
}
@Override
public String getContextPath() {
return request.getContextPath();
}
@Override
public boolean isWinInclude() {
return request instanceof WinletRequestWrapper;
}
}