Alfresco中的错误机制处理

来源:互联网 发布:编程一小时怎么登陆 编辑:程序博客网 时间:2024/05/19 22:26

Alfresco的错误机制是值得借鉴和学习的!

Alfresco的错误主要分为如下几点:

1.面向表现层 

Alfresco 面向表现层的处理,只要是向开发人员和用户呈现错误信息和引起错误的原因!实际上大多数错误机制的处理也就包含这两个层面的意思。通过如下代码我们可以看到Alfresco是如何配置错误处理页面的:

web.xml:

<error-page>
  
<exception-type>java.lang.Exception</exception-type>
  
<location>/jsp/error.jsp</location>
 
</error-page>

在Java中,java.lang.Exception是所有错误或异常的基类,其又继承了java.lang.Throwable这个超类,而所谓超类是指:只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。

由上配置可以看出,当页面访问出错时都会转向到error.jsp,这样就可以给用户一个统一的异常处理界面,看看error.jsp:

 

<%--
* Copyright (C) 2005-2007 Alfresco Software Limited.

* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.

* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.

* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception.  You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
--
%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %>

<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %>
<%@ page isELIgnored="false" %>
<%@ page import="org.alfresco.web.app.Application" %>

<r:page titleId="title_error">

<table cellspacing="0" cellpadding="2" width="100%">
<tr>
<%-- Top level toolbar and company logo area --%>
<td width=100%>
<table cellspacing="0" cellpadding="0" width="100%">
<tr>
<td><href="http://www.alfresco.org" target="new"><img src="<%=request.getContextPath()%>/images/logo/AlfrescoLogo32.png" width=32 height=30 alt="Alfresco" title="Alfresco" border=0 style="padding-right:4px"></a></td>
<td><img src="<%=request.getContextPath()%>/images/parts/titlebar_begin.gif" width="10" height="30"></td>
<td width=100% style="background-image: url(<%=request.getContextPath()%>/images/parts/titlebar_bg.gif)">
<span class="topToolbarTitle"><%=Application.getMessage(session, "system_error")%></span>
</td>
<td><img src="<%=request.getContextPath()%>/images/parts/titlebar_end.gif" width="8" height="30"></td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<r:systemError styleClass="errorMessage" detailsStyleClass="mainSubTextSmall" showDetails="false" />
</td>
</tr>
</table>

</r:page>

此代码中主要内容为:

 

<r:systemError styleClass="errorMessage" detailsStyleClass="mainSubTextSmall" showDetails="false" />

这是由标签库封装了的表现层技术,它主要完成了如下信息处理:

  • 错误信息是什么
  • 引起错误信息的原因是什么

核心代码:

 

public int doStartTag() throws JspException
   
{
      String errorMessage 
= "No error currently stored";
      String errorDetails 
= "No details";
      
      
// get the error details from the bean, this may be in a portlet
      
// session or a normal servlet session.
      ErrorBean errorBean = null;
      RenderRequest renderReq  
= (RenderRequest)pageContext.getRequest().
                                   getAttribute(
"javax.portlet.request");
      
if (renderReq != null)
      
{
         PortletSession session 
= renderReq.getPortletSession();
         errorBean 
= (ErrorBean)session.getAttribute(ErrorBean.ERROR_BEAN_NAME);
      }

      
else
      
{
         errorBean 
= (ErrorBean)pageContext.getSession().
                        getAttribute(ErrorBean.ERROR_BEAN_NAME);
      }


      
if (errorBean != null)
      
{
         errorMessage 
= errorBean.getLastErrorMessage();
         errorDetails 
= errorBean.getStackTrace();
      }

      
else
      
{
         
// if we reach here the error was caught by the declaration in web.xml so
         
// pull all the information from the request and create the error bean
         Throwable error = (Throwable)pageContext.getRequest().getAttribute("javax.servlet.error.exception");
         String uri 
= (String)pageContext.getRequest().getAttribute("javax.servlet.error.request_uri");
         
         
// create and store the ErrorBean
         errorBean = new ErrorBean();
         pageContext.getSession().setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean);
         errorBean.setLastError(error);
         errorBean.setReturnPage(uri);
         errorMessage 
= errorBean.getLastErrorMessage();
         errorDetails 
= errorBean.getStackTrace();
      }

      
      
try
      
