内容


Aspects for MDD

Rational Software Architect中的基于方面的追踪与首次失败数据捕获

方面能力的权衡

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: Aspects for MDD

敬请期待该系列的后续内容。

此内容是该系列的一部分:Aspects for MDD

敬请期待该系列的后续内容。

介绍

Aspects for MDD是一个框架,它使得建模和开发人员可以直接在IBM® Rational®Software Architect(下文称为 RSA)中应用AspectJ代码,而不再需要学习任何关于方面的技术知识。

这篇文章介绍了Aspects for MDD的com.ibm.aspectj.lib库中的两个方面,追踪首次失败数据捕获。追踪使用的是Java1.4或者更高版本的Java™ Developer Kit (JDK™)中的日志工具。不论是最初的开发人员,或者是服务工程师,再或者是最终用户,追踪通常都是用在问题诊断中。追踪的结果会输出到一个文本文件或者终端窗口上,其中会列出程序中运行的入口和出口方法。通过阅读追踪信息的文件,可以缩小运行程序代码的出错范围。我们版本的追踪是可配置的,并且可以把追踪结果输出到终端、文本文件,或者两者。

首次错误数据捕获(FFDC)也是用来做问题诊断的。当你的应用程序发生一个错误或者抛出一个异常时,FFDC可以确保这个错误或者异常被正确的处理,并且任何和这个错误相关的信息都将会被保存下来。在一个应用程序中使用FFDC可以减少我们诊断和修复一个问题的时间。除此之外,使用一个方面实现FFDC可以确保没有错误或者异常未被发现,如果手动诊断的话这个问题常常发生。

阅读这篇文章和开始使用Aspects for MDD是不需要AOP相关知识的。当然,一些基本的知识能够帮助你更好的阅读这篇文章的表面之下一节。虽然使用Software Architect的经验不是必须的,但是它可以帮助你更好的理解这篇文章。

在阅读这篇文章之后,你应该对追踪和FFDC适用的环境有个很好的理解。你需要安装 Aspects for MDD,然后应用追踪和FFDC到你的应用程序。你应该对方面是如何工作的有一些理解。

安装

在进行这篇文章之前,你应该已经安装好了Rational Software Architect v6.0.1,并且还需要做以下准备工作:

  • 安装AspectJ Development Tools (AJDT) for Eclipse 3.0
  • 安装Aspects for MDD Framework
  • 建立一个样例模型

介绍如何安装AJDT for Eclipse 3.0的文章可以在AJDT主页上找到,下面是主页的链接(查看参考资源)。查看下载Aspects for MDD Framework for Rational Architect的参考资源

你将要用到的样例模型是一个简单的HelloWorld程序,和所有的HelloWorld程序一样,这个程序是把"Hello World"输出到屏幕。由于这篇文章将告诉你如何应用FFDC方面后,我们可以使用一个方法,它的作用是抛出RuntimeException异常并且在屏幕上打印"Goodbye World"。

建立这个简单的样例模型:

  • 建立一个新的AspectJ Project,叫做aj_project(File > New > Other > AspectJ > AspectJ Project)
  • 在这个新的项目中,建立一个新的UML模型。右键单击aj_project项目并选择New > UML Model
  • 将名字改为SimpleModel其他属性保持默认,然后点击Finish
  • 在这个模型中,通过右键点击模型并选择Add UML > Class,来建立一个新的HelloWorld类。
  • 在UML类中,通过右键点击HelloWorld图表然后选择New UML > operation来建立4个操作,它们是main,sayHello,sayGoodbye和throwingMethod。
  • 在Properties视图中改变刚建立的操作的属性,确保main是静态的,sayHello和sayGoodbye返回字符串。
  • 通过右键点击刚在模型中建立的类,并选择Transform > UML to Java来把UML转变成Java程序。

完成这些操作后,打开编辑器中的HelloWorld类,把列表1中的代码输入进去。

