package org.nutz.mvc; import java.io.IOException; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.nutz.ioc.Ioc; import org.nutz.ioc.IocContext; import org.nutz.json.Json; import org.nutz.json.JsonFormat; import org.nutz.lang.Lang; import org.nutz.lang.Strings; import org.nutz.lang.util.Context; import org.nutz.mvc.config.AtMap; import org.nutz.mvc.impl.NutMessageMap; import org.nutz.mvc.ioc.SessionIocContext; /** * Mvc 相关帮助函数 * * @author zozoh(zozohtnt@gmail.com) * @author wendal(wendal1985@gmail.com) */ public abstract class Mvcs { // TODO 这个变量应该在 1.b.46 之后的某一个版本删掉 public static final String DEFAULT_MSGS = "$default"; public static final String MSG = "msg"; public static final String LOCALE_KEY = "nutz_mvc_localization_key"; // ==================================================================== public static Map<String, Object> getLocaleMessage(String key) { Map<String, Map<String, Object>> msgss = getMessageSet(); if (null != msgss) return msgss.get(key); return null; } /** * 获取当前请求对象的字符串表 * * @param req * 请求对象 * @return 字符串表 */ @SuppressWarnings("unchecked") public static Map<String, String> getMessages(ServletRequest req) { return (Map<String, String>) req.getAttribute(MSG); } /** * 获取当前请求对象的字符串表(NutMessageMap 封装) * * @param req * 请求对象 * @return 字符串表 */ public static NutMessageMap getMessageMap(ServletRequest req) { return (NutMessageMap) req.getAttribute(MSG); } /** * 获取当前请求对象的字符串表中的某一个字符串 * * @param req * 请求对象 * @param key * 字符串键值 * @return 字符串内容 */ public static String getMessage(ServletRequest req, String key) { Map<String, String> map = getMessages(req); if (null != map) return map.get(key); return null; } /** * 获取当前会话的本地字符串集合的键值;如果当前 HTTP 会话不存在,则返回 null * * @return 当前会话的本地字符串集合的键值;如果当前 HTTP 会话不存在,则返回 null */ public static String getLocalizationKey() { HttpSession sess = getHttpSession(false); return null == sess ? null : (String) sess.getAttribute(LOCALE_KEY); } /** * 设置本地化字符串的键值 * <p> * 如果你用的是 Nutz.Mvc 默认的本地化机制,那么你的本地字符串键值,相当于一个你目录名。 <br> * 比如 "zh_CN" 等 * * @param key * 键值 * @return 是否设置成功 */ public static boolean setLocalizationKey(String key) { HttpSession sess = getHttpSession(); if (null == sess) return false; sess.setAttribute(LOCALE_KEY, key); return true; } /** * 返回当前加载了的本地化字符串的键值 * * @return 当前都加载了哪些种字符串的 KEY */ public static Set<String> getLocalizationKeySet() { Map<String, Map<String, Object>> msgss = getMessageSet(); if (null == msgss) return new HashSet<String>(); return msgss.keySet(); } /** * 默认的本地化字符串 KEY,当为 NULL 时,Nutz.Mvc 会随便用一个 */ private static String default_localization_key = null; /** * 设置默认的多国语言 * * @param key * 默认的多国语言 KEY */ public static void setDefaultLocalizationKey(String key) { default_localization_key = key; } /** * 返回默认的本地化字符串 KEY * * @return 默认的本地化字符串 KEY */ public static String getDefaultLocalizationKey() { return default_localization_key; } /** * 为当前的 HTTP 请求对象设置一些必要的属性。包括: * <ul> * <li>本地化子字符串 => ${msg} * <li>应用的路径名 => ${base} * </ul> * * @param req * HTTP 请求对象 */ public static void updateRequestAttributes(HttpServletRequest req) { // 初始化本次请求的多国语言字符串 Map<String, Map<String, Object>> msgss = getMessageSet(); if (msgss == null && !ctx.localizations.isEmpty()) msgss = ctx.localizations.values().iterator().next(); if (null != msgss) { Map<String, Object> msgs = null; String lKey = Strings.sBlank(Mvcs.getLocalizationKey(), getDefaultLocalizationKey()); if (!Strings.isBlank(lKey)) msgs = msgss.get(lKey); // 没有设定特殊的 Local 名字,随便取一个 if (null == msgs) { if (msgss.size() > 0) msgs = msgss.values().iterator().next(); } // 记录到请求中 req.setAttribute(MSG, msgs); } // 记录一些数据到请求对象中 req.setAttribute("base", req.getContextPath()); req.setAttribute("$request", req); } /** * 获取当前请求的路径,并去掉后缀 * * @param req * HTTP 请求对象 */ public static String getRequestPath(HttpServletRequest req) { return getRequestPathObject(req).getPath(); } /** * 获取当前请求的路径,并去掉后缀 * * @param req * HTTP 请求对象 */ public static RequestPath getRequestPathObject(HttpServletRequest req) { String url = req.getPathInfo(); if (null == url) url = req.getServletPath(); return getRequestPathObject(url); } /** * 获取当前请求的路径,并去掉后缀 * * @param url * 请求路径的URL */ public static RequestPath getRequestPathObject(String url) { RequestPath rr = new RequestPath(); rr.setUrl(url); if (null != url) { int lio = 0; if (!url.endsWith("/")) { int ll = url.lastIndexOf('/'); lio = url.lastIndexOf('.'); if (lio < ll) lio = -1; } if (lio > 0) { rr.setPath(url.substring(0, lio)); rr.setSuffix(url.substring(lio + 1)); } else { rr.setPath(url); rr.setSuffix(""); } } else { rr.setPath(""); rr.setSuffix(""); } return rr; } /** * 注销当前 HTTP 会话。所有 Ioc 容器存入的对象都会被注销 * * @param session * HTTP 会话对象 */ public static void deposeSession(HttpSession session) { if (session != null) new SessionIocContext(session).depose(); } /** * 它将对象序列化成 JSON 字符串,并写入 HTTP 响应 * * @param resp * 响应对象 * @param obj * 数据对象 * @param format * JSON 的格式化方式 * @throws IOException * 写入失败 */ public static void write(HttpServletResponse resp, Object obj, JsonFormat format) throws IOException { resp.setHeader("Cache-Control", "no-cache"); if (resp.getContentType() == null) resp.setContentType("text/plain"); // by mawm 改为直接采用resp.getWriter()的方式直接输出! Json.toJson(resp.getWriter(), obj, format); resp.flushBuffer(); } // ================================================================== private static final ThreadLocal<String> NAME = new ThreadLocal<String>(); /** * NutMvc的上下文 */ public static NutMvcContext ctx = new NutMvcContext(); private static ServletContext servletContext; /** * 获取 HTTP 请求对象 * @return HTTP 请求对象 */ public static final HttpServletRequest getReq() { return ctx.reqThreadLocal.get().getAs(HttpServletRequest.class, "req"); } /** * 获取 HTTP 响应对象 * @return HTTP 响应对象 */ public static final HttpServletResponse getResp() { return ctx.reqThreadLocal.get().getAs(HttpServletResponse.class, "resp"); } public static final String getName() { return NAME.get(); } /** * 获取 Action 执行的上下文 * @return Action 执行的上下文 */ public static final ActionContext getActionContext() { return ctx.reqThreadLocal.get().getAs(ActionContext.class, "ActionContext"); } public static void set(String name, HttpServletRequest req, HttpServletResponse resp) { NAME.set(name); ctx.reqThreadLocal.get().set("req", req); ctx.reqThreadLocal.get().set("resp", resp); } /** * 设置 Servlet 执行的上下文 * * @param servletContext * Servlet 执行的上下文 */ public static void setServletContext(ServletContext servletContext) { Mvcs.servletContext = servletContext; } /** * 设置 Action 执行的上下文 * * @param actionContext * Action 执行的上下文 */ public static void setActionContext(ActionContext actionContext) { ctx.reqThreadLocal.get().set("ActionContext", actionContext); } /** * 获取 Servlet 执行的上下文 * @return Servlet 执行的上下文 */ public static ServletContext getServletContext() { return servletContext; } /** * 设置对象装配的上下文环境 * * @param iocContext * 对象装配的上下文环境 */ public static void setIocContext(IocContext iocContext) { ctx.reqThreadLocal.get().set("IocContext", iocContext); } /** * 获取对象装配的上下文环境 * @return 进行对象装配的上下文环境 */ public static IocContext getIocContext() { return ctx.reqThreadLocal.get().getAs(IocContext.class, "IocContext"); } // 新的,基于ThreadLoacl改造过的Mvc辅助方法 // ==================================================================== /** * 获取全局的Ioc对象 * @return 全局的Ioc对象 */ public static Ioc getIoc() { return ctx.iocs.get(getName()); } public static void setIoc(Ioc ioc) { ctx.iocs.put(getName(), ioc); } public static AtMap getAtMap() { return ctx.atMaps.get(getName()); } public static void setAtMap(AtMap atmap) { ctx.atMaps.put(getName(), atmap); } public static Map<String, Map<String, Object>> getMessageSet() { return ctx.localizations.get(getName()); } public static void setMessageSet(Map<String, Map<String, Object>> messageSet) { ctx.localizations.put(getName(), messageSet); } public static void setNutConfig(NutConfig config) { ctx.nutConfigs.put(getName(), config); } public static NutConfig getNutConfig() { return ctx.nutConfigs.get(getName()); } // ================================================================== /** * 重置当前线程所持有的对象 */ public static Context resetALL() { Context context = ctx.reqThreadLocal.get(); NAME.set(null); ctx.reqThreadLocal.set(Lang.context()); return context; } public static HttpSession getHttpSession() { return getHttpSession(true); } public static HttpSession getHttpSession(boolean createNew) { HttpServletRequest req = getReq(); if (null == req) return null; return req.getSession(createNew); } public static void close() { ctx.clear(); ctx.close(); } /** 在入口方法调用时,禁止调用1.b.51新加入的FastClass功能*/ // PS: 如果这个修改导致异常,请报issue,并将这个变量设置为true public static boolean disableFastClassInvoker = Lang.isAndroid; }