浅谈Struts2拦截器的原理与实现

Struts2拦截器是Struts2中的一个很重要的功能。本文将从概念开始,为大家讲解Struts2拦截器的实现原理以及如何定义等等内容

一、理解Struts2拦截器

1. Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现.

2. 拦截器栈(Interceptor Stack)。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。

struts.xml

            <!-- 自定义拦截栈 -->
            <interceptor-stack name="portalMSInterceptorStack">
                <interceptor-ref name="logInterceptor" />
                <interceptor-ref name="fileUploadStack" />
                <interceptor-ref name="json" />
                <interceptor-ref name="i18n" />
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="ExTokenInterceptor" />
                <interceptor-ref name="siteInterceptor" />
            </interceptor-stack>
        </interceptors>
        <!-- 覆盖默认的拦截栈 -->
        <default-interceptor-ref name="portalMSInterceptorStack" />

二、实现Struts2拦截器原理

Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的    拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器

三、定义Struts2拦截器。

Struts2规定用户自定义拦截器必须实现com.opensymphony.xwork2.interceptor.Interceptor接口。该接口声明了3个方法,

void init(); void destroy(); String intercept(ActionInvocation invocation) throws Exception;

其中,init和destroy方法会在程序开始和结束时各执行一遍,不管使用了该拦截器与否,只要在struts.xml中声明了该Struts2拦截器就会被执行。
intercept方法就是拦截的主体了,每次拦截器生效时都会执行其中的逻辑。

不过,struts中又提供了几个抽象类来简化这一步骤。

public abstract class AbstractInterceptor implements Interceptor;
public abstract class MethodFilterInterceptor extends AbstractInterceptor;

都是模板方法实现的。

其中AbstractInterceptor提供了init()和destroy()的空实现,使用时只需要覆盖intercept()方法;

而MethodFilterInterceptor则提供了includeMethods和excludeMethods两个属性,用来过滤执行该过滤器的action的方法。可以通过param来加入或者排除需要过滤的方法。

一般来说,拦截器的写法都差不多。看下面的示例:

package interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class MyInterceptor implements Interceptor {
public void destroy() {
// TODO Auto-generated method stub
}
public void init() {
// TODO Auto-generated method stub
}
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("Action执行前插入 代码");      
//执行目标方法 (调用下一个拦截器, 或执行Action)    
final String res = invocation.invoke();    
System.out.println("Action执行后插入 代码");    
return res;    
}
}

四、配置Struts2拦截器

Struts2拦截器需要在struts.xml中声明,如下struts.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.objectFactory" value="spring" />

<package name="default" extends="struts-default"> <interceptors> <interceptor name="MyInterceptor" class="interceptor.MyInterceptor"></interceptor> <interceptor-stack name="myInterceptorStack"> <interceptor-ref name="MyInterceptor"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <action name="loginAction" class="loginAction"> <result name="fail">/index.jsp </result> <result name="success">/success.jsp</result> <interceptor-ref name="myInterceptorStack"></interceptor-ref> </action> </package> </struts>

拦截器

名字

说明

Alias Interceptor

alias

在不同请求之间将请求参数在不同名字件转换,请求内容不变

Chaining Interceptor

chain

让前一个Action的属性可以被后一个Action访问,现在和chain类型的result)结合使用。

Checkbox Interceptor

checkbox

添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox

Cookies Interceptor

cookies

使用配置的name,value来是指cookies

Conversion Error Interceptor

conversionError

将错误从ActionContext中添加到Action的属性字段中。

Create Session Interceptor

createSession

自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。

Debugging Interceptor

debugging

提供不同的调试用的页面来展现内部的数据状况。

Execute and Wait Interceptor

execAndWait

在后台执行Action,同时将用户带到一个中间的等待页面。

Exception Interceptor

exception

将异常定位到一个画面

File Upload Interceptor

fileUpload

提供文件上传功能

I18n Interceptor

i18n

记录用户选择的locale

Logger Interceptor

logger

输出Action的名字

Message Store Interceptor

store

存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。

Model Driven Interceptor

model-driven

如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。

Scoped Model Driven

scoped-model-driven

如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用ActionsetModel方法将其放入Action内部。

Parameters Interceptor

params

将请求中的参数设置到Action中去。

Prepare Interceptor

prepare

如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法。

Scope Interceptor

scope

Action状态存入sessionapplication的简单方法。

Servlet Config Interceptor

servletConfig

提供访问HttpServletRequestHttpServletResponse的方法,以Map的方式访问。

Static Parameters Interceptor

staticParams

struts.xml文件中将中的中的内容设置到对应的Action中。

Roles Interceptor

roles

确定用户是否具有JAAS指定的Role,否则不予执行。

Timer Interceptor

timer

输出Action执行的时间

Token Interceptor

token

通过Token来避免双击

Token Session Interceptor

tokenSession