列表1:HelloWorld类
public class HelloWorld {

    public static void main(String[] args) {
           System.out.println("Starting");
           HelloWorld main = new HelloWorld();
           System.out.println( main.sayHello() );
           System.out.println( main.sayGoodbye() );
           System.out.println( "Finishing" );
    }

    public String sayHello() {
           return "Hello World";
    }

    public String sayGoodbye() {
           try {
                  throwingMethod();
           } catch (Exception e) {
           }
           return "Goodbye World";
    }

    public void throwingMethod() {
           throw new RuntimeException();
    }

}

在进行后面的工作前,请先按照下列步骤运行这段程序:

  1. 选择Run > Run
  2. 在左边的菜单中,选择Java Application,然后点击New
  3. 在右边的Main标签上,选择HelloWorld
  4. 选择OK

结果被输出到终端上,如列表2所示:

列表2:HelloWorld应用程序的输出结果
Starting
Hello World
Goodbye World
Finishing

现在你已经做好了应用追踪和FFDC方面的准备。

使用追踪方面

在这个章节中,你将会学到如何把追踪应用到你的程序中。首先你将会把它应用到安装章节中开发的简单的模型中。稍后这篇文章将会讨论在应用程序中应用追踪的不同的可能性。最后,我们将会在表面之下讨论追踪方面以及它是如何工作的。

把追踪应用到样例模型中

使用在安装章节讨论的样例测试模型,下面你将会把java.util.logging应用到sayHello()方法中。请跟着下面的步骤操作:

  1. 在图表中,右键单击sayHello()
  2. 从中选择Aspect Lib
  3. 在Aspect Specification对话框中,从AspectJ Library Aspects种类中选择JDK14Tracing
  4. 选择OK

你会发现在操作旁边有一个<<jdk14tracing>>,如图1所示:

图1:sayHello模型元素中的jdk14tracing
 jdk14tracing stereotype
  1. 现在,通过右键点击图表中的Transform > Run Transformation > UML to Java,将UML转换成Java程序。

如果你在查看Package Explorer(如果没有显示这项,请选择Window > Show View > Package Explorer),你会发现一个叫做HelloWorldJDK14Tracing.aj的文件被建立了,如图2所示:

图2:HelloWorldJDK14Tracing.aj文件
The generated file
The generated file

稍后你将会更加详细的查看这个文件。

为了运行HelloWorld这个程序,java.util.logging需要logging.properties文件的建立(关于java.util.logging的更多资料请查看参考资源)。请参考以下步骤建立logging.properties配置文件:

  1. 在包浏览器中选择项目。
  2. 选择File > New > File
  3. 输入logging.properties作为名称。
  4. 选择Finish
  5. 打开文件,粘贴列表3种的内容到这个文件中。
  6. 保存文件。
列表3:logging.properties文件
############################################################
#      Logging Configuration File
############################################################


# Global properties

handlers= java.util.logging.ConsoleHandler
.level= INFO


# Handler specific properties.

java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter = 
    java.util.logging.SimpleFormatter

正如先前所提到的,我们可以把追踪结果输出到一个文本文件或者在终端显示。下一个章节如何在你的应用程序中应用追踪,将会告诉你如何通过改变logging.properties文件来达到此目的。

首先运行样例程序。如下步骤所示:

  1. 通过选择Run > Run打开你在安装章节最后的设置的运行配置。
  2. 找到Arguments标签,在VM Arguments文本框中输入 - Djava.util.logging.config.file=logging.properties
  3. 选择OK

列表4中的输出结果被显示到了终端:

列表4:应用追踪方面后的应用程序输出
Starting
? initLogger() logger=HelloWorld
? initLogger() traceEnabled=true
21-Oct-2005 16:28:57 HelloWorld sayHello
FINER: ENTRY
Hello World
Goodbye World
Finishing
21-Oct-2005 16:28:57 HelloWorld sayHello
FINER: RETURN Hello World

