再发OSChina源码之 ActionServlet,控制类

红薯 发布于 2010/04/25 17:48
阅读 2K+
收藏 28

ActionServlet 这个类在 OSChina 是负责处理表单请求的,所有以 /action 开头的请求,自己感觉还不甚满意,别拍我砖头。

源码如下:

package my.mvc;

import java.io.*;
import java.lang.reflect.*;
import java.net.URLDecoder;
import java.util.*;

import javax.servlet.*;
import javax.servlet.http.*;

import my.db.DBException;
import my.util.ResourceUtils;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;

/**
 * 业务处理方法入口,URI的映射逻辑:
 * /action/xxxxxx/xxxx -> com.dlog4j.action.XxxxxxAction.xxxx(req,res)
 * <pre>
	林花谢了春红,
	太匆匆,
	无奈朝来寒雨晚来风。

	胭脂泪,
	相留醉,
	几时重,
	自是人生长恨水长东。
 * </pre>
 * @author Winter Lau (http://my.oschina.net/javayou)
 */
public final class ActionServlet extends HttpServlet {

	private final static String ERROR_PAGE = "error_page";
	private final static String GOTO_PAGE = "goto_page";
	private final static String THIS_PAGE = "this_page";
	private final static String ERROR_MSG = "error_msg";
	
	private final static String UTF_8 = "utf-8";	
	private List<String> action_packages = null;
	private final static ThreadLocal<Boolean> g_json_enabled = new ThreadLocal<Boolean>();
	
	@Override
	public void init() throws ServletException {
		String tmp = getInitParameter("packages");
		action_packages = Arrays.asList(StringUtils.split(tmp,','));
		String initial_actions = getInitParameter("initial_actions");
		for(String action : StringUtils.split(initial_actions,','))
			try {
				_LoadAction(action);
			} catch (Exception e) {
				log("Failed to initial action : " + action, e);
			}
	}

	@Override
	public void destroy() {
		for(Object action : actions.values()){
			try{
				Method dm = action.getClass().getMethod("destroy");
				if(dm != null){
					dm.invoke(action);
					log("!!!!!!!!! " + action.getClass().getSimpleName() + " destroy !!!!!!!!!");
				}
			}catch(NoSuchMethodException e){
			}catch(Exception e){
				log("Unabled to destroy action: " + action.getClass().getSimpleName(), e);
			}
		}
		super.destroy();
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		process(RequestContext.get(), false);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		process(RequestContext.get(), true);
	}
	
	/**
	 * 执行Action方法并进行返回处理、异常处理
	 * @param req
	 * @param resp
	 * @param is_post
	 * @throws ServletException
	 * @throws IOException
	 */
	protected void process(RequestContext req, boolean is_post) 
		throws ServletException, IOException 
	{
		try{
			req.response().setContentType("text/html;charset=utf-8");
			if(_process(req, is_post)){ 
				String gp = req.param(GOTO_PAGE);
				if(StringUtils.isNotBlank(gp))
					req.redirect(gp);
			}
		}catch(InvocationTargetException e){
			Throwable t = e.getCause();
			if(t instanceof ActionException)
				handleActionException(req, (ActionException)t);
			else if(t instanceof DBException)
				handleDBException(req, (DBException)t);
			else
				throw new ServletException(t);
		}catch(ActionException t){
			handleActionException(req, t);
		}catch(IOException e){
			throw e;
		}catch(DBException e){
			handleDBException(req, e);
		}catch(Exception e){
			log("Exception in action process.", e);
			throw new ServletException(e);
		}finally{
			g_json_enabled.remove();
		}
	}
	
	/**
	 * Action业务异常
	 * @param req
	 * @param resp
	 * @param t
	 * @throws ServletException
	 * @throws IOException
	 */
	protected void handleActionException(RequestContext req, ActionException t)	throws ServletException, IOException {		
		handleException(req, t.getMessage());
	}
	
	protected void handleDBException(RequestContext req, DBException e) throws ServletException, IOException {
		log("DBException in action process.", e);
		handleException(req, ResourceUtils.getString("error", "database_exception", e.getCause().getMessage()));
	}
	
	/**
	 * URL解码
	 * 
	 * @param url
	 * @param charset
	 * @return
	 */
	private static String _DecodeURL(String url, String charset) {
		if (StringUtils.isEmpty(url))
			return "";
		try {
			return URLDecoder.decode(url, charset);
		} catch (Exception e) {
		}
		return url;
	}

	protected void handleException(RequestContext req, String msg) throws ServletException, IOException {
		String ep = req.param(ERROR_PAGE);
		if(StringUtils.isNotBlank(ep)){
			if(ep.charAt(0)=='%')
				ep = _DecodeURL(ep, UTF_8);
			ep = ep.trim();
			if(ep.charAt(0)!='/'){
				req.redirect(req.contextPath()+"/");
			}
			else{
				req.request().setAttribute(ERROR_MSG, msg);
				req.forward(ep.trim());
			}
		}
		else{
			if(g_json_enabled.get())
				req.output_json("msg", msg);
			else
				req.print(msg);
		}
	}	
	
	/**
	 * 业务逻辑处理
	 * @param req
	 * @param resp
	 * @param is_post_method
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 * @throws IOException 
	 * @throws ServletException
	 * @throws IOException
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 */
	private boolean _process(RequestContext req, boolean is_post)
			 throws InstantiationException,
					IllegalAccessException, 
					IOException, 
					IllegalArgumentException,
					InvocationTargetException
	{
		String requestURI = req.uri();
		String[] parts = StringUtils.split(requestURI, '/');
		if(parts.length<2){
			req.not_found();
			return false;
		}
		//加载Action类
		Object action = this._LoadAction(parts[1]);
		if(action == null){
			req.not_found();
			return false;
		}
		String action_method_name = (parts.length>2)?parts[2]:"index";
		Method m_action = this._GetActionMethod(action, action_method_name);
		if(m_action == null){
			req.not_found();
			return false;
		}
		
		//判断action方法是否只支持POST
		if (!is_post && m_action.isAnnotationPresent(Annotation.PostMethod.class)){
			req.not_found();
			return false;
		}
		
		g_json_enabled.set(m_action.isAnnotationPresent(Annotation.JSONOutputEnabled.class));
		
		if(m_action.isAnnotationPresent(Annotation.UserRoleRequired.class)){
			IUser loginUser = req.user();
			if(loginUser == null){
				String this_page = req.param(THIS_PAGE, "");
				throw req.error("user_not_login", this_page);
			}
			if(loginUser.IsBlocked())
				throw req.error("user_blocked");
			
			Annotation.UserRoleRequired urr = (Annotation.UserRoleRequired)m_action.getAnnotation(Annotation.UserRoleRequired.class);
			if(loginUser.getRole() < urr.role())
				throw req.error("user_role_deny");			
		}
		
		//调用Action方法之准备参数
		int arg_c = m_action.getParameterTypes().length;
		switch(arg_c){
		case 0: // login()
			m_action.invoke(action);
			break ;
		case 1:
			m_action.invoke(action, req);
			break;
		case 2: // login(HttpServletRequest req, HttpServletResponse res)
			m_action.invoke(action, req.request(), req.response());
			break ;
		case 3: // login(HttpServletRequest req, HttpServletResponse res, String[] extParams)
			StringBuilder args = new StringBuilder();
			for(int i=3;i<parts.length;i++){
				if(StringUtils.isBlank(parts[i]))
					continue;
				if(args.length() > 0)
					args.append('/');
				args.append(parts[i]);
			}
			boolean isLong = m_action.getParameterTypes()[2].equals(long.class);
			m_action.invoke(action, req.request(), req.response(), isLong ? NumberUtils.toLong(
					args.toString(), -1L) : args.toString());
			break ;
		default:
			req.not_found();
			return false;
		}
		
		return true;
	}
	
	/**
	 * 加载Action类
	 * @param act_name
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 */
	protected Object _LoadAction(String act_name) throws InstantiationException,IllegalAccessException {
		Object action = actions.get(act_name);
		if(action == null){
			for(String pkg : action_packages){
				String cls = pkg + '.' + StringUtils.capitalize(act_name) + "Action";
				action = _LoadActionOfFullname(act_name, cls);
				if(action != null)
					break;
			}
		}
		return action ;
	}
	
	private Object _LoadActionOfFullname(String act_name, String cls) throws IllegalAccessException, InstantiationException {
		Object action = null;
		try {								
			action = Class.forName(cls).newInstance();
			try{
				Method action_init_method = action.getClass().getMethod("init", ServletContext.class);
				action_init_method.invoke(action, getServletContext());
			}catch(NoSuchMethodException e){
			}catch(InvocationTargetException excp) {
				excp.printStackTrace();
			}
			if(!actions.containsKey(act_name)){
				synchronized(actions){
					actions.put(act_name, action);
				}
			}
		} catch (ClassNotFoundException excp) {}
		return action;
	}
	
	/**
	 * 获取名为{method}的方法
	 * @param action
	 * @param method
	 * @return
	 */
	private Method _GetActionMethod(Object action, String method) {
		String key = action.getClass().getSimpleName() + '.' + method;
		Method m = methods.get(key);
		if(m != null) return m;
		for(Method m1 : action.getClass().getMethods()){
			if(m1.getModifiers()==Modifier.PUBLIC && m1.getName().equals(method)){
				synchronized(methods){
					methods.put(key, m1);
				}
				return m1 ;
			}
		}
		return null;
	}

	private final static HashMap<String, Object> actions = new HashMap<String, Object>();
	private final static HashMap<String, Method> methods = new HashMap<String, Method>();

}

加载中
0
大东哥
大东哥

哪里不满意,类行数太多?

0
红薯
红薯

引用来自“东明”的帖子

哪里不满意,类行数太多?

觉得这个类应该实现纯Controller,而不应该有业务的逻辑,但还是包含了一些。

0
李军

看来我还得好好学啊,我写的控制器比起你的真是小菜。

0
z
zdmcjm

单一职责.

0
宋浩
宋浩

引用来自“红薯”的帖子

引用来自“东明”的帖子

哪里不满意,类行数太多?

觉得这个类应该实现纯Controller,而不应该有业务的逻辑,但还是包含了一些。

还包含了诗词歌赋。 

0
李剑
李剑

唉..还是来晚了.呵呵.收藏了

0
带着爱心和梦想生活着
带着爱心和梦想生活着

红薯大哥 太牛逼了,收藏了

0
紫海龟
紫海龟

看不到对request,session的处理,你似乎封装到你的RequestContext里面了。

0
阎王他爹
阎王他爹

贴个标签,回来慢慢看。

0
赞山风
赞山风

我现在才发现,写程序也可以赋诗在上面

返回顶部
顶部