package com.ketayao.fensy.mvc;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.converters.SqlDateConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ketayao.fensy.exception.ActionException;
import com.ketayao.fensy.util.Base64;
import com.ketayao.fensy.util.CryptUtils;
import com.ketayao.fensy.util.FileUtils;
import com.ketayao.fensy.util.NumberUtils;
import com.ketayao.fensy.util.RandomStringUtils;
import com.ketayao.fensy.util.ResourceUtils;
import com.ketayao.fensy.util.StringUtils;
import com.ketayao.fensy.webutil.Multimedia;
import com.ketayao.fensy.webutil.RequestUtils;
/**
* 请求上下文
*
* @date 2010-1-13 下午04:18:00
*/
public class WebContext {
private final static Logger log = LoggerFactory
.getLogger(WebContext.class);
private final static String UTF_8 = "UTF-8";
private static int maxSize = 10 * 1024 * 1024;
private final static String TEMP_UPLOAD_FILE = "$TEMP_UPLOAD_FILE$";
private final static ThreadLocal<WebContext> CONTEXTS = new ThreadLocal<WebContext>();
private ServletContext context;
private HttpSession session;
private HttpServletRequest request;
private HttpServletResponse response;
private Locale locale;
private Map<String, Cookie> cookies;
public final static byte[] E_KEY = new byte[] { '1', '2', '3',
'4', '5', '6',
'7', '8' };
public final static String LOCALE = "___locale";
public final static int MAX_AGE = 86400 * 365; // 默认一年时间
public final static String COOKIE_LOGIN = "___login";
public final static String REQUEST_LOCALE_PARAM = "request_locale";
static {
// BeanUtils对时间转换的初始化设置
ConvertUtils.register(new SqlDateConverter(null), java.sql.Date.class);
ConvertUtils.register(new Converter() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-d");
SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-M-d H:m");
@SuppressWarnings("rawtypes")
public Object convert(Class type, Object value) {
if (value == null)
return null;
if (value instanceof Date)
return (value);
try {
return sdfTime.parse(value.toString());
} catch (ParseException e) {
try {
return sdf.parse(value.toString());
} catch (ParseException e1) {
return null;
}
}
}
}, java.util.Date.class);
}
/**
* 初始化请求上下文,默认转码为UTF8
*
* @param ctx
* @param req
* @param res
*/
public static WebContext begin(ServletContext servletContext, HttpServletRequest request,
HttpServletResponse response) {
WebContext wc = new WebContext();
wc.context = servletContext;
wc.request = autoUploadRequest(encodeRequest(request));// 是否是上传请求
wc.response = response;
// 保存request_locale参数设置
wc.saveLocale();
wc.response.setCharacterEncoding(UTF_8);
wc.session = request.getSession(false); //默认不创建session
//wc.session = req.getSession();
wc.cookies = new HashMap<String, Cookie>();// 获取cookie
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie ck : cookies) {
wc.cookies.put(ck.getName(), ck);
}
}
CONTEXTS.set(wc);
return wc;
}
/**
* 删除上传临时文件,清除context上下文。
*/
public void end() {
String tmpPath = (String) request.getAttribute(TEMP_UPLOAD_FILE);
if (tmpPath != null) {
try {
FileUtils.deleteDir(new File(tmpPath));
} catch (IOException e) {
log.error("Failed to cleanup upload directory: " + tmpPath, e);
}
}
this.context = null;
this.request = null;
this.response = null;
this.session = null;
this.cookies = null;
this.locale = null;
CONTEXTS.remove();
}
/**
* 自动文件上传请求的封装
*
* @param req
* @return
*/
private static HttpServletRequest autoUploadRequest(HttpServletRequest request) {
if (isMultipart(request)) {
String path = System.getProperty("java.io.tmpdir")
+ RandomStringUtils.randomAlphanumeric(10);
File dir = new File(path);
if (!dir.exists() && !dir.isDirectory())
dir.mkdirs();
try {
request.setAttribute(TEMP_UPLOAD_FILE, dir.getCanonicalPath());
return new MultipartRequest(request, dir.getCanonicalPath(), maxSize, UTF_8);
} catch (NullPointerException e) {
} catch (IOException e) {
log.error("Failed to save upload files into temp directory: " + path, e);
}
}
return request;
}
/**
* 自动编码处理
*
* @param req
* @return
*/
private static HttpServletRequest encodeRequest(HttpServletRequest req) {
if (req instanceof RequestProxy)
return req;
HttpServletRequest autoEncodingReq = req;
if ("POST".equalsIgnoreCase(req.getMethod())) {
try {
autoEncodingReq.setCharacterEncoding(UTF_8);
} catch (UnsupportedEncodingException e) {
}
}
return autoEncodingReq;
}
/**
* @return the maxSize
*/
public static int getMaxSize() {
return maxSize;
}
/**
* @param maxSize the maxSize to set
*/
public static void setMaxSize(int maxSize) {
WebContext.maxSize = maxSize;
}
/**
* 获取当前请求的上下文
*
* @return
*/
public static WebContext get() {
return CONTEXTS.get();
}
public ServletContext getContext() {
return context;
}
public HttpServletRequest getRequest() {
return request;
}
public HttpServletResponse getResponse() {
return response;
}
public HttpSession getSession() {
return session;
}
public HttpSession getSession(boolean create) {
return (session == null && create) ? (session = request.getSession()) : session;
}
public String getQueryString() {
return request.getQueryString();
}
@SuppressWarnings("unchecked")
public Enumeration<String> getParams() {
return request.getParameterNames();
}
public String getParam(String name) {
return request.getParameter(name);
}
public String getParam(String name, String... defValue) {
String v = request.getParameter(name);
return (v != null) ? v : ((defValue.length > 0) ? defValue[0] : null);
}
public long getParam(String name, long defValue) {
return NumberUtils.toLong(getParam(name), defValue);
}
public int getParam(String name, int defValue) {
return NumberUtils.toInt(getParam(name), defValue);
}
public byte getParam(String name, byte defValue) {
return NumberUtils.toByte(getParam(name), defValue);
}
public String[] getParams(String name) {
return request.getParameterValues(name);
}
public Object getRequestAttr(String name) {
HttpServletRequest request = getRequest();
return (request != null) ? request.getAttribute(name) : null;
}
public void setRequestAttr(String key, Object value) {
request.setAttribute(key, value);
}
public Object getSessionAttr(String name) {
HttpSession ssn = getSession();
return (ssn != null) ? ssn.getAttribute(name) : null;
}
public void setSessionAttr(String key, Object value) {
HttpSession ssn = getSession(true);
ssn.setAttribute(key, value);
}
public Cookie getCookie(String name) {
return cookies.get(name);
}
public void setCookie(String name, String value, int maxAge, boolean allSubDomain) {
RequestUtils.setCookie(request, response, name, value, maxAge, allSubDomain);
}
public void deleteCookie(String name, boolean all_domain) {
RequestUtils.deleteCookie(request, response, name, all_domain);
}
public String getHeader(String name) {
return request.getHeader(name);
}
public void setHeader(String name, String value) {
response.setHeader(name, value);
}
public void setHeader(String name, int value) {
response.setIntHeader(name, value);
}
public void setHeader(String name, long value) {
response.setDateHeader(name, value);
}
public void forward(String uri) throws ServletException, IOException {
RequestDispatcher rd = context.getRequestDispatcher(uri);
rd.forward(request, response);
}
public void redirect(String uri) throws IOException {
response.sendRedirect(uri);
}
public void include(String uri) throws ServletException, IOException {
RequestDispatcher rd = context.getRequestDispatcher(uri);
rd.include(request, response);
}
public static boolean isMultipart(HttpServletRequest request) {
return ((request.getContentType() != null)
&& (request.getContentType().toLowerCase().startsWith("multipart")));
}
public boolean isUpload() {
return (request instanceof MultipartRequest);
}
public boolean isRobot() {
return RequestUtils.isRobot(request);
}
public long getId() {
return getParam("id", 0L);
}
public String getIp() {
return RequestUtils.getRemoteAddr(request);
}
public File getFile(String fieldName) {
if (request instanceof MultipartRequest)
return ((MultipartRequest) request).getFile(fieldName);
return null;
}
public File getImage(String fieldname) {
File imgFile = getFile(fieldname);
return (imgFile != null && Multimedia.isImageFile(imgFile.getName())) ? imgFile : null;
}
public String getServletPath() {
return request.getServletPath();
}
public String getURI() {
return request.getRequestURI();
}
public String getContextPath() {
return request.getContextPath();
}
public static String getWebrootPath() {
String root = WebContext.class.getResource("/").getFile();
try {
root = new File(root).getParentFile().getParentFile().getCanonicalPath();
root += File.separator;
} catch (IOException e) {
throw new RuntimeException(e);
}
return root;
}
/**
* 从cookie中获取locale信息
* @return
*/
public Locale getLocale() {
if (locale != null) {
return locale;
}
Cookie cookie = getCookie(LOCALE);
if (cookie != null) {
return toLocale(cookie.getValue());
}
return request.getLocale();
}
public void saveLocale(String localeValue) {
saveLocale(localeValue, MAX_AGE);
}
/**
* 将local信息存入cookie
* @param localeValue
*/
public void saveLocale(String localeValue, int maxAge) {
if (localeValue != null) {
try {
locale = toLocale(localeValue);
response.setLocale(locale);// 放入请求
setCookie(LOCALE, locale.getLanguage() + "_" + locale.getCountry(), maxAge, true);
} catch (Exception e) {
log.warn("setLocale is error, localeValue=" + localeValue + "is not used.");
}
}
}
/**
* 从url参数中获取locale并存入cookie
*/
public void saveLocale() {
String requestLocale = getParam(REQUEST_LOCALE_PARAM);
saveLocale(requestLocale);
}
/**
* 输出信息到浏览器
*
* @param msg
* @throws IOException
*/
public void print(Object msg) throws IOException {
response.setContentType("text/html;charset=utf-8");
if (!UTF_8.equalsIgnoreCase(response.getCharacterEncoding()))
response.setCharacterEncoding(UTF_8);
response.getWriter().print(msg);
}
public void printJson(String[] key, Object[] value) throws IOException {
StringBuilder json = new StringBuilder("{");
for (int i = 0; i < key.length; i++) {
if (i > 0)
json.append(',');
boolean isNum = value[i] instanceof Number;
json.append("\"");
json.append(key[i]);
json.append("\":");
if (!isNum)
json.append("\"");
json.append(value[i]);
if (!isNum)
json.append("\"");
}
json.append("}");
print(json.toString());
}
public void printJson(String key, Object value) throws IOException {
printJson(new String[] { key }, new Object[] { value });
}
public void notFound() throws IOException {
error(HttpServletResponse.SC_NOT_FOUND);
}
public void error(int code, String... msg) throws IOException {
if (msg.length > 0)
response.sendError(code, msg[0]);
else
response.sendError(code);
}
public void forbidden() throws IOException {
error(HttpServletResponse.SC_FORBIDDEN);
}
public void closeCache() {
setHeader("Pragma", "No-cache");
setHeader("Cache-Control", "no-cache");
setHeader("Expires", 0L);
}
public ActionException fromResource(String bundle, String key, Object... args) {
String res = ResourceUtils.getStringForLocale(request.getLocale(), bundle, key, args);
return new ActionException(res);
}
/**
* 将HTTP请求参数映射到bean对象中
*
* @param req
* @param beanClass
* @return
* @throws Exception
*/
public <T> T convertBean(Class<T> beanClass) {
try {
T bean = beanClass.newInstance();
BeanUtils.populate(bean, request.getParameterMap());
return bean;
} catch (Exception e) {
throw new ActionException(e.getMessage());
}
}
public void populate(Object bean) throws IllegalAccessException, InvocationTargetException {
BeanUtils.populate(bean, request.getParameterMap());
}
/**
* 去除contextPath的URI
* @param rc
* @return
*/
public String getURIAndExcludeContextPath() {
if (getContextPath().equals("")) {
return getURI();
} else {
String t = StringUtils.substringAfter(getURI(), getContextPath());
if (!t.startsWith("/")) {
t = "/" + t;
}
return t;
}
}
/**
* 从cookie中读取保存的用户信息
*
* @param req
* @return
*/
public IUser getUserFromCookie() {
try {
Cookie cookie = getCookie(COOKIE_LOGIN);
if (cookie != null && StringUtils.isNotBlank(cookie.getValue())) {
return getUserByUUID(cookie.getValue());
}
} catch (Exception e) {
}
return null;
}
/**
* 保存登录信息
*
* @param req
* @param res
* @param user
* @param save
*/
public void saveUserInCookie(IUser user, boolean save) {
String newValue = genLoginKey(user, getIp(), getHeader("user-agent"));
int maxAge = save ? MAX_AGE : -1;
deleteCookie(COOKIE_LOGIN, true);
setCookie(COOKIE_LOGIN, newValue, maxAge, true);
}
/**
* 删除登录信息
*/
public void deleteUserFromCookie() {
deleteCookie(COOKIE_LOGIN, true);
}
/**
* 从cookie中读取保存的用户信息
*
* @param req
* @return
*/
public IUser getUserByUUID(String uuid) {
if (StringUtils.isBlank(uuid))
return null;
String ck = decrypt(uuid);
final String[] items = StringUtils.split(ck, '|');
if (items.length == 5) {
String ua = getHeader("user-agent");
int uaCode = (ua == null) ? 0 : ua.hashCode();
int oldUaCode = Integer.parseInt(items[3]);
if (uaCode == oldUaCode) {
return new IUser() {
public boolean isBlocked() {
return false;
}
public long getId() {
return NumberUtils.toLong(items[0], -1L);
}
public String getPassword() {
return items[1];
}
public byte getRole() {
return IUser.ROLE_GENERAL;
}
};
}
}
return null;
}
/**
* 生成用户登录标识字符串
*
* @param user
* @param ip
* @param userAgent
* @return
*/
public static String genLoginKey(IUser user, String ip, String userAgent) {
StringBuilder sb = new StringBuilder();
sb.append(user.getId());
sb.append('|');
sb.append(user.getPassword());
sb.append('|');
sb.append(ip);
sb.append('|');
sb.append((userAgent == null) ? 0 : userAgent.hashCode());
sb.append('|');
sb.append(System.currentTimeMillis());
return encrypt(sb.toString());
}
/**
* 加密
*
* @param value
* @return
* @throws Exception
*/
public static String encrypt(String value) {
byte[] data = CryptUtils.encrypt(value.getBytes(), E_KEY);
try {
Base64.Encoder encoder = Base64.getEncoder();
return URLEncoder.encode(new String(encoder.encode(data)), UTF_8);
} catch (UnsupportedEncodingException e) {
return null;
}
}
/**
* 解密
*
* @param value
* @return
* @throws Exception
*/
public static String decrypt(String value) {
try {
value = URLDecoder.decode(value, UTF_8);
if (StringUtils.isBlank(value))
return null;
Base64.Decoder decoder = Base64.getDecoder();
byte[] data = decoder.decode(value.getBytes());
return new String(CryptUtils.decrypt(data, E_KEY));
} catch (UnsupportedEncodingException excp) {
return null;
}
}
public static Locale toLocale(final String str) {
if (str == null) {
return null;
}
if (str.isEmpty()) { // LANG-941 - JDK 8 introduced an empty locale where all fields are blank
return new Locale("", "");
}
if (str.contains("#")) { // LANG-879 - Cannot handle Java 7 script & extensions
throw new IllegalArgumentException("Invalid locale format: " + str);
}
final int len = str.length();
if (len < 2) {
throw new IllegalArgumentException("Invalid locale format: " + str);
}
final char ch0 = str.charAt(0);
if (ch0 == '_') {
if (len < 3) {
throw new IllegalArgumentException("Invalid locale format: " + str);
}
final char ch1 = str.charAt(1);
final char ch2 = str.charAt(2);
if (!Character.isUpperCase(ch1) || !Character.isUpperCase(ch2)) {
throw new IllegalArgumentException("Invalid locale format: " + str);
}
if (len == 3) {
return new Locale("", str.substring(1, 3));
}
if (len < 5) {
throw new IllegalArgumentException("Invalid locale format: " + str);
}
if (str.charAt(3) != '_') {
throw new IllegalArgumentException("Invalid locale format: " + str);
}
return new Locale("", str.substring(1, 3), str.substring(4));
}
String[] split = str.split("_", -1);
int occurrences = split.length - 1;
switch (occurrences) {
case 0:
if (StringUtils.isAllLowerCase(str) && (len == 2 || len == 3)) {
return new Locale(str);
} else {
throw new IllegalArgumentException("Invalid locale format: " + str);
}
case 1:
if (StringUtils.isAllLowerCase(split[0])
&& (split[0].length() == 2 || split[0].length() == 3) && split[1].length() == 2
&& StringUtils.isAllUpperCase(split[1])) {
return new Locale(split[0], split[1]);
} else {
throw new IllegalArgumentException("Invalid locale format: " + str);
}
case 2:
if (StringUtils.isAllLowerCase(split[0])
&& (split[0].length() == 2 || split[0].length() == 3)
&& (split[1].length() == 0
|| (split[1].length() == 2 && StringUtils.isAllUpperCase(split[1])))
&& split[2].length() > 0) {
return new Locale(split[0], split[1], split[2]);
}
//$FALL-THROUGH$
default:
throw new IllegalArgumentException("Invalid locale format: " + str);
}
}
/**
* 自动解码
*
*/
private static class RequestProxy extends HttpServletRequestWrapper {
private String uriEncoding;
RequestProxy(HttpServletRequest request, String encoding) {
super(request);
this.uriEncoding = encoding;
}
/**
* 重载getParameter
*/
public String getParameter(String paramName) {
String value = super.getParameter(paramName);
return decodeParamValue(value);
}
/**
* 重载getParameterMap
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Map<String, Object> getParameterMap() {
Map params = super.getParameterMap();
HashMap<String, Object> newParams = new HashMap<String, Object>();
Iterator<String> iter = params.keySet().iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
Object oValue = params.get(key);
if (oValue.getClass().isArray()) {
String[] values = (String[]) params.get(key);
String[] new_values = new String[values.length];
for (int i = 0; i < values.length; i++)
new_values[i] = decodeParamValue(values[i]);
newParams.put(key, new_values);
} else {
String value = (String) params.get(key);
String newValue = decodeParamValue(value);
if (newValue != null)
newParams.put(key, newValue);
}
}
return newParams;
}
/**
* 重载getParameterValues
*/
public String[] getParameterValues(String arg) {
String[] values = super.getParameterValues(arg);
for (int i = 0; values != null && i < values.length; i++)
values[i] = decodeParamValue(values[i]);
return values;
}
/**
* 参数转码
*
* @param value
* @return
*/
private String decodeParamValue(String value) {
if (StringUtils.isBlank(value) || StringUtils.isBlank(uriEncoding)
|| StringUtils.isNumeric(value))
return value;
try {
return new String(value.getBytes("8859_1"), uriEncoding);
} catch (Exception e) {
}
return value;
}
}
}