Thursday, January 09, 2014

Using the XPages Toolbox, Guice, & Method Interceptors to profile Java methods

In 2013, I was fortunate enough to attend a few master class sessions at IBM Connect.  During one of those master class sessions, I was introduced to the XPages Toolbox by speakers Tony Mcguckin, and Marie Kehoe (two very talented engineers at IBM).  Among other things, the XPages toolbox provides statistics on method invocation times for SSJS and Java (with a little work).  By default, all SSJS methods are registered with the profiler which makes it relatively simple to find any performance bottlenecks.  Java, on the other hand, does require some custom code modifications to capture profiling data into the toolbox.  Below is a code sample from the XPages Toolbox documentation outlining how to use it in java code:


import com.ibm.commons.util.profiler.Profiler;
import com.ibm.commons.util.profiler.ProfilerAggregator;
import com.ibm.commons.util.profiler.ProfilerType;
...
private static final ProfilerType profilerCustom = new ProfilerType("MyJavaType");
public void myMethod() {
 if(Profiler.isEnabled()) {
  ProfilerAggregator agg=Profiler.startProfileBlock(profilerCustom, null); 
  long startProfiler=Profiler.getCurrentTime();
  try {
   _myMethod();
  } finally {
   Profiler.endProfileBlock(agg, startProfiler);
  }
 } else {
  _myMethod();
 }
}
private void _myMethod() {
 try {
  java.lang.Thread.sleep(1000);
 } catch(InterruptedException ex){}
}


As you can see from the above snippet, trying to profile many Java methods in your application would be very cumbersome, as you have to wrap each and every method implementation inside the profiler's start and end methods.  If you have a rough idea of what the performance problem could be, this might be an OK approach.   

What to do if you are unsure where the code bottleneck is?  Do you pollute all your java methods with a bunch of profiler code?  Heck no... use Google's Guice (pronounced juice) and method interceptors.  Let's start with what the profiled code will look like after we implement a Guice method interceptor:


@Profiled
public void myMethod() {
    try {
     java.lang.Thread.sleep(1000);
 } catch(InterruptedException ex){}
}


Notice in the above snippet, we've only added a single annotation called @Profiled.  This alerts Guice that the method "myMethod" needs to be intercepted.  Nice, clean, and easy to implement and roll back once you have setup Guice.  As with most things there are some trade offs to this approach.  Below is a list of potential issues of using this method to profile your Java classes in the XPages runtime:


  • You have to use and get familiar with Guice.  The basics are pretty straight forward, but it may not align with how you or your organization currently develop.  I highly recommend it as it promotes an interface heavy design and makes swapping out implementations a snap.  As your applications / inter-dependencies become more complex, Guice helps to manage those dependencies without having to create tons of getter/setter methods.  More importantly, Guice limits the amount of code changes required to provide a new implementation.  There are plenty of blog entries on the benefits of using Guice, so google around.

  • If your Java classes reside inside your .nsf, the java.policy file must be altered and opened up completely on the server for Guice to work its injection/interceptor "magic".  This problem can be circumvented if you develop your core java classes and business logic outside the nsf as an extension library (I typically do this).

  • You must hand off all object creation to Guice for method interceptors to work (even injecting post construction won't work, though dependency injection does).  To have Guice create certain managed beans, use a variable resolver to instantiate the required objects via Guice (see GuiceVariableResolver in com.tc.airts project in github).  Completely relying on Guice to create your objects is not so great as some of the nice scope storage mechanisms / behaviors are lost.  Once profiling is completed, you could always revert back to letting the XPages / JSF runtime create the instances by configuring your managed beans in faces-config, and then inject after construct (see GuiceVariableResolver).

Below is a java class, and sample screen capture of the output from @Profiled annotated method in the LookupData managed bean (found in com.tc.airts):


Simple Java Bean
package com.tc.airts;

import java.util.ArrayList;
import java.util.List;
import javax.faces.model.SelectItem;
import com.tc.xpage.profiler.Profiled;

public class LookupData implements ILookupData {
 
 @Profiled
 public List getAccidentTypes(){
  List list = new ArrayList(2);
  list.add(new SelectItem("PD","property damage"));
  list.add(new SelectItem("PI","personal injury"));
  return list;
 }
 
 @Profiled
 public List getSeverities(){
  List list = new ArrayList(2);
  list.add(new SelectItem(1,"low"));
  list.add(new SelectItem(2,"medium"));
  list.add(new SelectItem(3,"high"));
  return list;
 }
 
}

Output from XPages Toolbox









To disable the method intercept altogether, just comment out the ProfilerModule when applying the Guice modules, and the @Profiled annotation will be ignored (see AirtsActivator in com.tc.airts).  You could also just leave the interceptor in place and disable the XPages profiler in the notes.ini.  The interceptor will still get called, but will not attempt to log the method invocation times.

 public static List getModules(){
  List modules = new ArrayList();
  modules.add(new DominoModule());
  modules.add(new ManagedBeanModule());
  //modules.add(new ProfilerModule());
  return modules;

 }




You can grab the sample airts application from github in the below repo:
https://github.com/mwambler/webshell-xpages-ext-lib

The sub-projects to look into and relevant classes are:

  • com.tc.airts
    • AirtsActivator (inits Guice)
    • GuiceVariableResolver (creates guice objects and/or injects dependencies)
    • ILookupData, and LookupData (simple java bean class)
    • ManagedBeanModule (shows the Guice bindings, and defines what vars are created by guice)
  • com.tc.xpage.profiler
    • Profiled (annotation class for the method interceptor)
    • ProfileInterceptor (intercepts the method and logs the profiling data)
    • ProfileModule (sets up the Guice bindings, and the method interceptor)
  • com.tc.di.guicer
    • IGuicer (Guicer interface)
    • Guicer (wrapper for Guice injector)


Quick Install Notes:
If you receive a security exception when attempting to use this code, drop the following line into the default grant statement of the java.policy file on your domino server:

permission org.osgi.framework.AdminPermission "*", "metadata";

The above will open up access to the metadata associated with the plugins on your server, which is required by the com.tc.di.guicer plugin.

If you're trying to profile java code that is inside your own .nsf and you're getting security exceptions, backup the java.policy file, then replace the contents of the default grant statement with the below.  Think about moving your java code outside of the .nsf as an ext-lib, as that environment has more execution rights than code executed from within the .nsf.  You won't have to open up the permissions like this...

permission java.security.AllPermission;







Thanks for taking the time.


-Mark




1 comment:

The Geeks said...

hi..Im college student, thanks for sharing :)