Token Interceptor一样,不过双击的时候把请求的数据存储在Session

Validation Interceptor

validation

使用action-validation.xml文件中定义的内容校验提交的数据。

Workflow Interceptor

workflow

调用Actionvalidate方法,一旦有错误返回,重新定位到INPUT画面

Parameter Filter Interceptor

N/A

从参数列表中删除不必要的参数

Profiling Interceptor

profiling

通过参数激活profile


struts.xml
 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <!-- 后缀*.action(默认值为action) -->
    <constant name="struts.action.extension" value="do,action" />
    <constant name="struts.devMode" value="true" />
    <constant name="struts.objectFactory" value="spring" />
    <constant name="struts.multipart.maxSize" value="1048576000" />
    <constant name="struts.locale" value="${localLanguage}" />
    
    <!-- 配置Struts标签访问静态方法 -->
    <constant name="struts.ognl.allowStaticMethodAccess" value="true" />
    <constant name="struts.custom.i18n.resources"
        value="
              ApplicationResources,
              ApplicationResources_WebManage,
              ApplicationResources_Publish,
              ApplicationResources_ResourceManage,
              ApplicationResources_Template,
              ApplicationResources_Category,
              ApplicationResources_SiteManage,

              ApplicationResources_CommonJS
              " />

    <include file="struts-default.xml" />
    <include file="struts-plugin.xml" />
    
    <package name="defaults" extends="struts-default">
        <result-types>
            <result-type name="json" class="org.apache.struts2.json.JSONResult"
                default="false" />
        </result-types>

        <interceptors>
            <!-- 日志拦截器 -->
            <interceptor name="logInterceptor"
                class="com.xxxxxx.dhm.portalMS.base.web.interceptor.LogInterceptor">
                <param name="includeParams">查询,查找</param>
                <param name="excludeClass">com.xxxxxx.sdp.rights.view.action.*</param>
            </interceptor>
            <!-- 站点信息拦截器(设置siteID) -->
            <interceptor name="siteInterceptor"
                class="com.xxxxxx.dhm.portalMS.base.web.interceptor.SiteInterceptor"></interceptor>
            <interceptor name="json"
                class="org.apache.struts2.json.JSONInterceptor" />

            <!-- 防止重复提交拦截器 -->
            <interceptor name="ExTokenInterceptor"
                class="com.xxxxxx.dhm.portalMS.base.web.interceptor.ExTokenInterceptor" />
            <!-- 自定义拦截栈 -->
            <interceptor-stack name="portalMSInterceptorStack">
                <interceptor-ref name="logInterceptor" />
                <interceptor-ref name="fileUploadStack" />
                <interceptor-ref name="json" />
                <interceptor-ref name="i18n" />
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="ExTokenInterceptor" />
                <interceptor-ref name="siteInterceptor" />
            </interceptor-stack>
        </interceptors>
        <!-- 覆盖默认的拦截栈 -->
        <default-interceptor-ref name="portalMSInterceptorStack" />
        <!-- 全局错误页面配置 -->
        <global-results>
            <result name="error">/common/error.jsp</result>
            <result name="global.error">/common/error.jsp</result>
            <result name="global.error.window">/common/error_window.jsp</result>
        </global-results>
    </package>

    <!-- 鉴权产品管理 -->
    <include file="struts/authProduct.xml" />
    <!-- 业务映射管理 -->
    <include file="struts/business.xml" />
    <!-- 系统管理 -->
    <include file="struts/sysmanage.xml" />
    <!--资源管理 -->
    <include file="struts/resource.xml" />
    <!--产品管理 -->
    <include file="struts/product.xml" />
    <!--站点管理 -->
    <include file="struts/webmanage.xml" />
    <!-- 网站资源管理-->
    <include file="struts/noderesource.xml" />
    <!-- 发布管理-->
    <include file="struts/publishmanage.xml" />
    <!-- 站点管理 -->

</struts>

/*
 * 工程名: portalMS
 * 包     名: com.xxx.dhm.portalMS.base.web.interceptor
 * 文 件名: LogInterceptor.java
 * 版      权: Copyright (c) 2009 Coship All Rights Reserved.
 * 描      述: 日志拦截器,记录系统中所有Action的执行情况。
 * 修 改 人:
 * 修改时间:
 * 跟踪单号:
 * 修改单号:
 * 修改内容:
 */
package com.xxxx.dhm.portalMS.base.web.interceptor;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;

import com.xxxx.ChineseCharacter;
import com.xxxx.dhm.common.config.impl.PropertiesFactory;
import com.xxxx.dhm.common.log.level.DHMLogLevel;
import com.xxxx.dhm.portalMS.base.dao.IBaseDAO;
import com.xxxx.dhm.portalMS.base.service.IService;
import com.xxxx.dhm.portalMS.base.web.action.BaseAction;
import com.xxxx.dhm.portalMS.common.Constants;
import com.xxxx.dhm.portalMS.common.SerConstants;
import com.xxxx.dhm.portalMS.common.util.RequestUtil;
import com.xxxx.dhm.portalMS.exception.PortalMSException;
import com.xxxx.sdp.rights.model.domain.Admin;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