如何在你的应用程序中应用追踪

在介绍部分已经提到,追踪主要是用来做问题诊断。当你这么考虑问题时,追踪什么就会有很多不同的选择。首先,如果应用程序很小,你可能想要追踪所有的东西。这样可以帮助你在代码的未预料领域发现问题。但是,随着应用程序的扩大,这种完整的追踪方式的有效性降低了,因为它使得很多有用信息变得模糊。在大的应用程序中,你或许只想追踪有可能会发现问题的领域,或者除此之外你还想追踪你的API类。这就意味着如果一个问题发生在了一个客户端程序,那么你必须去确定谁的代码发生了错误。如果这个错误发生在你的代码中,那么查看API的哪个部分正在被使用,并且被怎样使用就是很有帮助的了。

在决定把追踪应用到你的应用程序之后,你或许想要配置一些你的追踪的细节信息。这是通过配置logging.properties文件完成的。

原始的logging.properties文件如之前的列表3所示。如果想要把追踪结果显示在终端同时输出到一个文本文件,那么就需要把配置文件handlers= java.util.logging.ConsoleHandler改成 handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler。如果只是把结果输出到一个文本文件中,那么就把它改成handlers= java.util.logging.FileHandler。稍后你需要为文件管理添加一些配置设置:

java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

更新后的logging.properties文件,如列表5所示:

列表5:更新后的logging.properties文件
############################################################
#      Logging Configuration File
############################################################


# Global properties

handlers = java.util.logging.FileHandler, 
    java.util.logging.ConsoleHandler
.level = INFO


# Handler specific properties.

java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter = 
    java.util.logging.SimpleFormatter
    
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = 
    java.util.logging.XMLFormatter

你或许还想为终端,文件或者个人的类配置日志级别。你选择的日志级别决定了在追踪中包括哪个事件。可用的级别有:

  • SEVERE
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST

SEVERE级别将会给你最少的信息,FINEST级别是最多的信息。除此之外,你还可以指定OFF来中断追踪或者选择ALL来包括所有事件。使用下列信息来指定级别:

  • Overall:.level= ...
  • The console:java.util.logging.ConsoleHandler.level = ...
  • The file:java.util.logging.FileHandler.level = ...
  • An individual class (for example, HelloWorld): HelloWorld.level = ...

注意:在你编写阶段,所有的输出都是默认的FINER级别。

想要了解更多的关于Java日志和如何配置日志的信息,请查看参考资源章节。

表面之下

Com.ibm.aspectj.lib方面库提供了一个包含抽象追踪方面的MDD方面,JDK14Tracing。 这个方面以及它的根源,追踪,包含所有的追踪执行逻辑,但是却没有包含应该追踪什么的信息。一个子方面需要提供这种信息,也就是pointcut的形式。如果你已经在部分UML模型中应用了一个方面,那么一个AspectJ方面将会作为UML转换成Java代码过程的一部分,建立在一个.aj文件中。在先前的例子中建立这个方面的过程如列表6所示:这是抽象的JDK14Tracing方面的一个具体的类型。如列表7所示:

请注意,这里我们为了使例子更加简明,所以省掉了导入语句,包的声明,版权声明,以及一些比较长的代码列表的部分内容。完整的代码在com.ibm.aspectj.lib库文献的aspectjLibsrc.zip文件中。

