Search code examples
javaaspectpointcut

Pointcut to match call to any method except those frm jdk


I want my point cut to mark call to any method except those in java sdk

Pointcut trace(): call(* *(..)) && !within(methodprofilt) && !call(* java*(..)

This doesnt work


Solution

  • I think it is not so easy, if you look into the JDK you see a lot more package names other than java..*. Check out my sample code which uses JDK classes from other packages (there are even more, this is just a little showcase), some for normal access and others for restricted access:

    Helper class:

    package de.scrum_master.app;
    
    public class Foo {
        public static void doSomething() {
            System.out.println("Doing something");
        }
    }
    

    Driver application:

    package de.scrum_master.app;
    
    import javax.swing.event.EventListenerList;
    import org.ietf.jgss.GSSException;
    import org.ietf.jgss.Oid;
    import org.omg.CORBA.IntHolder;
    import org.w3c.dom.bootstrap.DOMImplementationRegistry;
    import org.xml.sax.InputSource;
    
    // Internal Sun/Oracle-specific JDK classes,
    // not recommended for JDK- and version-agnostic use
    import sun.util.calendar.CalendarUtils;
    import com.sun.beans.finder.BeanInfoFinder;
    
    public class Application {
        public static void main(String[] args)
            throws GSSException, ClassNotFoundException, InstantiationException,
            IllegalAccessException, ClassCastException
        {
            sayHelloTo("world");
            Foo.doSomething();
            new Oid("1.2.840.113554.1.2.2").getDER();
            System.out.println("Process ID (PID) = " + getPID());
            new IntHolder(11)._type();
            DOMImplementationRegistry.newInstance().getDOMImplementation("XML 3.0");
            new InputSource().setEncoding("UTF-8");
            new EventListenerList().getListenerCount();
    
            // Internal Sun/Oracle-specific JDK classes,
            // not recommended for JDK- and version-agnostic use
            new BeanInfoFinder().getPackages();
            new CalendarUtils().isGregorianLeapYear(2015);
        }
    
        public static void sayHelloTo(String visitor) {
            System.out.println("Hello " + visitor);
        }
    
        public static long getPID() {
            String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
            return Long.parseLong(processName.split("@")[0]);
        }
    }
    

    Improved interceptor aspect:

    package de.scrum_master.aspect;
    
    public aspect NonJDKInterceptor {
        pointcut allCalls() : call(* *(..));
        pointcut jdkCalls() :
            call(* java..*(..)) || call(* javax..*(..)) ||
            call(* org.ietf..*(..)) || call(* org.omg..*(..)) ||
            call(* org.w3c..*(..)) || call(* org.xml..*(..)) ||
            call(* sun..*(..)) || call(* com.sun..*(..));
    
        before() : allCalls() && !jdkCalls() && !within(NonJDKInterceptor) {
            System.out.println(thisJoinPoint);
        }
    }
    

    Console output:

    call(void de.scrum_master.app.Application.sayHelloTo(String))
    Hello world
    call(void de.scrum_master.app.Foo.doSomething())
    Doing something
    call(long de.scrum_master.app.Application.getPID())
    Process ID (PID) = 13948
    

    Your simple pointcut would miss most of the JDK calls. With just this pointcut

    pointcut jdkCalls() : call(* java..*(..));
    

    the output would look as follows:

    call(void de.scrum_master.app.Application.sayHelloTo(String))
    Hello world
    call(void de.scrum_master.app.Foo.doSomething())
    Doing something
    call(byte[] org.ietf.jgss.Oid.getDER())
    call(long de.scrum_master.app.Application.getPID())
    Process ID (PID) = 13748
    call(TypeCode org.omg.CORBA.IntHolder._type())
    call(DOMImplementationRegistry org.w3c.dom.bootstrap.DOMImplementationRegistry.newInstance())
    call(DOMImplementation org.w3c.dom.bootstrap.DOMImplementationRegistry.getDOMImplementation(String))
    call(void org.xml.sax.InputSource.setEncoding(String))
    call(int javax.swing.event.EventListenerList.getListenerCount())
    call(String[] com.sun.beans.finder.BeanInfoFinder.getPackages())
    call(boolean sun.util.calendar.CalendarUtils.isGregorianLeapYear(int))
    

    Update: I forgot to mention the alternative of using an execution() pointcut instead of call(), because they

    • are more efficient because they create bytecode where a method is executed, not in a hundred places where it is called,
    • are only woven into your own code by default, i.e. you do not weave into JDK binaries anyway and thus JDK method executions are ignored by default. The simplified aspect becomes:
    package de.scrum_master.aspect;
    
    public aspect NonJDKInterceptor {
        before() : execution(* *(..)) {
            System.out.println(thisJoinPoint);
        }
    }
    

    The log output is similar to the first one, but now it also lists the execution of the main(..) method:

    execution(void de.scrum_master.app.Application.main(String[]))
    execution(void de.scrum_master.app.Application.sayHelloTo(String))
    Hello world
    execution(void de.scrum_master.app.Foo.doSomething())
    Doing something
    execution(long de.scrum_master.app.Application.getPID())
    Process ID (PID) = 13916
    

    I think this is the simple and efficient solution you want. May answer about call() pointcuts was just what you asked for.