{
         Writer out 
= pageContext.getOut();
         
         ResourceBundle bundle 
= Application.getBundle(pageContext.getSession());
         
         out.write(
"<div");
         
         
if (this.styleClass != null)
         
{
            out.write(
" class='");
            out.write(
this.styleClass);
            out.write(
"'");
         }

         
         out.write(
">");
         out.write(errorMessage);
         out.write(
"</div>");
         
         
// work out initial state
         boolean hidden = !this.showDetails; 
         String display 
= "inline";
         String toggleTitle 
= "Hide";
         
if (hidden)
         
{
            display 
= "none";
            toggleTitle 
= "Show";
         }

         
         
// output the script to handle toggling of details
         out.write("<script language='JavaScript'> ");
         out.write(
"var hidden = ");
         out.write(Boolean.toString(hidden));
         out.write(
"; ");   
         out.write(
"function toggleDetails() { ");
         out.write(
"if (hidden) { ");
         out.write(
"document.getElementById('detailsTitle').innerHTML = '");
         out.write(bundle.getString(MSG_HIDE_DETAILS));
         out.write(
"<br/><br/>'; ");
         out.write(
"document.getElementById('details').style.display = 'inline'; ");
         out.write(
"hidden = false; ");
         out.write(
"} else { ");
         out.write(
"document.getElementById('detailsTitle').innerHTML = '");
         out.write(bundle.getString(MSG_SHOW_DETAILS));
         out.write(
"'; ");
         out.write(
"document.getElementById('details').style.display = 'none'; ");
         out.write(
"hidden = true; ");
         out.write(
"} } </script> ");
         
         
// output the initial toggle state
         out.write("<br/>");
         out.write(
"<a id='detailsTitle' href='javascript:toggleDetails();'>");
         out.write(toggleTitle);
         out.write(
" Details</a>");
         
         out.write(
"<div style='padding-top:5px;display:");
         out.write(display);
         out.write(
"' id='details'");
         
         
if (this.detailsStyleClass != null)
         
{
            out.write(
" class='");
            out.write(
this.detailsStyleClass);
            out.write(
"'");
         }

         
         out.write(
">");
         out.write(errorDetails);
         out.write(
"</div>");
         
         
// output a link to return to the application
         out.write(" <div style='padding-top:16px;'><a href='");
      
         
if (Application.inPortalServer())
         
{
            RenderResponse renderResp  
= (RenderResponse)pageContext.getRequest().getAttribute(
                  
"javax.portlet.response");
            
if (renderResp == null)
            
{
               
throw new IllegalStateException("RenderResponse object is null");
            }

            
            PortletURL url 
= renderResp.createRenderURL();
            
// NOTE: we don't have to specify the page for the portlet, just the VIEW_ID parameter
            
//       being present will cause the current JSF view to be re-displayed
            url.setParameter("org.apache.myfaces.portlet.MyFacesGenericPortlet.VIEW_ID""current-view");
            out.write(url.toString());
         }

         
else
         
{
            String returnPage 
= null;
            
            
if (errorBean != null)
            
{
               returnPage 
= errorBean.getReturnPage();
            }

            
            
if (returnPage == null)
            
{
               out.write(
"javascript:history.back();");
            }

            
else
            
{
               out.write(returnPage);
            }

         }

         
         out.write(
"'>");
         out.write(bundle.getString(MSG_RETURN_TO_APP));
         out.write(
"</a></div>");
                   
         
// use External Access Servlet to generate a URL to relogin again
         
// this can be used by the user if the app has got into a total mess
         if (Application.inPortalServer() == false)
         
{
            out.write(
" <div style='padding-top:16px;'><a href='");
            out.write(((HttpServletRequest)pageContext.getRequest()).getContextPath());
            out.write(ExternalAccessServlet.generateExternalURL(
"logout"null));
            out.write(
"'>");
            out.write(bundle.getString(MSG_LOGOUT));
            out.write(
"</a></div>");
         }

      }

      
catch (IOException ioe)
      
{
         
throw new JspException(ioe);
      }

      
finally
      
{
         
// clear out the error bean otherwise the next error could be hidden
         pageContext.getSession().removeAttribute(ErrorBean.ERROR_BEAN_NAME);
      }

      
      
return SKIP_BODY;
   }

上面两个信息分别由属性errorMessage和errorDetails的显示的。他们又是从哪里取得的呢?答案是ErrorBean,此类封装用来如何取得错误或异常的信息,如下代码:

 

/**
    * 
@return Returns the last error to occur in string form
    
*/

   
public String getLastErrorMessage()
   