列表6:HelloWorldJDK14Tracing.aj
public aspect HelloWorldJDK14Tracing extends 
    org.aspectj.lib.tracing.JDK14Tracing {

    protected pointcut tracingScope () : 
        execution( * HelloWorld.sayHello() ) 
        || staticinitialization(HelloWorld);

}
列表7:JDK14Tracing.aj
public abstract aspect JDK14Tracing extends Tracing {

    /*
     * Sub-aspects implement to determine what to trace
     */
    protected abstract pointcut tracingScope ();

    /*
     * Use if() pointcut to efficiently determine 
     * when to trace
     */
    protected pointcut shouldTrace () :
        if(tracingEnabled) && tracingScope();
    
    private static boolean tracingEnabled = false;

    /*
     * Ensure logger is initialized for each class we are tracing
     */
    before(): staticinitialization(!Tracing+) && tracingScope() {
        initLogger(thisJoinPointStaticPart);
    }
    
    public final static Level ENABLED = Level.FINER;
    public final static Level DISABLED = Level.OFF;

    protected String getLoggerName (JoinPoint.StaticPart sjp) {
        return sjp.getSignature().getDeclaringTypeName();
    }

    protected void initLogger (JoinPoint.StaticPart sjp) {
        Logger logger = Logger.getLogger(getLoggerName(sjp));
        System.err.println("? initLogger() logger=" + 
                logger.getName());
        if (!tracingEnabled) {
            tracingEnabled = isTracingEnabled(logger);
        }
        System.err.println("? initLogger() traceEnabled=" + 
                tracingEnabled);
    }
    
    /*
     * From the Javadoc for Level: "By default logging calls for 
     * entering, returning, or throwing an exception are traced 
     * at this level."
     */
    private static boolean isTracingEnabled (Logger logger) {
        return logger.isLoggable(ENABLED);
    }
    
    protected void enter (JoinPoint jp) {
        Logger logger = Logger.getLogger(getLoggerName(
                jp.getStaticPart()));
        if (isTracingEnabled(logger)) {
            CodeSignature signature = 
                    (CodeSignature)jp.getSignature();
            logger.entering(signature.getDeclaringTypeName(),
                    signature.getName(),jp.getArgs());
        }
    }
    
    protected void enter (JoinPoint jp, Object obj) {
        Logger logger = Logger.getLogger(getLoggerName(
                jp.getStaticPart()));
        if (isTracingEnabled(logger)) {
            CodeSignature signature = 
                    (CodeSignature)jp.getSignature();
            logger.entering(signature.getDeclaringTypeName(),
                    signature.getName(),jp.getArgs());
        }
    }
    
    protected void exit (JoinPoint.StaticPart sjp) {
        Logger logger = Logger.getLogger(getLoggerName(sjp));
        if (isTracingEnabled(logger)) {
            CodeSignature signature = 
                    (CodeSignature)sjp.getSignature();
            logger.exiting(signature.getDeclaringTypeName(),
                    signature.getName());
        }
    }
    
    protected void exit (JoinPoint.StaticPart sjp, Object ret) {
        Logger logger = Logger.getLogger(getLoggerName(sjp));
        if (isTracingEnabled(logger)) {
            CodeSignature signature = 
                    (CodeSignature)sjp.getSignature();
            logger.exiting(signature.getDeclaringTypeName(),
                    signature.getName(),ret);
        }
    }
    
    protected void exception (JoinPoint.StaticPart sjp, 
            Exception ex) {            
        Logger logger = Logger.getLogger(getLoggerName(sjp));
        if (isTracingEnabled(logger)) {
            Signature signature = sjp.getSignature();
            logger.exiting(signature.getDeclaringTypeName(),
                    signature.getName(),ex);
        }
    }
}

JDK14Tracing继承另一个追踪方面,如列表8所示:

Listing 8. Tracing.aj
public abstract aspect Tracing {

    /*
     * Sub-aspects implement to determine what and when to trace
     */
    protected abstract pointcut shouldTrace ();

    private pointcut staticContext () : !this(Object);
    private pointcut nonStaticContext (Object obj) : this(obj);
    private pointcut voidMethod () : execution(void *(..));
    private pointcut objectMethod () : execution(* Object.*(..));

    /* 
     * Ensure we don't trace ourselves or any other sub-aspects
     */
    private pointcut excluded () : 
        objectMethod()
        || within(Tracing+);

    private pointcut tracedMethod () :
        execution(public * *(..))
        && !excluded();

    private pointcut tracedConstructor (Object obj) :
        execution(new(..))
        && this(obj)
        && !excluded()
        ;
    
    before (Object obj) : tracedMethod() && nonStaticContext(obj) 
            && shouldTrace() {
        enter(thisJoinPoint,obj);
    }
    
    before () : tracedMethod() && staticContext() && shouldTrace() {
        enter(thisJoinPoint);
    }
    
    after() returning() : tracedMethod()&& voidMethod() 
            && shouldTrace() {
        exit(thisJoinPointStaticPart);
    }
    
    after() returning(Object ret) : tracedMethod() && !voidMethod() 
            && shouldTrace() {
        exit(thisJoinPointStaticPart,ret);
    }
    
    after() throwing(Exception ex) : tracedMethod() 
            && shouldTrace() {
        exception(thisJoinPointStaticPart,ex);
    }
    
    before () : tracedConstructor(Object) && shouldTrace() {
        enter(thisJoinPoint);
    }
    
    after (Object obj) : tracedConstructor(obj) && shouldTrace() {
        exit(thisJoinPointStaticPart,obj);
    }
    
    /*
     * Template methods implemented by infrastructure-specific 
     * sub-aspects
     * e.g. Log4j to log data 
     */
    protected abstract void enter (JoinPoint jp, Object obj);
    
    protected abstract void enter (JoinPoint jp);
    
    protected abstract void exit (JoinPoint.StaticPart sjp);
    
    protected abstract void exit (JoinPoint.StaticPart sjp, 
            Object ret);
    
    protected abstract void exception (JoinPoint.StaticPart sjp, 
            Exception ex);

...

}

列表8中的追踪方面是一个通用的追踪方面,它提供了一个追踪的机制——Pointcuts和Advice的形式——但是并不包含任何特殊的追踪或者日志框架。它被(在列表7中)JDK14Tracing继承,它是通过使用java.util.logging框架的追踪来实现入口与出口模型方法的。这意味着,在将来其它的追踪实现也可以在工作台中使用(例如,基于Log4j的追踪),并且可以复用追踪的上级方面。

列表8的第6行中显示:追踪方面包含一个抽象的Pointcut,我们在列表9中再次显示这几行内容:

列表9:Tracing.aj中的一个Pointcut
    /*
     * Sub-aspects implement to determine what and when to trace
     */
    protected abstract pointcut shouldTrace ();

这在列表10中的HelloWorldJDK14Tracing方面已经构建成了具体形式。具体的实现描述了HelloWorld类中sayHello()方法的追踪的执行范围。staticinitalization pointcut是JDK141Tracing的实现工件。

列表10:HelloWorldJDK14Tracing.aj
public aspect HelloWorldJDK14Tracing extends 
    org.aspectj.lib.tracing.JDK14Tracing {

    protected pointcut tracingScope () : 
        execution( * HelloWorld.sayHello() ) 
        || staticinitialization(HelloWorld);

}

追踪方面包含了方法和构造器的 beforeafter Advice,这意味着入口和出口都被追踪了。列表11是这些条Advice的一个例子。Advice代表一个入口或者出口模型方法(或者异常模型方法)。这些方法在JDK14Tracing中执行,如列表12所示:

列表11:Tracing.aj中的Before advice
    before (Object obj) : tracedMethod() && nonStaticContext(obj) 
            && shouldTrace() {
        enter(thisJoinPoint,obj);
    }
列表12:JDK14Tracing.aj中的入口方法
    protected void enter (JoinPoint jp) {
        Logger logger = Logger.getLogger(getLoggerName(
                jp.getStaticPart()));
        if (isTracingEnabled(logger)) {
            CodeSignature signature = 
                    (CodeSignature)jp.getSignature();
            logger.entering(signature.getDeclaringTypeName(),
                    signature.getName(),jp.getArgs());
        }
    }

