Skip to main content

AOP@Work: Performance monitoring with AspectJ, Part 2

Putting the Glassbox Inspector to work with load-time weaving

Ron Bodkin (ron.bodkin@newaspects.com), Founder, New Aspects of Software
Ron Bodkin is the founder of New Aspects of Software, which provides consulting and training on application development and architectures, with an emphasis on performance management and effective uses of aspect-oriented programming. Ron previously worked for the AspectJ group at Xerox PARC, where he led the first AOP implementation projects and training for customers, and he was a founder and the CTO of C-bridge, a consultancy that delivered enterprise applications using frameworks for Java, XML, and other Internet technologies. Ron frequently speaks and presents tutorials at conferences and for customers, including presentations at Software Development, The Colorado Software Summit, TheServerSide Symposium, EclipseCon, StarWest, Software Test & Performance, OOPSLA, and AOSD. Recently, he has been working with the Glassbox Corporation to develop application performance management and analysis products using AspectJ and JMX. Ron can be reached at ron.bodkin@newaspects.com.

Summary:  Once you have a basic aspect-oriented monitoring infrastructure in place, you'll want to extend it to address real-world monitoring needs. In this second half of his two-part article, Ron Bodkin shows you how to add enterprise monitoring features to the Glassbox Inspector, including monitoring multiple applications, Web services, and Web application frameworks. He also shows you how to track application errors and contain them in monitoring code and demonstrates pragmatic approaches to deploying and controlling your monitoring infrastructure.

View more content in this series

Date:  15 Nov 2005
Level:  Advanced
Activity:  5415 views

About this series

The AOP@Work series is intended for developers who have some background in aspect-oriented programming and want to expand or deepen what they know. As with most developerWorks articles, the series is highly practical: you can expect to come away from every article with new knowledge that you can put immediately to use.

Each of the authors contributing to the series has been selected for his leadership or expertise in aspect-oriented programming. Many of the authors are contributors to the projects or tools covered in the series. Each article is subjected to a peer review to ensure the fairness and accuracy of the views expressed.

Please contact the authors individually with comments or questions about their articles. To comment on the series as a whole, you may contact series lead Nicholas Lesiecki. See Resources for more background on AOP.