/**
 *
 * 日志拦截器,可以拦截IEPG后台管理系统的所有struts的Action动作。该拦截器具备以下功能: 1、打印Action执行前、后的日志。
 * 2、针对一些必须记录操作的Action,如(新增操作),根据操作码将操作信息写入数据库日志。如果 操作出现异常,那么将操作信息写入数据库日志和系统日志。
 *
 * @author liqi
 * @version [V200R001, 2009-11-23]
 * @since [DHM.Core.portalMS-V200R001]
 */
public class LogInterceptor extends AbstractInterceptor implements LocaleProvider
{

    private static final long serialVersionUID = 8317600420657773448L;

    /** IEPG系统日志 */
    private static final Logger sysLogger = Logger.getLogger(LogInterceptor.class);

    /** IEPG数据库日志 */
    private static final Logger dbLogger = Logger.getLogger(LogInterceptor.class);

    /** 操作码 */
    private String logMessageId;

    /**
     * 排除不进行日志记录的class类,多个以分号隔开,支持正则表达式
     */
    private String excludeClass;

    private String[] excludeClassStr = {};

    /** 错误码 */
    private int errorCode;

    /** 操作描述 */
    private String operateDesc = "";

    /** 操作结果 */
    private boolean operateResult = true;

    /** 获取错误代码的资源文件 */
    TextProvider errorCodeProvider = null;

    /** 获取操作码的资源文件 */
    TextProvider operateCodePrivider = null;

    /** IEPG后台Action基类 */
    private BaseAction baseAction = null;

    /** 传入拦截器的参数 */
    private String includeParams = null;

    /** 封装查询动作数组 */
    private String[] filterActions = null;

    /**
     * 应用启动的时候,初始化拦截器
     */
    public void init()
    {
        errorCodeProvider = new TextProviderFactory().createInstance(ResourceBundle.getBundle("ErrorCodeResources",
                Locale.getDefault()), this);

        operateCodePrivider = new TextProviderFactory().createInstance(ResourceBundle.getBundle("LogDefinitions",
                Locale.getDefault()), this);

        if (this.includeParams != null && this.includeParams.length() != 0)
        {
            String[] temp = this.includeParams.split(SerConstants.COMMA);
            if (temp != null && temp.length != 0)
            {
                this.filterActions = temp;
            }
        }

        // 判断是否存在不需要拦截的类
        if (excludeClass != null && excludeClass.trim().length() > 0)
        {
            excludeClassStr = excludeClass.split(SerConstants.SEMICOLON);
        }
    }

    /**
     * 拦截器对Action进行拦截。
     *
     * 1、从Session中获取用户的登录对象,如果登录对象为空,直接执行拦截器后,返回登录页面。
     * 2、从登录对象中获取用户ID,以及获取HTTP请求的IP地址,在执行拦截器。 3、获取操作描述、操作码。
     * 4、如果Action执行异常,那么在异常处理中将执行信息写入系统日志、数据库日志。 5、如果Action执行成功,那么将执行信息写入数据库日志。
     *
     * @param invoke
     *            Action执行的状态
     * @return 返回 String代表Action执行的执行结果
     * @throws Exception
     *             抛出拦截器执行的异常对象,由容器来捕获。
     *
     * @return String 返回 String代表Action执行的执行结果
     * @exception throws Exception 抛出拦截器执行的异常对象
     */
    @Override
    public String intercept(ActionInvocation invoke) throws Exception
    {

        HttpServletRequest request = ServletActionContext.getRequest();
        Map<String, Object> session = ServletActionContext.getContext().getSession();
        Object obj = session.get(Constants.LOGIN_USER.getStringValue());
        Admin userInfo = null;
        String result = null;
        String ipAddr = RequestUtil.getInstance().getIPAddress(request);
        if (obj == null)
        {
            try
            {
                return invoke.invoke();
            }
            catch (Exception e)
            {
                String message = handleException(0, ipAddr, invoke, e);
                request.setAttribute(Constants.ERROR_MESSAGE.getStringValue(), message);
                return Action.INPUT;
            }
        }
        else
        {
            userInfo = (Admin) obj;
        }
        // 获取登录用户的编号
        int userId = -1;
        if (userInfo != null)
        {
            userId = userInfo.getId();
        }
        String className = invoke.getAction().getClass().getName();
        try
        {
            ActionProxy proxy = invoke.getProxy();
            String actionMethod = proxy.getMethod();
            sysLogger.log(Level.DEBUG, "method starts..." + className + "." + actionMethod);
            result = invoke.invoke();
            if (sysLogger.isDebugEnabled())
            {
                sysLogger.log(Level.DEBUG, printActionExceuteResult(proxy, invoke));
                printValueStackAfterActionExceuted(proxy, invoke);
            }

            if (!needLog(className))
            {
                return result;
            }
            baseAction = (BaseAction) invoke.getAction();
            // 获取描述
            operateDesc = baseAction.getOperateDesc();
            // 获取操作码
            logMessageId = baseAction.getOperateCode();
            // 获取不发生异常情况的操作结果
            operateResult = baseAction.isOperateResult();
        }
        catch (Exception e)
        {
            if (!needLog(className))
            {
                return result;
            }
            String message = handleException(userId, ipAddr, invoke, e);
            request.setAttribute(Constants.ERROR_MESSAGE.getStringValue(), message);
            String errorPageWindow = request.getParameter("errorPageWindow");

            // 是否采用打开新窗口来显示错误页面
            if (errorPageWindow != null)
            {
                return Constants.GLOBAL_ERROR_WINDOW.getStringValue();
            }
            else
            {
                return Constants.GLOBAL_ERROR.getStringValue();
            }
        }

        // 不发生异常情况的处理
        return handleActionResult(userId, ipAddr, invoke, result);
    }