追踪方面除了一个通过我们产生的方面实现的抽象Pointcut外,还包含很多具体的Pointcut。如列表12所示:

列表13:Tracing.aj中的Pointcuts
    private pointcut staticContext () : !this(Object);
    private pointcut nonStaticContext (Object obj) : this(obj);
    private pointcut voidMethod () : execution(void *(..));
    private pointcut objectMethod () : execution(* Object.*(..));

第一个Pointcut用来定义静态方法的范围,第二个用来定义非静态方法的范围。第三个用来确定一个空返回类型。第四个Pointcut用来确定从java.lang.Object继承的方法的异常(例如,toString()hashcode()),为了防止递归的产生,这在追踪中是被禁止的。这些Pointcut过去经常被Advice用在不同的联合方式中,为了保证所有的方法都被正确的追踪。

使用FFDC方面

在这个章节中,你将会学些如何把FFDC应用到你的应用程序。首先,你要把它应用到之前建立的样例应用程序中,查看它是如何工作的。之后我们会讨论把FFDC应用到你的应用程序的可能性,最后,我们会在方面覆盖之下部分讨论它们是如何工作的。

在样例应用程序中应用FFDC方面

使用在安装章节建立的样例测试模型,把FFDC应用到sayGoodbye()方法中。这和使用追踪方面章节中在sayHello()方法中应用日志的方法类似。按照下列步骤将FFDC应用的模型应用程序:

  1. 在图表中,右键点击sayGoodbye()
  2. 从菜单中选择Aspect Lib
  3. 在Aspect Specification对话框中,从AspectJ Library Aspects中选择JDK14FFDC
  4. 选择OK

你会发现在操作旁边出现一个<<jdk14ffdc>>如图3所示:

图3:把jdk14ffdc应用到sayGoodbye模型元素
The jdk14ffdc stereotype
The jdk14ffdc stereotype

下一步,在图表中选择运行UML到Java代码转换类,右键点击它,找到Transform > Run Transformation > UML to Java

如果你使用的是Package Explorer,你会发现一个叫做HelloWorldJDK14FFDC.aj的文件被建立,如图4所示:

图4:新建立的HelloWorldJDK14FFDC.aj文件
The generated file
The generated file

稍后你会看到这个文件的详细信息。

现在我们可以运行应用程序来查看FFDC的应用情况。请注意在使用追踪方面章节中我们也是把它应用到了sayHello()方法中,因此当你运行应用程序时,会看到输出的追踪信息。由于最后运行的应用程序是HelloWorld,所以你只需点击工具栏上的Run按钮即可返回(或者找到Run > Run History > HelloWorld)。

和之前一样,输出结果被显示在终端上。在列表14中你会看到FFDC方面的输出结果。

列表14:FFDC和追踪方面的输出
Starting
? initLogger() logger=HelloWorld
? initLogger() traceEnabled=true
Hello World
Goodbye World
Finishing
21-Oct-2005 13:17:31 HelloWorld sayHello
FINER: ENTRY
21-Oct-2005 13:17:31 HelloWorld sayHello
FINER: RETURN Hello World
21-Oct-2005 13:17:31 org.aspectj.lib.logging.JDK14FFDC processNonStaticFFDC
WARNING: object=HelloWorld@400e699e, source=HelloWorld.sayGoodbye, line=25
java.lang.RuntimeException
    at HelloWorld.throwingMethod(HelloWorld.java:31)
    at HelloWorld.sayGoodbye(HelloWorld.java:24)
    at HelloWorld.main(HelloWorld.java:7)

如何在你的应用程序中应用FFDC

和在使用追踪方面章节讨论的追踪方面不同,你通常希望在整个应用程序中使用FFDC。它提供了一个兼容的错误处理机制,同时会确保在整个程序设计阶段不会丢失任何错误信息。使用 Aspects for MDD 框架,你可以使用同样的方法在类中应用追踪方面。