Whereas the base Glassbox Inspector I left off with in Part 1 of this article could monitor only simple Servlets-based applications (as you saw in the Duke's Bookstore example), a more pragmatic framework would include support for popular Web application frameworks such as Struts and Spring. Modern Web applications rarely use Servlets to handle requests directly. These frameworks typically delegate all dynamic page requests to a gateway Servlet, such as the Spring Framework’s DispatcherServlet, which then delegates request processing to various controllers. Similarly, a Struts ActionServlet delegates to subclasses of Action that act as controllers.

In this second half of my foray into performance monitoring with AspectJ, I extend the Glassbox Inspector to provide more meaningful information about application performance by adding monitors that track the interaction of controllers for the Struts and Spring Web application frameworks, as well as serving and requesting Web services operations. I also extend the system to support multiple applications and add an error-handling layer and the ability to easily enable and disable monitoring at run time. I conclude the article by showing you how to deploy the Glassbox Inspector using load-time weaving and how to measure the resulting overhead.

You need some familiarity with Spring MVC, Struts, and Apache Axis for Web Services to follow the examples in this article. See Download for the complete code for the Glassbox Inspector monitoring infrastructure. See Resources to download AspectJ, JMX, and Tomcat 5, which you need to run the examples.

Reusable operation monitors

To accommodate a broader range of monitoring functionality, I've pulled most of the reusable Servlet-monitoring code from Part 1, Listing 5 into an AbstractOperationMonitor aspect. Often it is important to track nested requests, both to track controllers at different levels of a framework as well as to track performance in delegated requests (e.g., from forwarding to a nested request). Listing 1 shows how I've extended the Glassbox Inspector to track nested requests for operations:


Listing 1. Reusable operation monitoring aspect
public abstract aspect AbstractOperationMonitor extends AbstractRequestMonitor {  
      
  protected abstract class OperationRequestContext extends RequestContext {
      /**
       * Find the appropriate statistics collector object for this
       * operation.
       * @param operation an instance of the operation being 
       *         monitored
       */
      public PerfStats lookupStats() {                
          if (getParent() != null) {
              // nested operation
              OperationStats parentStats =
                      (OperationStats)getParent().getStats();
              return parentStats.getOperationStats(getKey());
          }
          return getTopLevelStats(getKey());
      }
        
      /**
       * Determine the top-level statistics for a given operation 
       * key. This also looks up the context name for the
       * application from the operation monitor: 
       * @see AbstractOperationMonitor#getContextName(Object)
       * For a Web application, top-level statistics are normally
       * all Servlets, and the key is the Servlet name. 
       * @param key An object to uniquely identify the 
       *         operation being performed.
       */
      protected OperationStats getTopLevelStats(Object key) {
          OperationStats stats;
          synchronized(topLevelOperations) {
              stats = (OperationStats)topLevelOperations.get(key);
              if (stats == null) {                
                  stats =
                    perfStatsFactory.createTopLevelOperationStats(key,
                            getContextName(controller));
                  topLevelOperations.put(key, stats);
              }
          }
          return stats;        
      }
        
      /** 
       * @return An object that uniquely identifies the operation
       *          being performed. 
       */
      protected abstract Object getKey();     
        
      /** The current controller object executing, if any. */
      protected Object controller;
  };

  /** 
   * This advice stores the controller object whenever we construct a
   * request context. 
   */
  after(Object controller) returning (OperationRequestContext ctxt) : 
    cflow(adviceexecution() && args(controller, ..) &&
    this(AbstractOperationMonitor)) &&
    call(OperationRequestContext+.new(..)) {
      ctxt.controller = controller;
  }    

...

The first part of the AbstractOperationMonitor extends the original Servlet monitor from Part 1 to look up statistics for nested operations. The use of nested operations lets me track resource use in cases like dispatched JSPs or break down different methods for Web-service or multiple-method application controllers. The original lookupStats() method now checks for a parent request context. If there is one, it calls the new method getOperationStats() to retrieve it. Otherwise, it calls the new method getTopLevelStats(), which invokes a factory to create a new OperationStats. Using the factory ensures that my monitoring infrastructure isn't dependent on the implementation of statistics classes.


Monitoring multiple applications

In Listing 1, I've also included support for monitoring multiple applications running in a single application server. I did this mainly by adding the provision to look up an application context name. When looking up top-level statistics, I invoke a template method on the monitor, getContextName(), to determine what application the operation is associated with. You'll see below how this is handled for Servlets. Note that the getContextName() method is passed an instance of the controller so I can look up the associated application context.

The abstract operation monitor also provides pointcuts with concrete advice that define common ways to monitor requests. These are extended to allow monitoring of incoming operations like Struts actions and incoming Web services requests. Listing 2 shows a representative example:


Listing 2. Monitoring templates in AbstractOperationMonitor
  /** 
   * This defaults to no join points. If a concrete aspect overrides
   * classControllerExec with a concrete definition,
   * then the monitor will track operations at matching join points
   * based on the class of the controller object.
   */ 
  protected pointcut classControllerExec(Object controller);
    
  Object around(final Object controller) :
    classControllerExec(controller) {
      RequestContext rc = new OperationRequestContext() {
          public Object doExecute() {
              return proceed(controller);
          }
                       
          protected Object getKey() {
              return controller.getClass();
          }                        
      };
      return rc.execute();        
  }    

  // Controller where the name of the signature at the monitored join point
  // determines what is being executed, for example, the method name
  /** 
   * This defaults to no join points. If a concrete monitor overrides
   * methodSignatureControllerExec with a concrete
   * definition, then it will track operations at matching join points
   * based on the run-time class of the executing controller instance
   * and the method signature at the join point.
   */ 
  protected pointcut methodSignatureControllerExec(Object controller);
    
  Object around(final Object controller) :
    methodSignatureControllerExec(controller) {
      RequestContext rc = new OperationRequestContext() {
          public Object doExecute() {
              return proceed(controller);
          }
                       
          protected Object getKey() {
              return concatenatedKey(controller.getClass(),
                thisJoinPointStaticPart.getSignature().getName());
          }
            
      };
      return rc.execute();        
  }   

The pointcut for classControllerExec() captures any point where a class controller is handling requests such as a Servlet do method execution or the normal Struts action execute method, where the class of the object servicing the request determines the operation being performed. More precisely, the classControllerExec() pointcut defines an empty pointcut (one that will never match any join point). It then provides concrete advice that sets up the worker object and returns the right key value for this case. This is similar to using an abstract pointcut, where subaspects must override the pointcut to apply advice. In this case, however, I provide a default definition that never matches. If a subaspect of the AbstractOperationMonitor doesn't need to monitor class controllers, it simply doesn't override the pointcut. If it does need to monitor class controllers, it provides a definition of when to monitor a point.


Concrete operation monitors

The methodSignatureControllerExec() pointcut and associated advice are similar: they provide a means for concrete operation monitor aspects to match controllers that dispatch to different methods, based on the signature at the join point.

Listing 3 shows the concrete aspects that extend the AbstractOperationMonitor to monitor Struts and Spring MVC operations:


Listing 3. Monitoring Struts and Spring MVC frameworks
public aspect StrutsMonitor extends AbstractOperationMonitor {
    /** 
     * Marker interface that allows explicitly _excluding_ classes
     * from this monitor: not used by default. If using Java™ 5, an 
     * annotation would be better.
     */
    public interface NotMonitoredAction {}

  /** 
   * Matches execution of any method defined on a Struts action or
   * any subclass, which has signature of an action execute (or 
   * perform) method, including methods dispatched to in a
   * DispatchAction or template methods with the same signature.
   */ 
  public pointcut actionMethodExec() : 
      execution(public ActionForward Action+.*(ActionMapping,
              ActionForm, ServletRequest+, ServletResponse+)) &&
      !within(NotMonitoredAction);

  /** 
   * Matches execution of an action execute (or perform) method for
   * a Struts action. Supports the Struts 1.0 API (using the perform
   * method) as well as the Struts 1.1 API (using the execute method).
   */ 
  public pointcut rootActionExec() : 
      actionMethodExec() && (execution(* Action.execute(..)) ||
      execution(* Action.perform(..)));
    
  /** @Override */
  protected pointcut classControllerExec(Object controller) :
      rootActionExec() && this(controller);

  protected pointcut dispatchActionMethodExec() :
      actionMethodExec() && execution(* DispatchAction+.*(..));

  protected pointcut methodSignatureControllerExec(Object controller):
      dispatchActionMethodExec() && this(controller);
}

public aspect SpringMvcMonitor extends AbstractOperationMonitor {
  /** 
   * marker interface that allows explicitly _excluding_ classes
   * from this monitor: not used by default 
   */
  public interface NotMonitoredController {}
    
  public pointcut springControllerExec() :
      execution(public ModelAndView Controller+.*(HttpServletRequest,
              HttpServletResponse)) &&
      !within(NotMonitoredController+);

  protected pointcut classControllerExec(Object controller) :
      springControllerExec() && execution(* handleRequest(..)) &&
      this(controller);

  protected pointcut methodSignatureControllerExec(Object controller):
      springControllerExec() && 
      execution(* MultiActionController+.*(..)) && this(controller);    
}

The first thing to note about both of these aspects is how concise they are. They simply extend two pointcuts from the operation monitor to concretely monitor their specific APIs. Because of their simplicity, these concrete monitors could also be defined in XML without requiring compilation. See Listing 10 for an example of this feature of AspectJ 5.

About StrutsMonitor and SpringMvcMonitor

The StrutsMonitor aspect in Listing 3 is designed to work with both old and new versions of the Struts API: Struts 1.0 actions are invoked by calling perform() whereas Struts 1.1 actions are invoked by calling execute(). I track the concrete subclass of normal action classes as a Class controller: only the class of the executing object matters. However, Struts allows for controllers that dispatch to multiple methods in a single class if the class extends DispatchAction. I monitor the individual methods being executed in these dispatch actions by matching any methods with the signature of a Struts action in a subclass of a DispatchAction. This approach lets me track statistics for each different controller method separately.

I have tested the StrutsMonitor against the iBatis JPetStore 1.3 sample application (see Resources). Like many applications, it extends Struts with a little framework of its own: the common base action has a method called perform() that dispatches to helper actions using doPerform() as a template method. However, there's no need to track execution of these template methods: The class-level controller will identify the specific subclass of Action being executed in the execute() method, which is sufficient to distinguish them.

The SpringMvcMonitor aspect is quite similar to the StrutsMonitor in that it monitors any Controller object as a class controller when it executes handleRequest(). It further monitors the execution of public methods with the signature of a Spring controller method in a MultiActionController or any subclass thereof. For example, I would like to monitor the execution of welcomeHandler() and ownerHandler() in this code snippet separately:

public class ClinicController extends MultiActionController 
  implements InitializingBean {
...
  public ModelAndView welcomeHandler(HttpServletRequest request, 
    HttpServletResponse response) throws ServletException {
      return new ModelAndView("welcomeView");
  }
...
  public ModelAndView ownerHandler(HttpServletRequest request, 
    HttpServletResponse response) throws ServletException {
       Owner owner = clinic.loadOwner(
         RequestUtils.getIntParameter(request, "ownerId", 0));
...
       model.put("owner", owner);
       return new ModelAndView("ownerView", "model", model);
  }
...

Naturally, it is easy to extend this approach to handling other frameworks, including custom frameworks, even using an XML-defined aspect as described previously.

Writing portable aspects

Note that the StrutsMonitor aspect, like most of my monitoring aspects, is designed to work against multiple versions of common components. Its pointcuts will match for different versions of Struts; they reference types without requiring them to be available at weaving time (for example, at load-time). You can often write pointcuts this way by only using class names in a statically resolvable context such as in a field signature or type pattern. If you use the class names in a dynamic context (such as this, target, or args), then the weaver requires access to the type. Note that the Struts monitor binds the this pointcut designator to an instance of Object, which does not require the Struts JAR to be present. While a bit ugly (because it loses the advantages of compile-time type checking), this approach is preferable to requiring the presence of many dependent libraries when deploying reusable aspects.

However, advice or other executable code that refers to a class or accesses its members (for example, by invoking a method) needs to follow the usual rules for portability in Java code. If the advice must invoke changing methods on different versions of an API, it is possible to use Java reflection to test for the presence of different methods and execute them; for example, to call perform() or execute() on an action method explicitly. Note that if I simply want to proceed with the original execute() or perform() method at a join point in an around advice, AspectJ handles this situation for me without requiring any special effort. In practice, many APIs provide backward compatibility so I can compile against the oldest version I want to support, and the result will work with newer versions.

Typically, the aspect won't execute code that triggers loading a class unless the class would be loaded by the program's execution anyhow; for example, advice that references a class will be triggered when a method for that class is going to execute. This property is important to preserve for library aspects that refer to optional classes. For the Glassbox Inspector, I don't want to require monitored applications to include a version of Struts or other libraries that it can monitor.

Monitoring JSPs and application names

Listing 4 shows the ServletMonitor from Part 1 refactored to use the abstract operation monitor as a base aspect. I continue to track Servlets based on their class by extending the classControllerExec() pointcut. I've added support for monitoring JSPs by monitoring the jspService() method. Because I want to be able to determine the name of a Web application, I've set the ServletMonitor to override the getContext() method to look up the name of a Servlet context.

All the other Web application controllers I've implemented so far are ultimately called from a Servlet, so only this controller needs to provide an implementation of the application context. In particular, my Web application framework monitors are nested within their dispatching Servlet invocations. When I further extend the Glassbox Inspector to monitor other operations such as incoming Web services requests, I will need to provide the operation name as a different kind of application context. When I want to extend the framework to monitor incoming JMS messages or EJB requests, I will have a framework to supply application contexts for those resources as well.


Listing 4. Extending a ServletMonitor
 
public aspect ServletMonitor extends AbstractOperationMonitor {
    
  /** 
   * Execution of any Servlet method: typically not overridden in
   * HttpServlets. 
   */
  public pointcut ServletService(Servlet Servlet) :
      execution(void Servlet.service(..)) && this(Servlet);
        
  /** Execution of any Servlet request methods. */
  public pointcut httpServletDo(HttpServlet Servlet) :
      execution(void HttpServlet.do*(..)) && this(Servlet);
    
  /** Execution of any JSP page service method. */
  public pointcut jspService(JspPage page) : 
      execution(* _jspService(..)) && this(page);
    
  protected pointcut classControllerExec(Object controller) :
      (ServletService(*) || httpServletDo(*) || jspService(*)) &&
      this(controller);

  /** 
   * Create statistics for this object: looks up Servlet context to
   * determine application name.
   */
  protected String getContextName(Object controller) {
      Servlet Servlet = (Servlet)controller;
      return Servlet.getServletConfig().getServletContext().
              getServletContextName();
  }
}


Updating JMX for nested statistics

In Part 1, I extended the Glassbox monitoring infrastructure to provide nested statistics, such as JDBC statements within connections within Servlet requests. Here, I look at how the StatsJmxManagement aspect can be updated to expose a compound name for these nested statistics. I also show how to integrate the application name into this compound name. For example, the statistics for a database statement might be named with the following string:

application=Spring Petclinic,operation=org.springframework.samples.
petclinic.web.ClinicController,database=jdbc:hsqldb:hsql://localhost:
9001,statement=SELECT id;name from types ORDER BY name

This string uses the descriptions and names for the performance statistics of all the ancestor statistics for the given information. Naming the statistics this way lets JMX tools group and display related information naturally. JMX tools like the JConsole use structured names to group common elements, making hierarchical browsing easier, as you will see in Figure 1. Listing 5 shows the necessary updates to StatsJmxManagement to support this feature:


Listing 5. Updating StatsJmxManagement to Support for nested statistics
public aspect StatsJmxManagement {

  private String PerfStats.cachedOperationName;
    
  /** JMX operation name for this performance statistics bean. */    
  public String PerfStats.getOperationName() {
      // look up from cache
      // else 
...
      appendOperationName(buffer);
...
      return operationName;
  }
    
  /** Add bean's JMX operation name to a StringBuffer. */    
  public void PerfStats.appendOperationName(StringBuffer buffer) {
      if (cachedOperationName != null) {
          buffer.append(cachedOperationName);
      } else {
          aspectOf().nameStrategy.appendOperationName(this, buffer);
      }
  }    

  public void PerfStats.appendName(StringBuffer buffer) {
      // append the appropriate name & JMX encode
...
  }

  public StatsJmxNameStrategy getNameStrategy() {...}
 
  public void setNameStrategy(StatsJmxNameStrategy nameStrategy) { ... }
    
  private StatsJmxNameStrategy nameStrategy;
}

public interface StatsJmxNameStrategy {
  void appendOperationName(PerfStats stats, StringBuffer buffer);
}

public class GuiFriendlyStatsJmxNameStrategy extends 
  AbstractStatsJmxNameStrategy {
    
  public void appendOperationName(PerfStats stats, 
          StringBuffer buffer) {        
      PerfStats parent = stats.getParent(); 
      if (parent != null) {
          appendOperationName(parent, buffer);
          buffer.append(',');
        else {
          if (stats instanceof OperationStats) {
              OperationStats opStats = (OperationStats)stats;
              String contextName = opStats.getContextName();
              if (contextName != null) {
                  buffer.append("application=\"");
                  int pos = buffer.length();
                  buffer.append(contextName);
                  JmxManagement.jmxEncode(buffer, pos);
                  buffer.append("\",");
              }
          }
      }
      buffer.append(stats.getDescription());
      buffer.append('=');
      stats.appendName(buffer);
  }
} 

Listing 5 shows how I've set up StatsJmxManagement to allow different naming strategies. For use with GUI tools like the JConsole, I've listed a GuiFriendlyStatsJmxNameStrategy, which returns names as shown in Listing 5 and in Figure 1. In addition to this, the complete code includes an alternative CanonicalStatsJmxNameStrategy, which adheres to the JMX recommendations for naming MBeans, but which doesn't display as well in any JMX tool I've tried.

The basic approach used to build up these names is to create a string buffer and recursively get the operation name for the parent statistic, then add the name afterwards. For top-level operation statistics, the context (application) name is added first. To support this approach, I added a getDescription() method to PerfStats. I also updated the operation and resource statistics implementations to set appropriate values (such as operation, operation1, .., resource, and request). Download the Glassbox Inspector source to see all the details and the CanonicalStatsJmxNameStrategy alternative.


Monitoring Web service calls

The Glassbox Inspector framework is now quite extensible to support new types of incoming requests (operations) and also new resources. Monitoring provided (incoming) remote service operations is quite similar to monitoring Web application frameworks: the article source gives an example of monitoring services implemented with Apache Axis.

With just a few minor adjustments I can also extend the Glassbox Inspector to monitor the consumption of remote services that are external to an application. As applications with service-oriented architectures become increasingly common, it is becoming important to track the reliability of externally controlled services and determine correlations with application problems. Correlating such data with application context (such as the Web operation being performed and time spent in other resources) is an important way to quickly identify root causes of application failures. This type of analysis is quite different from using SOAP handlers to monitor Web services calls in isolation because it connects implementation information with performance on external requests.

Listing 6 shows a remote-call monitor that monitors Web services calls made through the JAX-RPC API for calling Web Services, as well as RMI calls (which are themselves normally remote):


Listing 6. Monitoring remote calls

public aspect RemoteCallMonitor extends AbstractResourceMonitor {
  /** Call to remote proxy: RMI or JAX-RPC */
  public pointcut remoteProxyCall(Object recipient) : 
      call(public * Remote+.*(..) throws RemoteException) &&
      target(recipient) && !within(glassbox.inspector..*);
    
  /** Monitor remote proxy calls based on called class and method */
  Object around(final Object recipient) : 
    remoteProxyCall(recipient) {
      RequestContext requestContext = new ResourceRequestContext() {
            
          public Object doExecute() {
              return proceed(recipient);
          }
            
          public PerfStats lookupStats() {
              String key = "jaxrpc:"+recipient.getClass().getName()+
                "."+thisJoinPointStaticPart.getSignature().getName();
              key = key.intern();
                
              return lookupResourceStats(key);
          }
      };
      return requestContext.execute();
  }
}

Notice how little I had to do to support this feature; most of the support is contained in the AbstractRequestMonitor. I simply defined remote proxy calls to be calls to public methods on any object implementing the Remote interface where the method can throw a RemoteException. With that small change in place, I can use the Worker Object pattern (see Part 1) to monitor remote calls, providing a name based on the class and method name of the object being invoked. The RemoteCallMonitor aspect uses a helper method to find the top-level resource statistics, which were extracted from the worker object in the JdbcConnectionMonitor and pulled into the new common base aspect: AbstractResourceMonitor.

Naturally, I could extend this approach further to monitor other Web services calls, as well as the execution of methods based on popular frameworks such as Apache Axis. I could also use this technique to monitor the time spent in XML processing, which would be useful for any XML-intensive application. Many applications that utilize Web services would also benefit from this type of monitoring.


Monitoring for failure

Extending the Glassbox Inspector system to monitor application failures is a natural next step. I start by recording a failure whenever the system exits a monitored point by throwing an exception (more precisely, any Throwable). Failure recording provides useful information for tracking Web operations and for database connection attempts: throwing a Throwable from either of these is problematic in almost all cases. For JDBC statements and other resource accesses, a throwable often indicates a true application failure, but in some cases, it is part of the normal program logic. For example, trying to register with a name that is already taken can trigger an exception on insert. To account for this case, I use a configurable strategy for each monitor to determine whether a given throwable is actually an error or should be accepted as a normal exit condition.

Listing 7 contains the advice and changes that allow me to configure the AbstractRequestMonitor to determine whether a Throwable is a failure:


Listing 7. Adding extensible failure detection
public aspect AbstractRequestMonitor {

  /** 
   * Record an error if this request exited by throwing a 
   * Throwable.
   */
  after(RequestContext requestContext) throwing (Throwable t) : 
    requestExecution(requestContext) {
      PerfStats stats = requestContext.getStats();
      if (stats != null) {
          if (failureDetectionStrategy.isFailure(t,
                  thisJoinPointStaticPart)) {
              requestContext.recordFailure(t);
          } else {
              requestContext.recordExecution();
          }
      }
  }
  public void setFailureDetectionStrategy(...) { ... }
  public FailureDetectionStrategy getFailureDetectionStrategy() { ... }
  protected FailureDetectionStrategy failureDetectionStrategy =
      new FailureDetectionStrategy() {
        public boolean isFailure(Throwable t, StaticPart staticPart) {
            return true;
        }
      };
...
}   

public interface FailureDetectionStrategy {
  boolean isFailure(Throwable t, StaticPart staticPart);
}

The net effect of Listing 7 is to record a count whenever I exit a monitored join point by throwing an exception. Note that I definitely want to identify cases where I exit by throwing an Error, as they are almost certainly failures! From here, I could easily extend my AbstractRequestMonitor to also track some or all of the exceptions, stack traces, and values of the current object and parameters at the point where the exception occurred. If I did this, I would need a means to be sure I did not record sensitive information such as passwords, credit card numbers, or personally identifiable information. To complete this integration, I made a few minor revisions to my AbstractRequestMonitor. See the article source for details.

Using exception conversion for errors

Given the previous discussion, you may be wondering how you could configure the Glassbox Inspector to identify some throwables as being not errors. The easiest way would be to pass in a strategy that always returns false for a given monitoring aspect (such as the JdbcStatementMonitor). Another simple solution is to detect certain exception types, such as NumberFormatException, and indicate that they are not failures. This could be combined with the Exception Conversion pattern (see Resources) to convert certain exceptions into a meaningful hierarchy. Listing 8 is an example of how an application might apply exception conversion along with a failure detection strategy:


Listing 8. Tracking failures with exception conversion
/** 
 * Softens and uses Spring's exception translator to convert from
 * SQLException to unchecked, meaningful exception types 
 */
aspect DataAccessErrorHandling {
  declare soft: SQLException: jdbcCall();

  after() throwing (SQLException e) : jdbcCall() {
      throw sqlExceptionTranslator.translate("dbcall", getSql(), e);
  }

  declare precedence: ModelErrorHandling, JdbcStatementMonitor;
}

/** 
 * Conservative detection strategy: failures from SQL that often
 * indicate valid business conditions are not flagged as failures.
 */
public class DataAccessDetectionStrategy implements FailureDetectionStrategy {
  public boolean isFailure(Throwable t, StaticPart staticPart) {
      return !(t instanceof ConcurrencyFailureException) &&
         !(t instanceof DataIntegrityViolationException);
  }
}

The first part of Listing 8 shows a simple error-handling aspect that softens SQLExceptions from JDBC calls and then uses a Spring framework SQLExceptionTranslator (such as SQLErrorCodeSQLExceptionTranslator) to convert the exceptions into meaningful (unchecked) exceptions. This aspect is also declared to take precedence over the JdbcStatementMonitor using AspectJ's declare precedence form. This ensures that the DataAccessErrorHandling aspect has already converted exceptions before the JdbcStatementMonitor tracks the exception type being returned.

The other part of Listing 8 shows an example strategy that indicates failures only for conditions that are virtually guaranteed to indicate a failure (like the failure to access a resource). In particular, it excludes concurrency failures (which can happen in well-designed, multi-user applications) and data-integrity violations (which often happen when registering new users, for example).

For even more control over failure detection, you might rely on having a marker interface or an annotation on a method or class to indicate that certain parts of a system can throw an Exception as a normal course of action. This could be combined with the use of declare parents or AspectJ 5's new declare annotation form to capture modular rules indicating whether throwing a Throwable indicates a failure or not. In most cases, the simple rules I've indicated are a good match for coarse-grained monitoring, but it's nice to have more flexibility in cases where you want to avoid spurious warnings.

Detecting common Web failures

An application can fail even if it doesn't throw an exception. One of the most important cases occurs when a controller handles an exception and forwards the response to an error page. Another case is when the HTTP response for the page indicates an error. Listing 9 updates the ServletMonitor from Listing 4 to detect failures for such cases. I also made a small update to the AbstractRequestMonitor.RequestContext worker object to allow setting an error context and to record failures if an error context has been set.


Listing 9. Detecting common Web failures
  /** Call of send error in the Servlet API */
  public pointcut sendingErrorResponse(HttpServletResponse response,
          int sc) : 
      target(response) && call(* sendError(..)) && args(sc, ..);    
  /** Track a failure after sending an error response */
  after(HttpServletResponse response, RequestContext requestContext,
          int sc) returning : 
    sendingErrorResponse(response, sc) && inRequest(requestContext) {
      requestContext.setErrorContext(new Integer(sc));
  }

  /** Execute handle page exception in the JSP API */
  public pointcut handlePageException(PageContext pageContext,
          Throwable t) :
      call(* PageContext.handlePageException(*)) &&
      target(pageContext) && args(t);

  /** Track a failure when showing an error page */
  after(PageContext pageContext, RequestContext requestContext,
          Throwable t) returning : 
    handlePageException(pageContext, t) && inRequest(requestContext) {
      requestContext.setErrorContext(t);
  }    


Extending library aspects

Several times, as I've built up the Glassbox Inspector, I've needed to make my library aspects more extensible. I've listed some of the common options available below. I've already demonstrated some of these options, and all can be usefully applied:

  • Provide an abstract pointcut to define where the aspect applies (for example, a scope() definition).

  • Provide an empty pointcut that must be overridden for certain advice to apply (as in the AbstractOperationMonitor in Listing 2). In many cases, a minority of concrete aspects will need to apply a given piece of advice, so having an empty default is more useful. Having an empty default implementation for a template pointcut like this is quite similar to having an empty default implementation for a template method.

  • Rely on marker interfaces to indicate in which types the aspect should apply. Because interfaces are inherited, any subtypes of these types will also be included.

  • Rely on Java 5 annotations to indicate in which types and/or methods the aspect should apply. This approach gives you more fine-grained control over where aspects should apply, allowing definition by method, as well as inclusion of a class but not its subclasses.

  • Provide run-time configuration of behavior, with logic to test whether a capability is enabled. See Run-time control for an example.

With these extensions integrated into the application, I'm done extending the Glassbox Inspector for monitoring (at least for the purpose of this article -- the open source project begins where this article ends!). I now have a useful view of application performance organized logically. With this in place, I can use standard JMX clients to review overall performance statistics and errors by request in a Web application. I can also drill down into the performance and reliability of database or Web services for any that are suspect. Figure 1 shows the Glassbox Inspector monitoring tool at work on the Spring Petclinic, iBatis JPetstore, and Duke's Bookstore application examples:


Figure 1. Snapshot of the complete monitoring infrastructure at work


Error handling

From here forward, I'll focus on making it easy to deploy the extended Glassbox Inspector monitoring infrastructure. My first step toward an enterprise-ready management infrastructure is to extend the Glassbox Inspector for error isolation. Traditionally, developers do their best to ensure that functions like performance monitoring do not create errors that impact monitored applications. However, unexpected behavior in monitored code (for example, unanticipated null values, proxies that throw exceptions, etc.) occasionally exposes bugs in the monitoring code itself. In these cases, having the ability to isolate errors raises confidence in the robustness of the monitoring implementation. While problems from initializing the system can occasionally cause failures that the error isolation aspect can't buffer, this type of problem normally occurs immediately and is easy to catch during development, making it much less of a risk.

My basic error-isolation strategy is to catch and handle any Throwables that might be thrown from an advice-execution join point so they don't affect the underlying application. Note that the advice-execution pointcut matches join points where some advice is executing, so this aspect will affect the behavior of other aspects. This strategy works well for before and after advice, but implementing error isolation is a little more complicated for around advice. If an exception is thrown before reaching the proceed statement, I'd like to still proceed with the original join point. Conversely, if an exception is thrown by the proceed statement, I want to allow that exception to flow out and not "isolate" it (that is, not swallow it).

Because there's no join point for invoking proceed inside advice, I can't write fully generic advice that does what I want. However, I can use the structure of the Glassbox Inspector to provide error isolation for the one type of around advice that it uses. The only around advices in the Glassbox Inspector always construct a worker object and then invoke the execute() template method on it, which invokes doExecute() to proceed with the original join point. So I will handle any exceptions returned by a helper method in a monitor (AbstractRequestMonitor or any subtype) or that are returned by calls within execute or doExecute(). Listing 10 shows how I've extended the Glassbox Inspector to handle errors:


Listing 10. Error handling in the Glassbox Inspector
public aspect ErrorHandling {
  public pointcut scope() : 
      within(glassbox.inspector..*) && !within(ErrorHandling+);

  public pointcut inMonitor() : 
      within(AbstractRequestMonitor+) || within(RequestContext+);

  public pointcut voidReturnAdviceExecution() :
      adviceexecution() &&
      if(((AdviceSignature)thisJoinPointStaticPart.getSignature()).
             getReturnType() == void.class);

  protected pointcut monitorHelperExec() : 
      inMonitor() && execution(* *(..)) && 
      !execution(* execute(..)) && !execution(* doExecute(..));
    
  protected pointcut monitorExecuteCall() : 
      (inMonitor() && withincode(* execute(..)) || 
      withincode(* doExecute(..))) && 
      (call(* *(..)) && !call(* doExecute(..)) || call(new(..)));
    
  public pointcut handlingScope() : 
       scope() && 
         voidReturnAdviceExecution() || monitorHelperExec() || monitorExecuteCall();

  /**
   * This advice ensures that errors in the monitoring code will not
   * poison the underlying application code.
   */
  Object around() : handlingScope() {
      try {
          return proceed();
      } catch (Throwable e) {
          handleError(e, thisJoinPointStaticPart);
          return null;
      }
  }

  public synchronized void handleError(Throwable t, 
          StaticPart joinPointStaticPart) {
      // log 1 in 1000 but don't rethrow
      ...
  }

  /** 
   * Count of errors: a weak map so we don't leak memory after apps
   * have been disposed of.
   */
  private Map/* <JoinPoint.StaticPart, MutableInteger> */errorCount =
      new WeakHashMap();
    
...
}

About the error-isolation layer

Looking at Listing 10, you can see that I first define a pointcut for scope to limit advice in ErrorHandling to apply only to code in the Glassbox Inspector but not to the ErrorHandling aspect itself. Then, I define the inMonitor() pointcut to define code that is lexically within the request monitor (including any worker objects). I define the voidReturnAdviceExecution() pointcut to match advice executions that return void by using an if test based on the signature of the advice execution. Before and after advice always returns void and none of the Glassbox Inspector around advices do. For my system, this is equivalent to matching advice execution for before and after advice, but not for around advice.

Next, I define monitorHelperExec() to match the execution of helper methods in the monitor (that is, any call other than execute() or doExecute()). I define monitorExecuteCalls() as any call from within the execute() or doExecute() methods in the monitor other than calls to doExecute() itself. These pointcuts are combined in handlingScope() to define the join points where I will handle exceptions.

The net effect is that I handle exceptions at any before or after advice and at the right points in the monitoring around advice to prevent errors from affecting application code, but not to swallow application exceptions. To handle the exceptions, I use around advice that catches any Throwable thrown by proceeding and then delegates to a helper method to log them when first encountered. The advice then returns without throwing an exception. The Glassbox Inspector also uses AspectJ's declare soft form to indicate to the compiler that it handles any checked exceptions inside of advice.

Overall, I've been able to create a very effective error-isolation layer. The changes required to add this to the Glassbox Inspector are low overhead, as indicated by the performance measurements you'll see below.


Run-time control

Having built a useful monitoring infrastructure, I now want to add run-time control over the monitoring to be performed without restarting the server. The run-time control infrastructure will also let users of the Glassbox Inspector add higher overhead monitors to capture more detailed data when a system is having problems. I can dynamically enable or disable the monitor as a whole, or each advice individually, at run time by adding an if test for a simple flag to each advice's pointcut. To provide a common framework for control, I use the idiom in Listing 11 to enable and disable the advice for each aspect:


Listing 11. Enabling and disabling advice at run time

public aspect AbstractRequestMonitor {
...
    protected pointcut scope() : if(true);
    protected pointcut monitorEnabled() : isMonitorEnabled() && scope();
    protected abstract pointcut isMonitorEnabled();

    public boolean isEnabled() {
        return enabled;
    }
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
    protected boolean enabled = true;
}

public aspect JdbcStatementMonitor extends AbstractResourceMonitor {
...
    /** Monitor performance for executing a JDBC statement. */
    Object around(final Statement statement) :
      statementExec(statement) && monitorEnabled() {
        ...
    }
...
    protected pointcut isMonitorEnabled() : if(aspectOf().isEnabled());
}

Naturally, I will use JMX to provide the necessary run-time control, as shown in Listing 12:


Listing 12. Enabling JMX management of monitors
public aspect MonitorJmxManagement {
  /** 
   * Management interface for monitors allows enabling and disabling 
   * at runtime. 
   */
  public interface RequestMonitorMBean extends ManagedBean {
      public boolean isEnabled();
      public void setEnabled(boolean enabled);
  }

  /** 
   * Make the {@link AbstractRequestMonitor} aspect implement 
   * {@link RequestMonitorMBean}, so all instances can be managed 
   */
  declare parents: AbstractRequestMonitor implements MonitorMBean;

  public String AbstractRequestMonitor.getOperationName() {
      return "control=monitor,type="+getClass().getName();
  }
    
  public MBeanInfoAssembler AbstractRequestMonitor.getAssembler() {
      if (assembler == null) {
          initAssembler();
      }
      return assembler;
  }
...
}

The MonitorJmxManagement aspect defines the management interface of a monitor to consist of a single attribute, enabled. Most of the additional support required to manage the monitors through JMX comes from the JmxManagement aspect discussed in Part 1. I also moved some common code from StatsJmxManagement into that shared class. Figure 2 shows an example of controlling monitoring at run time with JMX:


Figure 2. Controlling monitoring at run time


Deploying the Glassbox Inspector

At this point, I'm ready to deploy the Glassbox Inspector for application monitoring. I've written the code to allow it to be applied to just application code as much as possible. In many cases, however, applying aspects to library code is advantageous. In the case of the Petclinic and JPetstore sample applications I worked with, the JDBC access is handled by Spring and iBatis library code respectively, and in the Petclinic, the core controller logic is handled by the Spring library. So it is often advantageous for monitoring aspects to affect some of the library code used by applications. Sometimes, you can avoid advising execution of library code by advising calls from the application code or by adding explicit hooks into the application code that can be advised (for example, by subclassing methods defined in a library class).

I have a choice of two basic approaches to weaving monitoring into applications: at build time or at load time. If I use build-time weaving, it is much more convenient to process binary jars (for example, using the ajc -inpath command line tool to perform weaving), rather than rebuilding the libraries from source. I've used this approach in the past, and it does offer the best performance on application startup and it imposes minimal memory overhead. However, it can add significant complexity to the build environment. One issue is the requirement that the AspectJ weaver be able to resolve references to third party classes contained in the jar being woven. In the case of Spring, this means including JARs such as Hibernate, a variety of open source cache managers, and the Servlet API.

AOP support in the VM

BEA JRockIt has recently made available a prototype Java VM with support for aspects. Such integration promises high performance and late-binding support for AOP, which constitutes the best of both worlds. I am looking forward to the maturation of this technology and offerings from other major Java VM creators.

The other approach that has become practical with AspectJ 5 is load-time weaving. Using this approach, I can build monitoring aspects separately and include a small deployment descriptor to define where the aspects apply.

Load-time weaving

Listing 13 shows an example of an aop.xml deployment descriptor file that describes where load-time weaving should apply. AspectJ 5 load-time weaving agents find all META-INF/aop.xml resources in a directory or jar accessible by any ClassLoader in the system. These aspects can be loaded automatically by any Java ClassLoader agent (such as the Java 5 JVMTI -javaagent, the JRockIt JVM agent, or even the WebSphere or WebLogic ClassLoader plugins) to apply aspects to all or some of the code in a system. This lets me apply monitoring to an application or a server without building it in up front, and it avoids the need to change build processes.

Note that code in (for example) the Spring framework that refers to classes that aren't on the classpath will never be loaded, so there's no need to add extra jars with this approach. As of this writing in November 2005, AspectJ 5 Milestone 4 has been released and the final release of AspectJ 5 is anticipated by year-end. Load-time weaving is very useful in preserving module independence in larger systems; it provides the same kind of run-time resolution for aspects that Java class loading does for objects.


Listing 13. Load-time weaving configuration
<aspectj>
    <weaver>
      <exclude within="org.springframework.jmx..*"/>
      <!--  don't reweave -->
      <exclude within="glassbox.inspector..*"/>
    </weaver>
    <aspects>
      <aspect
 name="glassbox.inspector.monitor.operation.AxisOperationMonitor"/>
      <aspect
 name="glassbox.inspector.monitor.operation.SpringMvcMonitor"/>
      <aspect
...
      <aspect
 name="glassbox.inspector.monitor.resource.JdbcStatementMonitor"/>
      <concrete-aspect
 name="glassbox.inspector.monitor.operation.CustomMvcMonitor "
 extends="glassbox.inspector.monitor.operation.TemplOperationMonitor">
        <pointcut name="classControllerExecTarget"
           expression="execution(* com.ibatis.struts.BaseBean..*(..))
&& cflow(execution(* com.ibatis.struts.BeanAction.execute(..)))"/>
      </concrete-aspect>
    </aspects>
</aspectj>

The aop.xml file in Listing 13 starts with a definition of where the AspectJ weaver should and should not apply aspects. By default, it affects any code loaded through the ClassLoader. I've added some exclusions for some parts of the system where load-time weaving is not required to improve start-up time.

The AOP configuration file then lists all the aspects to be applied. Currently, this list of aspects must be maintained by hand, which is a common source of bugs. Fortunately, it is planned that the final release of AspectJ 5 will include a facility to generate these lists when compiling aspects.

Finally, note that the example file includes an XML-defined aspect, CustomMVCMonitor. This concrete aspect extends an abstract aspect to monitor an application-specific framework. Basically, it just defines a pointcut for operations that should be monitored. This shows how you can extend the Glassbox Inspector to monitor custom code without having to compile any code. You can also prevent monitoring aspects from running by removing them from the aop.xml file. If you undeploy them in this way, you can reduce overhead but you cannot re-enable them without restarting the application (or server). This technique can also be used to limit monitoring. For example, you could define a more narrow pointcut for monitoring Struts actions in an XML-defined aspect and exclude the standard StrutsMonitor.

Running Tomcat 5.5 with load-time weaving

I'll demonstrate the deployment of Glassbox Inspector on the Tomcat 5.5 application server. This server implements Java 5 by default, so I use Java 5's -javaagent start-up parameter to invoke AspectJ 5 load-time weaving. (See Resources to learn about using AspectJ load-time weaving on earlier versions of the Java language.) To use this feature, you simply need to add a new java option flag for start up. This is typically done by editing a shell script. For example you could add the following line to the start of setclasspath.bat on a Windows machine:

 set JAVA_OPTS=-javaagent:%CATALINA_HOME%\common\lib\aspectjweaver.jar -Xmx256m 

You can make a similar addition to the setclasspath.sh script for Linux or other Unix operating systems.

This setup simply allows load-time weaving in the Tomcat VM. To monitor Web applications, I can build them as usual and add the glassboxInspector.jar file and its dependencies into their WEB-INF/lib directory subsequently. The AspectJ weaver then finds the aop.xml file that I've deployed and ensures that the aspects it defines are woven as applications are loaded. Alternately, I could add the glassboxInspector.jar and file to the shared/lib folder for Tomcat. This would add monitoring to all Web applications in the server without requiring me to add this jar to each application. This solution is analogous to adding any other library functionality to a single application or to all applications in an application server. I could even put the inspector on my system classpath or Tomcat's common/lib folder. This would let me monitor the internals of Tomcat, although I haven't found much need for that, and doing so would require additional start up time.

I prefer to deploy monitoring across the entire server at once. Typically, application servers are managed as a whole, and it's desirable to have consistent management and monitoring capabilities across all applications on the server.


Performance and memory use

The trade-off for the simplicity of using load-time weaving is the additional time required to start an application. I have been working with Alexandre Vasseur and Matthew Webster to optimize the performance of load-time weaving before the AspectJ 5 release. Already the overhead is reasonable, and we have identified some helpful optimizations to integrate. I anticipate further optimizations of load-time weaving performance will continue over the next year. Likewise, these tests are on an alpha version of the Glassbox Inspector. I expect that the overhead will drop significantly with further tuning of the Glassbox Inspector and AspectJ load-time weaving. I plan to optimize the Glassbox Inspector to use the java.util.concurrent library, with aspects that can use the most efficient concurrent implementation depending on what Java VM the system is running.

I took some simple measurements of server start-up times and median time per request using an October 2005 development build of AspectJ (after 1.5.0 M4) on a Windows XP workstation. This system was a developer build (after alpha 1) of the Inspector. In my test, I ran Tomcat 5.5.9 on the JRockIt 1.5.0_03 Java VM and auto-started the iBatis 1.3 JPetstore, Spring 1.2.1 Petclinic and Duke's Bookstore applications.

The end-to-end response time overhead was minimal, once the system had started. With 50 simultaneous users running on the same machine, the median server response time increased by about 10 milliseconds. However, start-up time with load-time weaving took about 15 seconds, including weaving time. By contrast, starting up without weaving took about five seconds. Finally, the use of load-time weaving added significant memory overhead: The Java process used about 100 percent more memory with load-time weaving than without.

Table 1 shows the per request overhead, net startup time, and memory usage when the Glassbox Inspector is enabled with load-time weaving, deployed with load-time weaving but disabled, or not deployed:

Table 1. Performance Overhead with Load-Time Weaving
MonitoringMedian end-to-end
Response time (ms)
Server start up
Time(s)
Start-up process
Memory use (MB)
Enabled2015135
Disabled (at run time)10130
Not deployed10565


In conclusion

In this two-part article, I've shown you how you can use AspectJ to tackle a complex crosscutting problem (in this case application monitoring) and build a solution incrementally. Even if you choose not to take an aspect-oriented approach to your next monitoring solution, many of the lessons and techniques described here will prove valuable. I certainly have found them to be useful on my other AspectJ projects.

That said, I hope this article has made a good case for using aspect-oriented code for monitoring. The aspects I've written are much easier to extend and adapt than traditional scattered and tangled instrumentation code, and they provide a better way to get data about production applications. Without aspects, it would be hard to implement the advanced functionality described here just once, let alone adapt it as you learn more about a production application.

Development and analysis of the Glassbox Inspector monitoring infrastructure need not end with this article. The project is looking for contributions in many areas, such as:

  • Capturing more context about what's happening (for example, stack traces and parameters), especially for unusual results such as failures or abnormal response times.

  • Monitoring more operations, resources such as JMS and EJB, and expanding on the Inspector's basic support for monitoring XML document processing.

  • Handling distributed monitoring, to track information across clustered applications and to correlate information across distributed calls.

  • Using Java 5 management information, such as CPU times or thread-specific statistics.

  • Using application server JMX statistics, such as thread pools.

  • Capturing history and trends with persistent storage and reports.

  • Using JMX to provide alerts and exposing statistical summaries. It would be very nice to roll-up key information about nested monitors in the JMX statistics.

  • Monitoring top-level resource information (for example, total time spent calling services or connecting to databases). This could be provided with better summary data.

  • Providing different degrees of statistical summarization (for example, histograms of times spent in a request).

  • Adaptively discovering relevant parameters to track (for example, for unknown database queries or Servlet requests).

  • Providing resource monitoring for higher-level database and service access frameworks (like Hibernate, TopLink, EJB 3 Persistence Managers, JAX-WS, etc.).

  • Allowing sampling to vary the amount of data captured (across an entire request).

  • Monitoring system events such as 404 errors for requests to a Web application that aren't bound to a Servlet. This is a good case for advising Servlet filters, including adding an empty Servlet filter to match any requests for an application.

  • Monitoring business events such as customer purchases or abandoned shopping carts.

Now it's your turn. I encourage you to download the Glassbox Inspector and try it out. I also would love for you to contribute to the project, whether by offering feedback, developing another monitor, extending the system in one of the directions suggested above, or establishing a new direction for the project. See Download for the complete Glassbox Inspector source code. See Resources to learn more about the topics discussed here.

Acknowledgments

Thanks to Eugene Kuleshov, Nicholas Lesiecki, Eamonn McManus, Srinivas Narayanan, Ramnivas Laddad, Will Edwards, Matt Hutton, David Pickering, Rob Harrop, Alex Vasseur, Paul Sutter, and Mik Kersten for reviewing this article and giving such insightful comments.



Download

DescriptionNameSizeDownload method
Source codej-aopwork12source.zip72KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

Discuss

About the author

Ron Bodkin is the founder of New Aspects of Software, which provides consulting and training on application development and architectures, with an emphasis on performance management and effective uses of aspect-oriented programming. Ron previously worked for the AspectJ group at Xerox PARC, where he led the first AOP implementation projects and training for customers, and he was a founder and the CTO of C-bridge, a consultancy that delivered enterprise applications using frameworks for Java, XML, and other Internet technologies. Ron frequently speaks and presents tutorials at conferences and for customers, including presentations at Software Development, The Colorado Software Summit, TheServerSide Symposium, EclipseCon, StarWest, Software Test & Performance, OOPSLA, and AOSD. Recently, he has been working with the Glassbox Corporation to develop application performance management and analysis products using AspectJ and JMX. Ron can be reached at ron.bodkin@newaspects.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, SOA and Web services
ArticleID=98530
ArticleTitle=AOP@Work: Performance monitoring with AspectJ, Part 2
publish-date=11152005
author1-email=ron.bodkin@newaspects.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers