扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
在后一种情况下,异常发生在EJB容器的边缘并且通过RMI连接传递到客户端。必须注意不要传送任何属于服务端类的异常(如来自对象关系映射框架这类的)到客户端。而由EJB异常处理器通过使用SerializableException作为中介来处理这个问题。在客户端,顶层的Swing异常处理器捕获其他未处理的错误并采取相应措施。
异常处理框架
在Rampart框架中异常处理器是一个实现了ExceptionHandler接口的类。这个接口仅有一个包含两个参数(待处理的Throwable和当前的Thread)的方法。方便起见,框架提供了包含基本的实现类ExceptionHandlerBase,他辨别Throwable并将其代理给RuntimeException, Error, Throwable和Rampart框架的Unrecoverable的特定的抽象方法来处理。子类提供这些方法的实现并区别处理。
下面的类图显示了异常处理器的层次和三个缺省的异常处理器。
许多人认为SUN应该在每应用的基础上给J2EE框架内置插入所有容器的钩子。这样就允许自定义错误处理方案、安全及更多可安装的功能,而不需要依赖特定厂商的方案和框架。不幸地是,SUN并没有在EJB规范中提供这样的机制。既然如此,我们只有拿出AOP这个强有力的工具来增加异常处理。我们选择的AspectWerkz框架,可以如下使用方面:
public class EJBExceptionHandler implements AroundAdvice {
private ExceptionHandler handler;
public EJBExceptionHandler() {
handler = ConfigHelper.getEJBExceptionHandler();
}
public Object invoke(JoinPoint joinPoint) throws Throwable {
Log log = LogFactory.getLog(joinPoint.getEnclosingStaticJoinPoint().getClass().getName());
log.debug("EJB Exception Handler bean context aspect!!");
try {
return joinPoint.proceed();
} catch (RuntimeException e) {
handler.handle(Thread.currentThread(), e);
} catch (Error e) {
handler.handle(Thread.currentThread(), e);
}
return null;
}
}
实际的处理器是通过ConfigHelper类来配置和获取的。如果RuntimeException 或者Error在业务逻辑处理过程被抛出时,处理器就会被请求处理了。
DefaultEJBExceptionHandler序列化任何并非来自SUN核心包异常的堆栈信息到专门的SerializableException中,这从好的方面来看,可以将在远程客户端不存在的类的异常的堆栈以任意方式传播,另一方面,这会丢失原始的异常。
如果客户端是远程的,EJB容器忠实地捕获RuntimeException或Error并将他包在java.rmi.RemoteException中,否则使用javax.ejb.EJBException。为了在最低程度保持来源的精确性及堆栈信息,框架在BusinessDelegates剥离传送异常并重新抛出原始异常。
Rampart框架的BusinessDelegate类提供一个EJB无关的接口给客户端,而在内部包含本地或远程的EJB接口。BusinessDelegate类从EJB实现类中用XDoclet生成的,他遵循图4中UML图结构:
BusinessDelegate提供所有来自源EJB实现类的业务方法并代理给相应的LocalProxy或RemoteProxy类。在内部两个代理类处理EJB相关的异常,从而隐藏了BusinessDelegate的实现细节。下面的代码是来自某个LocalProxy类的方法:
public java.lang.String someOtherMethod() {
try {
return serviceInterface.someOtherMethod();
} catch (EJBException e) {
BusinessDelegateUtil.throwActualException(e);
}
return null; // Statement is never reached
}
serviceInterface变量代表EJB本地接口。任何被容器抛出的EJBException实例意味着一个未知错误被BusinessDelegateUtil类捕获和处理,如下面发生的操作:
public static void throwActualException(EJBException e) {
doThrowActualException(e);
}
private static void doThrowActualException(Throwable actual) {
boolean done = false;
while(!done) {
if(actual instanceof RemoteException) {
actual = ((RemoteException)actual).detail;
} else if (actual instanceof EJBException) {
actual = ((EJBException)actual).getCausedByException();
} else {
done = true;
}
}
if(actual instanceof RuntimeException) {
throw (RuntimeException)actual;
} else if (actual instanceof Error) {
throw (Error)actual;
}
}
actual异常被摘出并重新被抛出给顶层的客户端异常处理器。当异常到达处理器时,堆栈信息会是来自服务端且包含实际错误的原始异常。没有多余的客户端信息被附加。
Swing异常处理器
JVM为每一个控制线程提供了缺省的顶层异常处理器。在异常发生时,处理器输出Error或RuntimeException的堆栈信息到System.err并且结束线程。这种处理行为与用户的要求相差很远而且从调试的观点来看也不是很优雅。我们需要一种机制在保存堆栈信息和为以后调试准备的唯一请求ID的同时允许通知用户。“创建基于J2EE的应用范围用户会话”描述了如何在所有层都可以形成这样的请求ID。
J2SE1.4以前的版本,在Thread实例中未捕获的异常将导致其所在的ThreadGroup的uncaughtException()方法被执行。在应用中控制异常处理的简单方法是继承ThreadGroup类,重写uncaughtException()方法,并且确认所有Thread在自定义的ThreadGroup类的实例中启动。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者