    private boolean needLog(String className)
    {
        // 判断是否需要写日志
        if (excludeClassStr.length > 0)
        {
            for (int i = 0; i < excludeClassStr.length; i++)
            {
                String pattern = excludeClassStr[i];
                boolean isMatch = Pattern.matches(pattern, className);
                if (isMatch)
                {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 向日志信息中写入Action执行结果的详细信息。
     *
     * @param proxy
     *            Action代理对象
     * @param invoke
     *            Action执行对象
     * @return [返回Action执行结果]
     *
     * @return String [返回Action执行结果]
     */
    protected String printActionExceuteResult(ActionProxy proxy, ActionInvocation invoke)
    {
        StringBuilder buffer = new StringBuilder();
        buffer.append("method ends...");
        buffer.append(invoke.getAction().getClass().getName());
        buffer.append(".");
        buffer.append(proxy.getMethod());
        buffer.append(", and the execute result is ").append(invoke.getResultCode());

        return buffer.toString();
    }

    /**
     * 打印Action执行后,ValueStack栈中的数据。
     *
     * @param proxy
     *            Action执行代理对象
     * @param invoke
     *            Action执行对象
     */
    protected void printValueStackAfterActionExceuted(ActionProxy proxy, ActionInvocation invoke)
    {

        String actionName = invoke.getAction().getClass().getName();
        String actionMethod = proxy.getMethod();

        StringBuilder buffer = new StringBuilder();
        Object action = invoke.getAction();
        Method[] methods = action.getClass().getMethods();

        for (Method method : methods)
        {
            if (method.getParameterTypes() != null && method.getParameterTypes().length > 0)
            {
                continue;
            }
            printMethodInAction(buffer, method, action);
        }

        String result = buffer.toString();
        int index = result.lastIndexOf(SerConstants.COMMA);
        if (index != -1)
        {
            result = result.substring(0, index);
        }

        buffer = new StringBuilder();
        buffer.append("invoke '");
        buffer.append(actionName);
        buffer.append(".");
        buffer.append(actionMethod);
        buffer.append("', and it's variables are: ");
        buffer.append(result);

        sysLogger.debug(buffer.toString());
    }

    protected boolean printMethodInAction(StringBuilder buffer, Method method, Object action)
    {
        boolean doProcess = true;
        String methodName = method.getName();
        if (methodName != null && methodName.startsWith("get"))
        {
            try
            {
                Object obj = method.invoke(action);
                if (!isFilter(obj))
                {
                    String fieldName = getFieldName(methodName);
                    boolean doOutput = cannotOutput(fieldName);
                    if (!doOutput)
                    {
                        String className = obj.getClass().getName().toLowerCase();
                        if (className.contains("service") || className.contains("dao"))
                        {
                            doProcess = false;
                        }
                        else
                        {
                            append(buffer, fieldName, obj);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                sysLogger.error(methodName, e);
            }
        }

        return doProcess;
    }

    public boolean isFilter(Object obj)
    {
        if (obj == null)
        {
            return true;
        }
        else if (obj instanceof IService)
        {
            return true;
        }
        else if (obj instanceof IBaseDAO)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    protected void append(StringBuilder buffer, String fieldName, Object object)
    {
        buffer.append(fieldName);
        buffer.append("=");
        buffer.append(Arrays.toString(new Object[] { object }));
        buffer.append(SerConstants.COMMA);
    }

    protected boolean cannotOutput(String fieldName)
    {
        if (isReturnTrue(fieldName))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private boolean isReturnTrue(String fieldName)
    {
        return "locale".equalsIgnoreCase(fieldName) || "actionErrors".equalsIgnoreCase(fieldName)
                || "actionMessages".equalsIgnoreCase(fieldName) || "errorMessages".equalsIgnoreCase(fieldName)
                || "class".equalsIgnoreCase(fieldName) || "fieldErrors".equalsIgnoreCase(fieldName)
                || "errors".equalsIgnoreCase(fieldName) || "error".equalsIgnoreCase(fieldName);
    }

    protected String getFieldName(String methodName)
    {
        String firstChar = methodName.substring(3, 4);
        String out = firstChar.toLowerCase() + methodName.substring(4);
        return out;
    }

    /**
     * 执行Action后没有发生异常的处理。 1、先根据操作码从操作码定义的资源文件获取内容,解析成Log实体。
     * 2、再根据action执行的操作结果,设置该Log实体的操作结果属性。 3、插入一条数据库日志记录。
     *
     * 如果不存在操作,则不做任何处理,即:不会往数据库写入日志记录。
     *
     * @param userId
     *            登录用户的编号
     * @param ipAddr
     *            登录用户的IP地址
     * @param invoke
     *            拦截器调用对象
     * @param result
     *            拦截器执行Action的返回结果
     * @return 拦截器执行Action的返回结果
     *
     * @return String 拦截器执行Action的返回结果
     */
    protected String handleActionResult(int userId, String ipAddr, ActionInvocation invoke, String result)
    {
        // 存在操作码,写入操作日志
        if (logMessageId != null)
        {
            if (!"browse".equalsIgnoreCase(baseAction.getAction()))
            {

                // 如果返回的是“input”字符串,表明是校验没有通过,返回输入页面,因此不需要记录数据库日志。
                String resultCode = invoke.getResultCode();
                if (null == resultCode)
                {
                    resultCode = "";
                }
                if ("input".equalsIgnoreCase(resultCode))
                {
                    return result;
                }
                else if (resultCode.toLowerCase().contains("input"))
                {
                    return result;
                }

                // 成功写入数据库日志
                com.xxxx.dhm.portalMS.log.entity.BusinessLog log = createLog(userId, ipAddr);
                if (log != null)
                {
                    if (this.operateResult)
                    {
                        log.setResult(Constants.SUCCEED.getStringValue()); // 表示成功
                    }
                    else
                    {
                        log.setResult(Constants.FAILURED.getStringValue());// 不发生异常情况下,Action返回失败的状态码,并设置日志操作结果为失败。
                    }

                    dbLogger.log(DHMLogLevel.SERVICE, log);
                }
            }
        }

        return result;
    }

    /**
     * 异常处理 首先判断异常类型,如果是portalMSExceptin,那么这样的处理:
     * 1、从异常对象中获取错误代码,然后根据错误代码从资源文件中,读取该错误代码所对应的内容, 然后将内容和异常堆栈信息,写入系统日志。
     * 2、从操作的Action中获取操作码和操作描述,根据操作码从资源文件中读取具体什么操作(操作名称、操作对象),写入数据库日志
     * 如果不是portalMSExceptin,那么将错误信息和异常堆栈写入系统日志。
     *
     * @param userId
     *            登录用户的编号
     * @param ipAddr
     *            登录用户的IP地址
     * @param invoke
     *            拦截器调用对象
     * @param e
     *            异常对象
     * @return 返回错误信息
     *
     * @return String 返回错误信息
     */
    protected String handleException(int userId, String ipAddr, ActionInvocation invoke, Exception e)
    {
        baseAction = (BaseAction) invoke.getAction();
        // 获取描述
        operateDesc = baseAction.getOperateDesc();
        // 获取操作码
        logMessageId = baseAction.getOperateCode();
        // 错误信息
        String message = null;
        if (e instanceof PortalMSException)
        {
            PortalMSException iepg = (PortalMSException) e;
            // 从异常中获取错误代码
            int errorCodeTmp = (int) iepg.getErrorCode();
            // 从拦截器中获取错误代码
            if (errorCodeTmp != 0)
            {
                errorCode = errorCodeTmp;
            }
            if (errorCode != 0)
            {
                // 发生异常写入系统日志
                message = errorCodeProvider.getText(Constants.MSG_PORTALMS
                        + Constants.ERROR_CODE_PREFIX.getStringValue() + String.valueOf(errorCodeTmp),
                        new String[] { String.valueOf(errorCodeTmp) });
                // 从错误定义的资源文件中没有获取相应的错误信息,则从异常堆栈中获取错误信息。
                if ((Constants.ERROR_CODE_PREFIX.getStringValue() + errorCodeTmp).equalsIgnoreCase(message))
                {
                    message = iepg.getMessage();
                }
                // 判断是否是增,删和改操作
                boolean isManipulate = isDataBaseManipulateException(message);
                // 发生异常写入操作描述
                StringBuilder buffer = new StringBuilder();
                buffer.append(message);
                LogMessageBean logMessageBean = parseOperateContentAsLogMessageBean();
                if (logMessageBean != null)
                {
                    buffer.append("Operate:[OperateDescription=");
                    buffer.append(logMessageBean.getOperateModule());
                    buffer.append("]");
                }
                sysLogger.error(buffer.toString(), e);

                // 无法与数据库建立连接
                if (!isValidConnection(e))
                {
                    message = getConnectDatabaseErrorMessage();
                    sysLogger.error("can't connect to database, please check the connection is valid!!");
                }
                else
                {
                    // 获取异常信息,并处理异常信息
                    message = getExceptionMessageDisplayInErrorPage(message);
                    // 能够建立与数据库的连接
                    if (isManipulate)
                    {
                        // 发生异常写入数据库日志
                        com.xxxx.dhm.portalMS.log.entity.BusinessLog log = createLog(userId, ipAddr);
                        if (log != null)
                        {
                            String exceptionMessage = pareseExceptionMessage(iepg);
                            if (exceptionMessage != null)
                            {
                                String operateDesc = log.getDescription() + "\n<br/>Exception [" + exceptionMessage
                                        + "]";
                                log.setDescription(operateDesc);
                            }

                            log.setResult(Constants.FAILURED.getStringValue()); // 表示失败
                            dbLogger.log(DHMLogLevel.SERVICE, log, e);
                        }
                    }
                }
            }
            else
            {
                // 从portalMSException中获取获取信息。
                message = e.getMessage();
            }
        }
        else
        {
            ActionProxy proxy = invoke.getProxy();

            StringBuilder buffer = new StringBuilder();
            buffer.append(invoke.getAction().getClass().getName());
            buffer.append(".");
            buffer.append(proxy.getMethod());
            if (e.getMessage() == null)
            {
                return null;
            }
            // 发生异常写入系统日志
            sysLogger.error(buffer.toString(), e);
            // 将异常信息显示在错误页面
            message = getExceptionMessageDisplayInErrorPage(e.getMessage());
        }

        return message;
    }

    protected String getExceptionMessageDisplayInErrorPage(String errorMessage)
    {
        String message = errorMessage;
        int index = message.indexOf("portalMS-");
        if (index == -1)
        {
            message = this.errorCodeProvider.getText(Constants.MSG_PORTALMS
                    + Constants.ERROR_CODE_PREFIX.getStringValue()
                    + String.valueOf(Constants.ERROR_CODE_ACCESS_DATABASE.getLongValue()), new String[] { String
                    .valueOf(Constants.ERROR_CODE_ACCESS_DATABASE.getLongValue()) });
        }
        if (message.equals(String.valueOf(Constants.ERROR_CODE_ACCESS_DATABASE.getLongValue())))
        {
            message = PropertiesFactory.getValueString(ChineseCharacter.ERROR_CONNECT_DB);
        }

        return message;
    }

    public boolean isDataBaseManipulateException(String exceptionMessage)
    {
        boolean isManipulate = true;
        if (this.filterActions != null && this.filterActions.length != 0)
        {
            for (String action : this.filterActions)
            {
                if (exceptionMessage.contains(action.trim()))
                {
                    isManipulate = false;
                    break;
                }
            }
        }

        return isManipulate;
    }

    public String pareseExceptionMessage(Exception e)
    {

        StringBuilder buffer = new StringBuilder();
        String message = e.toString();

        int start = message.indexOf("java.sql.SQLException");
        int end = message.indexOf("at", 5000);
        if (start != -1 && end != -1)
        {
            buffer.append(message.substring(start, end));
        }
        else if (start != -1 && end == -1)
        {
            buffer.append(message.substring(start));
        }
        else
        {
            buffer.append(e.getMessage());
        }

        StackTraceElement[] elements = e.getStackTrace();
        for (int i = 0, size = elements.length; i < 5 && i < size; i++)
        {
            StackTraceElement element = elements[i];
            buffer.append("\n<br/>");
            buffer.append("at&nbsp;&nbsp;&nbsp;&nbsp;");
            buffer.append(element);
        }

        return buffer.toString();
    }

    protected String getConnectDatabaseErrorMessage()
    {
        StringBuilder buffer = new StringBuilder();
        buffer.append("DHM.Studio-");
        buffer.append(Constants.ERROR_CODE_CONNECT_DATABASE.getLongValue());
        buffer.append(":");
        buffer.append(this.errorCodeProvider.getText(Constants.MSG_PORTALMS + "cant.connected.db.message"));

        return buffer.toString();
    }

    /**
     * 根据异常对象,来判断该异常是否是由于无法连接到数据库产生的。
     *
     * @param e
     *            异常对象
     * @return 返回true表示能够正常连接到数据库,返回false无法连接到数据库
     *
     * @return boolean 返回true表示能够正常连接到数据库,返回false无法连接到数据库
     */
    protected boolean isValidConnection(Exception e)
    {
        if (e == null)
        {
            return true;
        }
        boolean isValid = true;
        String message = e.toString();
        String cantConnectedMessage = this.errorCodeProvider.getText(Constants.MSG_PORTALMS
                + "cant.connected.db.message");
        if (message != null && message.contains(cantConnectedMessage))
        {
            isValid = false;
        }

        return isValid;
    }

    /**
     * 根据操作码解析出操作内容,并将操作内容解析成LogMessageBean对象
     *
     * @return 返回解析的LogMessageBean对象
     *
     * @return LogMessageBean 返回解析的LogMessageBean对象
     */
    protected LogMessageBean parseOperateContentAsLogMessageBean()
    {
        if (logMessageId == null)
        {
            return null;
        }

        String messageContent = this.operateCodePrivider.getText(Constants.MSG_PORTALMS
                + Constants.OPERATE_CODE_PREFIX.getStringValue() + String.valueOf(logMessageId));
        return LogMessageParser.convert2LogMessageBean(messageContent);
    }

    /**
     * 创建日志实体,日志实体的信息来源与日志配置文件。
     * 根据操作码,从LogDefinitions.properties读取操作信息,并将信息封装成LogMessageBean对象。
     *
     * @param userId
     *            登录用户的ID号
     * @param ipAddr
     *            客户端请求的IP地址
     * @return LogMessageBean对象,封装具体的操作信息。
     *
     * @return com.xxxx.dhm.portalMS.log.entity.BusinessLog
     *         LogMessageBean对象,封装具体的操作信息
     */
    protected com.xxxx.dhm.portalMS.log.entity.BusinessLog createLog(int userId, String ipAddr)
    {
        if (logMessageId == null && userId != 0)
        {
            return null;
        }

        String messageContent = this.operateCodePrivider.getText(Constants.MSG_PORTALMS
                + Constants.OPERATE_CODE_PREFIX.getStringValue() + String.valueOf(logMessageId));
        LogMessageBean messageBean = LogMessageParser.convert2LogMessageBean(messageContent);
        if (messageBean != null)
        {
            com.xxxx.dhm.portalMS.log.entity.BusinessLog log = LogMessageParser.createLog(userId, ipAddr,
                    logMessageId, messageContent, this.operateDesc);

            return log;
        }
        else
        {
            sysLogger.warn("there is no '" + logMessageId + "' define in LogDefinitions.xml");
            return null;
        }
    }

    /**
     * 获取语言
     */
    public Locale getLocale()
    {
        ActionContext ctx = ActionContext.getContext();
        if (ctx != null)
        {
            return ctx.getLocale();
        }
        else
        {
            sysLogger.debug("Action context not initialized");
            return null;
        }
    }

    public String getLogMessageId()
    {
        return logMessageId;
    }

    public void setLogMessageId(String logMessageId)
    {
        this.logMessageId = logMessageId;
    }

    public int getErrorCode()
    {
        return errorCode;
    }

    public void setErrorCode(int errorCode)
    {
        this.errorCode = errorCode;
    }

    public String getIncludeParams()
    {
        return includeParams;
    }

    public void setIncludeParams(String includeParams)
    {
        this.includeParams = includeParams;
    }

    public String[] getFilterActions()
    {
        String[] temp = filterActions;
        return temp;
    }

    public void setFilterActions(String[] filterActions)
    {
        this.filterActions = (String[]) filterActions.clone();
    }

    public String getExcludeClass()
    {
        return excludeClass;
    }

    public void setExcludeClass(String excludeClass)
    {
        this.excludeClass = excludeClass;
    }
}



/*
 * 工程名: XXXXX
 * 包     名: com.XXX.dhm.portalMS.base.web.interceptor
 * 文 件名: ExTokenInterceptor.java
 * 版      权: Copyright (c) 2009 Coship All Rights Reserved.
 * 描      述: 防止重复提交的拦截器
 * 修 改 人:
 * 修改时间:
 * 跟踪单号:
 * 修改单号:
 * 修改内容:
 */
package com.XXXX.dhm.portalMS.base.web.interceptor;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.interceptor.TokenInterceptor;
import org.apache.struts2.util.TokenHelper;

import com.XXX.dhm.portalMS.common.Constants;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ValidationAware;
import com.opensymphony.xwork2.util.LocalizedTextUtil;

/**
 *
 * 防止重复提交的拦截器,通过扩展了struts2的重复提交的拦截器。
 * 该拦截器的使用如下:
 *     1、不进行重复提交校验,那么该拦截器将不做任何拦截操作。
 *  2、如果进行重复提交校验,需要做的操作如下:
 *     1)、在jsp页面中form中加入:<s:token />,那么就进行重复提交的校验操作。
 *     2)、校验的规则是这样的,首先会重http请求参数中获取该token的值,然后再从
 *     session中获取同名的token值,然后两个值进行比较。
 *     3)、如果两个一样,则校验通过,否则struts2将会将跳转到“input”配置的页面去。
 *     
 *
 *
 * @version
 * @since  
 */
public class ExTokenInterceptor extends TokenInterceptor {

    /**
     * 序列化ID
     */
    private static final long serialVersionUID = -6688464557536335754L;
    /**日志记录对象*/
    private static final Logger logger = Logger.getLogger( ExTokenInterceptor.class );
    
    @Override
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("Intercepting invocation to check for valid transaction token.");
        }

        HttpSession session = ServletActionContext.getRequest().getSession(true);

        synchronized (session) {
            String tokenName = getTokenName();
            if( tokenName == null ) {
                return handleValidToken(invocation);
            }
            
            if (!validToken()) {
                return handleInvalidToken(invocation);
            } else {
                return handleValidToken(invocation);
            }
        }
    }
    
    @Override
    protected String handleInvalidToken(ActionInvocation invocation) throws Exception {
        Object action = invocation.getAction();
        String errorMessage = LocalizedTextUtil.findText(this.getClass(), Constants.MSG_PORTALMS+"struts.messages.invalid.token",
                invocation.getInvocationContext().getLocale(),
                "The form has already been processed or no token was supplied, please try again.", new Object[0]);

        if (action instanceof ValidationAware) {
            ((ValidationAware) action).addActionError(errorMessage);
        } else {
            logger.warn(errorMessage);
        }

        return Action.INPUT;
    }
    
    public static boolean validToken() {
        String tokenName = TokenHelper.getTokenName();
        if ( tokenName == null ) {
            return true;
        }

        String token = TokenHelper.getToken(tokenName);

        if (token == null) {
            return false;
        }

        Map<String, Object> session = ActionContext.getContext().getSession();
        String sessionToken = (String) session.get(tokenName);

        if (!token.equals(sessionToken)) {
            logger.warn(LocalizedTextUtil.findText(TokenHelper.class, "struts.internal.invalid.token", ActionContext.getContext().getLocale(), "Form token {0} does not match the session token {1}.", new Object[]{
                    token, sessionToken
            }));
            
            return false;
        }
        
        // remove the token so it won't be used again
        session.remove(tokenName);
        return true;
    }
    
    public static String getTokenName() {
        Map<String, Object> params = ActionContext.getContext().getParameters();

        if (!params.containsKey( "struts.token.name" )) {
            return null;
        }

        String[] tokenNames = (String[]) params.get("struts.token.name");
        String tokenName;

        if ((tokenNames == null) || (tokenNames.length < 1)) {
            return null;
        }

        tokenName = tokenNames[0];

        return tokenName;
    }
}


/*
 * 工 程 名:  XXX
 * 包       名:  com.XXXX.dhm.portalMS.base.web.interceptor
 * 文 件 名:  SiteInterceptor.java
 * 版       权:  Copyright (c) 2010 XXXX All Rights Reserved.
 * 描       述:  <描述>
 * 修 改 人:  
 * 修改时间:  
 * 跟踪单号:  <跟踪单号>
 * 修改单号:  <修改单号>
 * 修改内容:  <修改内容>
 */

package com.XXX.dhm.portalMS.base.web.interceptor;

import java.util.Map;

import org.apache.struts2.ServletActionContext;

import com.XXX.dhm.portalMS.common.Constants;
import com.XXXX.dhm.portalMS.common.util.SessionUtil;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

/**
 * ClassName:SiteInterceptor
 * Function: TODO ADD FUNCTION
 * Reason:     TODO ADD REASON
 *
 * @author   903881
 * @version  V200R001
 * @since    DHM.Core.portalMS-V200R002
 * @Date     2010-6-18        下午02:37:22
 *
 * @see     
 */
/**
 * 站点拦截器,用来设置每个请求时的siteID,当用户触发一个Action, 该拦截器从当前用户的Session中取出
 * 正在操作的siteID并将其绑定到当前操作线程
 *
 * @author  903881
 * @version  [V200R002, 2010-6-18]
 * @see  [相关类/方法]
 * @since  [DHM.Core.portalMS-V200R002]
 */
public class SiteInterceptor extends AbstractInterceptor {

    /**
     * 注释内容
     */
    private static final long serialVersionUID = -7194853807889275168L;

    @Override
    public String intercept(ActionInvocation invoke) throws Exception {
        Map<String, Object> session = ServletActionContext.getContext().getSession();
        Long siteID = (Long)session.get(Constants.CURRENT_SITE_ID.getStringValue());
        if(siteID != null){
            SessionUtil.setLocalSiteID(siteID);
        }
        return invoke.invoke();
    }

}

<s:form  theme="simple" validate="true" action="addApplication" namespace="/application" >
<s:token/>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值