表面之下

如果你已经在部分UML模型中应用了方面,那么在.aj文件中会产生一个AspectJ方面,它是作为UML转换成Java代码过程的一个部分。在范例过程中建立方面,如列表15所示:

列表15:HelloWorldJDK14FFDC.aj
public aspect HelloWorldJDK14FFDC extends 
                   org.aspectj.lib.logging.JDK14FFDC {

           protected pointcut ffdcScope () : 
                   withincode( * HelloWorld.sayGoodbye() );

}

和追踪方面相比,这次产生的方面继承了一个叫JDK14FFDC的方面。列表16中显示了这个方面的部分内容:

列表16:JDK14FFDC方面
public abstract aspect JDK14FFDC extends FFDC {

    /** 
     * Method for consumption of raw FFDC in a 
     * static context
     */ 
    protected void processStaticFFDC(
            Throwable th, 
            JoinPoint.StaticPart tjp, 
            JoinPoint.StaticPart ejp) {
        Logger logger = Logger.getLogger(getLoggerName(ejp));
        logger.log(Level.WARNING,"source=" + getSourceId(ejp) 
                + ", line=" + getProbeId(tjp),th);
    }

    /** 
     * Method for consumption of raw FFDC in a 
     * non-static context
     */ 
    protected void processNonStaticFFDC(
            Throwable th, 
            Object obj, 
            JoinPoint.StaticPart tjp, 
            JoinPoint.StaticPart ejp) {
        Logger logger = Logger.getLogger(getLoggerName(ejp));
        logger.log(Level.WARNING,"object=" + obj 
                        + ", source=" 
                + getSourceId(ejp) + ", line=" 
                + getProbeId(tjp),th);
    }

    ...
}

FFDC方面和追踪方面类似,你会发现JDK14FFDC同时还继承了另外一个方面,也就是FFDC。列表17中显示了这个方面的一部分:

列表17:FFDC方面
public abstract aspect FFDC {
    
    /** 
     * Scope of FFDC policy e.g. packages, classes, methods 
     * is declared by sub-aspect.
     */ 
    protected abstract pointcut ffdcScope();
    
    private pointcut staticContext() : !this(Object);
    private pointcut nonStaticContext(Object obj) : this(obj);
    private pointcut caughtThrowable(Throwable th) : 
        handler(Throwable+) && args(th);

    /** 
     * Exclude FFDC aspects from exception reporting to avoid 
     * unwanted recursion
     */
    private pointcut excluded() : within(FFDC+);

    /** 
     * Advice for catch blocks in static context
     */
    before (Throwable th) : caughtThrowable(th) 
         && ffdcScope() && !excluded() && staticContext() {
       processStaticFFDC(th,
               thisJoinPointStaticPart,
               thisEnclosingJoinPointStaticPart);
    }

    /** 
     * Advice for catch blocks in non-static context. Extract 
     * the object that caught the exception
     */
    before (Throwable th, Object obj) : caughtThrowable(th) 
         && ffdcScope() && !excluded() && nonStaticContext(obj) {
       processNonStaticFFDC(th,
               obj,
               thisJoinPointStaticPart,
               thisEnclosingJoinPointStaticPart);
    }
    
    /** 
     * Method for consumption of raw FFDC in a 
     * static context
     */ 
    protected abstract void processStaticFFDC(
            Throwable th, 
            JoinPoint.StaticPart tjp, 
            JoinPoint.StaticPart ejp);

    /** 
     * Method for consumption of raw FFDC in a 
     * non-static context
     */ 
    protected abstract void processNonStaticFFDC(
            Throwable th, 
            Object obj, 
            JoinPoint.StaticPart tjp, 
            JoinPoint.StaticPart ejp);

    ...
}

请看FFDC方面,你会发现这里有两个抽象的方法processStaticFFDCprocessNonStaticFFDC。这两个方法在列表16的JDK14FFDC方法中显示了,它们各自记录了信息。