{
      String message 
= "No error currently stored";
      
      
if (this.lastError != null)
      
{
         StringBuilder builder 
= new StringBuilder(this.lastError.toString());;
         Throwable cause 
= this.lastError.getCause();
         
         
// build up stack trace of all causes
         while (cause != null)
         
{
            builder.append(
" caused by: ");
            builder.append(cause.toString());
            
            
if (cause instanceof ServletException && 
                  ((ServletException)cause).getRootCause() 
!= null)
            
{
               cause 
= ((ServletException)cause).getRootCause();
            }

            
else
            
{
               cause 
= cause.getCause();
            }
  
         }

         
         message 
= builder.toString();
         
         
// format the message for HTML display
         message = message.replaceAll("<""&lt;");
         message 
= message.replaceAll(">""&gt;");
         message 
= message.replaceAll(" ""<br/>");
      }

      
      
return message;
   }

   
   
/**
    * 
@return Returns the stack trace for the last error
    
*/

   
public String getStackTrace()
   
{
      String trace 
= "No stack trace available";
      
      
if (this.lastError != null)
      
{
         StringWriter stringWriter 
= new StringWriter();
         PrintWriter writer 
= new PrintWriter(stringWriter);
         
this.lastError.printStackTrace(writer);
         
         
// format the message for HTML display
         trace = stringWriter.toString();
         trace 
= trace.replaceAll("<""&lt;");
         trace 
= trace.replaceAll(">""&gt;");
         trace 
= trace.replaceAll(" ""<br/>");
      }

      
      
return trace;
   }

其抛出了最上层的错误和错误堆栈的全部信息。

 

2.面向业务逻辑层

在业务逻辑处理中,Alfresco使用了大量的servlet来处理,如何铺获那些异常呢,看上面的问题,即如何给errorMessage和errorDetails以对应的信息呢?Alfresco 处理servlet抛出的错误或异常直接是引用了java.lang.Throwable这个超类,如下:

 

protected void service(HttpServletRequest request, HttpServletResponse response)
      
throws ServletException, IOException
   
{
      String uploadId 
= null;
      String returnPage 
= null;
      
final RequestContext requestContext = new ServletRequestContext(request);
      
boolean isMultipart = ServletFileUpload.isMultipartContent(requestContext);
      
      
try
      
{
      
//.......
      }

      
catch (Throwable error)
      
{
         Application.handleServletError(getServletContext(), (HttpServletRequest)request,
                                        (HttpServletResponse)response, error, logger, returnPage);
      }

看到了吧,对于servlet处理错误异常是利用了Application.handleServletError这个方法来实现的,这个方法里面做什么呢

 

public static void handleServletError(ServletContext servletContext, HttpServletRequest request,
         HttpServletResponse response, Throwable error, Log logger, String returnPage)
      
throws IOException, ServletException
   
{
      
// get the error bean from the session and set the error that occurred.
      HttpSession session = request.getSession();
      ErrorBean errorBean 
= (ErrorBean)session.getAttribute(ErrorBean.ERROR_BEAN_NAME);
      
if (errorBean == null)
      
{
         errorBean 
= new ErrorBean();
         session.setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean);
      }

      errorBean.setLastError(error);
      errorBean.setReturnPage(returnPage);
      
      
// try and find the configured error page
      boolean errorShown = false;
      String errorPage 
= getErrorPage(servletContext);
      
      
if (errorPage != null)
      
{
         
if (logger.isDebugEnabled())
            logger.debug(
"An error has occurred, redirecting to error page: " + errorPage);
         
         
if (response.isCommitted() == false)
         
{
            errorShown 
= true;
            response.sendRedirect(request.getContextPath() 
+ errorPage);
         }

         
else
         
{
            
if (logger.isDebugEnabled())
               logger.debug(
"Response is already committed, re-throwing error");
         }

      }

      
else
      
{
         
if (logger.isDebugEnabled())
            logger.debug(
"No error page defined, re-throwing error");
      }

      
      
// if we could not show the error page for whatever reason, re-throw the error
      if (!errorShown)
      
{
         
if (error instanceof IOException)
         
{
            
throw (IOException)error;
         }

         
else if (error instanceof ServletException)
         
{
            
throw (ServletException)error;
         }

         
else
         
{
            
throw new ServletException(error);
         }

      }

   }

到这里就应该明白了吧!它把错误信息赋值给lastError这个Throwable对象,然后通过getLastErrorMessage()和getStackTrace()类取得错误或异常了!

 

3.错误信息属性文件

由于本地化实现要求,Alfreso封装了部分错误信息到属性文件,这些属性分散在配置文件夹${Alfresco}/WEB-INF/classes/alfreso/messages下面!

原创粉丝点击