再次查看FFDC方面,你会发现有一个叫做ffdcScope的Pointcut,如列表18所示:

列表18:抽象的Pointcut ffdcScop()
/** 
 * Scope of FFDC policy e.g. packages, classes, methods is declared by
 * sub-aspect.
 */ 
protected abstract pointcut ffdcScope();

请查看列表19中显示的HelloWorldJDKFFDC方面:

列表19:刚产生的HelloWorldJDK14FFDC方面
public aspect HelloWorldJDK14FFDC extends 
                   org.aspectj.lib.logging.JDK14FFDC {

           protected pointcut ffdcScope () : 
                   withincode( * HelloWorld.sayGoodbye() );

}

你会看到这里提供了一个ffdcScope Pointcut的具体实现方式。更具体的说,FFDC方面的范围应该在HelloWorld类的sayGoodbye()方法中,在这里sayGoodbye()不带任何参数(因此用()),并且可以返回任意类型的值。

现在你已经了解了FFDC方面所涉及的范围。但是你还是不知道它做了什么。这在FFDC方面的Advice中有详细信息,如列表20所示:

列表20:FFDC方面中的Advice
/** 
 * Advice for catch blocks in static context
 */
before (Throwable th) : caughtThrowable(th) 
      && ffdcScope() && !excluded() && staticContext() {
    processStaticFFDC(th,
           thisJoinPointStaticPart,
           thisEnclosingJoinPointStaticPart);
}

/** 
 * Advice for catch blocks in non-static context. Extract 
 * the object that caught the exception
 */
before (Throwable th, Object obj) : caughtThrowable(th) 
      && ffdcScope() && !excluded() && nonStaticContext(obj) {
    processNonStaticFFDC(th,
           obj,
           thisJoinPointStaticPart,
           thisEnclosingJoinPointStaticPart);
}

这里的Advice是before Advice,这意味着它将在Join point实现之前执行,这正好符合Pointcut的条件。请注意在静态内容和非静态内容中各有一个捕捉块。在静态内容中,Pointcut表达式是由很多命名的Pointcut组成的:

  • caughtThrowable
  • ffdcScope
  • excluded
  • staticContext

与非静态内容相比,staticContextnonStaticContext替换了。这些Pointcut在FFDC方面中已经定义,如列表21所示:

列表21:FFDC方面中定义的Pointcut
protected abstract pointcut ffdcScope();
    
private pointcut staticContext() : !this(Object);
private pointcut nonStaticContext(Object obj) : this(obj);
private pointcut caughtThrowable(Throwable th) : 
    handler(Throwable+) && args(th);

private pointcut excluded() : within(FFDC+);

通过先前的讨论,你已经了解了ffdcScope Pointcut是什么。依次查看剩余的四个Pointcut,下面是它们的功能:

  • staticContext(): 它和未邦定的Join point匹配
  • nonStaticContext(Object obj):他是staticContext()的一个补充,它允许你得到一个正在运行的对象的参考说明。
  • caughtThrowable(Throwable th):它和处理Throwable类型的异常相匹配。通过使用args Pointcut指示器,你可以找到异常实例,还可以在你的报告中使用它。
  • excluded(): 它和FFDC方面和其子方面中的所有Join points匹配。例如象防止递归发生的Pointcut。

列表21中的Advice通过内容(静态或者非静态)组合这些Pointcut,然后调用一个执行方法处理信息,最终将其打印到屏幕上。

结论

这篇文章介绍了Aspects for MDD 框架,以及com.ibm.aspectj.lib是如何帮助我们在MDD应用程序中使用追踪和FFDC的。这篇文章一步一步地详细描述了各种方面,并且讨论了这些方面是如何应用到应用程序的。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational
ArticleID=156190
ArticleTitle=Aspects for MDD: Rational Software Architect中的基于方面的追踪与首次失败数据捕获
publish-date=08242006