Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-users] Add my aspect to different projects

Well, that's exactly why I was asking how you intend to use your aspects. My understanding was that you have access to the code and can weave into it at compile time. Obviously you cannot. So either you use the ugly callstacks or you make sure your other applications run with LTW (load-time weaving) enabled, then you can weave call() pointcuts into the other in-house modules and just use my example as specified in my previous e-mail. If LTW is for some reason unwanted, use the callstacks. But I think this is not a solution for the real problem (your team works with AOP, the others don't want or care about it), only a workaround.

[Mike] We create a framework used for writing testcases. We have control over this source code and can make updates to it. Then each "area" are testing different aspects ( no not that aspect 😊 ) of the products like robustness and stability. They use our framework that is makes calls to our public API. They compile their tests using our framework ( jar). We are not allowed to change their code in their test code repos. Sort of their domain.


One more remark to what you said in a previous message:
We have had issues when forcing people to change. Some users is afraid of change. So it is merly a "soft" issue. Will the new method be as stable as old.
I think you have a serious issue with your service governance. You do not execute it because people are afraid? What are they afraid of? Has your team a history of breaking applications with new service versions? If so, make sure to test your APIs better. If not, use "declare error" and force them to upgrade. But "declare warning/error" both require the apps depending on your module to be compiled with the AspectJ compiler and with your aspects on the aspect path, so you cannot decide that alone. The guy or team responsible for the governance would have to decide that.

I think it happened once. We now have better tests to ensure quality but this does not matter since people remember that one time 😞
I used aspects since it did not want to make change on the user side. So this means we cannot use "declare warning/error".

br,

//mike




 

 


Från: aspectj-users-bounces@xxxxxxxxxxx <aspectj-users-bounces@xxxxxxxxxxx> för Alexander Kriegisch <alexander@xxxxxxxxxxxxxx>
Skickat: den 20 mars 2018 11:44
Till: aspectj-users@xxxxxxxxxxx
Ämne: Re: [aspectj-users] Add my aspect to different projects
 

Hi Mikael.

Well, that's exactly why I was asking how you intend to use your aspects. My understanding was that you have access to the code and can weave into it at compile time. Obviously you cannot. So either you use the ugly callstacks or you make sure your other applications run with LTW (load-time weaving) enabled, then you can weave call() pointcuts into the other in-house modules and just use my example as specified in my previous e-mail. If LTW is for some reason unwanted, use the callstacks. But I think this is not a solution for the real problem (your team works with AOP, the others don't want or care about it), only a workaround.

One more remark to what you said in a previous message:

We have had issues when forcing people to change. Some users is afraid of change. So it is merly a "soft" issue. Will the new method be as stable as old.

I think you have a serious issue with your service governance. You do not execute it because people are afraid? What are they afraid of? Has your team a history of breaking applications with new service versions? If so, make sure to test your APIs better. If not, use "declare error" and force them to upgrade. But "declare warning/error" both require the apps depending on your module to be compiled with the AspectJ compiler and with your aspects on the aspect path, so you cannot decide that alone. The guy or team responsible for the governance would have to decide that.

--
Alexander Kriegisch
https://scrum-master.de
 

Mikael Petterson schrieb am 20.03.2018 14:18:

I appreciate the input . I will definitely simplify my aspect and add a warning.

The only concern  I have is about finding the calling class. You have aspect and calling class in the same application. In my case I classify that as an internal call e.g.
classes in our framework calling deprecated classes in in the same framework. That is no problem to us since we have control over our framework classes.

The problem appears when other java applications ( also within the company but in other branches) call a deprecated method in the framework. Then I could not use the:
"Deprecated method " + thisJoinPoint.getSignature() +
          " called by " + thisEnclosingJoinPointStaticPart.getSignature()
This would not give me the name of the calling , in bold, class. So I had to retrieve the stack trace for  the target class and it contains a lot of information that has to be filtered out. Maybe I have missed something ?

 

Till: aspectj-users@xxxxxxxxxxx
Ämne: Re: [aspectj-users] Add my aspect to different projects
 

I think you are over-engineering a bit with your helper classes (StackTraceHelper, Access, DeprecatedMethodData) and your complex aspect code. Keep it simple! How about just something like this?

 

    package com.google.common.annotations;
    
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    
    @Retention(RUNTIME)
    public @interface Beta {}

    package de.scrum_master.app;
    
    import java.util.Date;
    
    import com.google.common.annotations.Beta;
    
    public class Application {
      public void doSomething() {
        doSomethingDeprecatedBeta();
        doSomethingBeta();
        new Date(2018, 5, 5);
      }
    
      @Deprecated
      public void doSomethingDeprecated() {}
    
      @Beta
      public void doSomethingBeta() {
        doSomethingDeprecated();
      }
    
      @Deprecated
      @Beta
      public void doSomethingDeprecatedBeta() {}
    
      public static void main(String[] args) {
        Application application = new Application();
        application.doSomething();
        application.doSomethingDeprecated();
        application.doSomethingBeta();
        application.doSomethingDeprecatedBeta();
      }
    }

    package de.scrum_master.aspect;
    
    public aspect DeprecatedMethodLogger {
      pointcut deprecated() :
        @annotation(Deprecated) &&
        (call(public * de.scrum_master..*(..)) || call(public de.scrum_master..new(..)));
    
      pointcut beta() :
        @annotation(com.google.common.annotations.Beta);
    
      pointcut deprecatedMethods() :
        deprecated() && !beta();
    
      declare warning : deprecatedMethods() :
        "Deprecated method called, please refactor";
    
      before() : deprecatedMethods() {
        System.out.println(
          "Deprecated method " + thisJoinPoint.getSignature() +
          " called by " + thisEnclosingJoinPointStaticPart.getSignature()
        );
      }
    }


Now you have both compiler warnings and runtime logging. If you prefer compiler errors, just change "declare warning" to "declare error". BTW, I have also included deprecated constructor calls. If you like you can also expand the pointcut to include deprecated classes.

The console log looks like this:

Deprecated method void de.scrum_master.app.Application.doSomethingDeprecated() called by void de.scrum_master.app.Application.doSomethingBeta()
Deprecated method void de.scrum_master.app.Application.doSomethingDeprecated() called by void de.scrum_master.app.Application.main(String[])
Deprecated method void de.scrum_master.app.Application.doSomethingDeprecated() called by void de.scrum_master.app.Application.doSomethingBeta()

In Eclipse with AJDT it looks like this (please note that there is no warning for the deprecated JDK Date constructor call because you said you only want to target your own companie's classes):

 

Regards

--
Alexander Kriegisch
https://scrum-master.de

 

Mikael Petterson schrieb am 16.03.2018 13:55:
Hi,

Thanks for reply Alexander. I try to answer your questions:
 
  • How are you using the aspect in your code? Do you compile the whole code with the AspectJ compiler (Ajc) or is the aspect in a separate JAR and used via LTW?
        [Mike] currently we use ajc and weave it into our product at compile time. But I want to have a separate jar so I can use it with other products.
  • How do you intend to use it for other products? LTW? Binary post-compile weaving? Compilation with Ajc? I guess it depends on what you mean by "other products" and whether they are in-house or 3rd party.
    [Mike] It is inhouse products. And I want to add aspect ( and all its helper classes) into a seprate jar and use that when I compile the products.
  • What do you need the stacktraces for? Please elaborate and explain as detailed as possible.
    [Mike] When the target class is called I also get the stacktrace elements
    Thread.currentThread().getStackTrace()
    >From it I can find the class that calls our deprecated method. Startingpoint is target class. There are irrelevant information in stacktrace that is not applicable ( classes that are part of the java API or test frameworks).
    We also look up information in gerrit and push data to a db. So there are more classes.
     
  • Is your intention to find usages of your own deprecated methods or that of 3rd party code, maybe even JRE/JDK?
    [Mike] Yes it is our own deprecated methods. No 3rd party code or JRE/JDK that we why I analyze the stacktrace.
  • Have you considered using the AspectJ features "declare error" or "declare warning" and just get rid of deprecated class/method usage right during compilation instead of during runtime, using expensive stacktrace analysis?
    [Mike] I guess this means that users have to get rid of their deprecated method usage at compile time. It is an interesting option.
    Do you have an example on how it works. WIll compilation fail?
  • As for your original question, the answer could be anything from implementing an interface as you suggest to using an abstract base aspect. But really, this looks like a "declare warning" use case to me.
    [Mike] We have had issues when forcing people to change. Some users is afraid of change. So it is merly a "soft" issue. Will the new method be as stable as old.
  • Would you mind giving us a full picture with the stacktrace helper class and some simple sample code? An [SSCCE](http://sscce.org/) maybe?
    [Mike] Sure I will include it here ( see below).

Sample code:
 
package com.company.aspect;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
/**
 * Helper class for stacktraces
 */
class StackTraceHelper {
    private ListIterator<StackTraceElement> iterator = null;
    private StringBuilder builder = null;
    private final List<String> excludedPackages = new ArrayList<>();
    public StackTraceHelper(List<StackTraceElement> stackTraceElements) {
        this.iterator = stackTraceElements.listIterator();
        stackTraceToString(stackTraceElements);
    }
    /**
     * Find matching stack trace element and set iterator.
     *
     * @param className
     * @return {@link StackTraceElement} matching className
     */
    public StackTraceElement matchClassName(String className) {
        while (iterator.hasNext()) {
            StackTraceElement ste = iterator.next();
            if (className.equals(ste.getClassName())) {
                return ste;
            }
        }
        //TODO: warn that no StackTraceElement was found for Name.
        return null;
    }
    public StackTraceElement next() {
        return iterator.next();
    }
    public boolean hasNext() {
        return iterator.hasNext();
    }
    public boolean hasPrevious() {
        return iterator.hasPrevious();
    }
    public StackTraceElement previous() {
        iterator.previous();
        return iterator.previous();
    }
    @Override
    public String toString() {
        return builder.toString();
    }
    DeprecatedMethodData extractData(String targetClass) {
        DeprecatedMethodData dmd = new DeprecatedMethodData();
        dmd.setTargetClassName(targetClass);
        //This is the called  class so we need to make sure that next class is a test class
        //otherwise we are not interested.
        StackTraceElement steTarget = this.matchClassName(targetClass);
        if (steTarget != null) {
            //we have a match of target in stack trace
            while (this.hasNext()) {
                StackTraceElement ste = this.next();
                String clazzName = ste.getClassName();
                String methodName = ste.getMethodName();
                if (this.isTestClass(clazzName) && !this.isExternalFramework(clazzName)) {
                //if (this.isTestClass(clazzName)) {//TODO: Fix
                    dmd.setCallingClassName(clazzName);
                    dmd.setCallingMethodName(methodName);
                    dmd.setDeprecatedClassName(steTarget.getClassName());
                    dmd.setDeprecatedMethodName(steTarget.getMethodName());
                    dmd.setAccess(Access.EXTERNAL);
                    break;
                }
                dmd.setAccess(Access.INTERNAL);
            }
        }
        //Add complete stacktrace to find problems with current algorithm.
        dmd.setStackTrace(this.builder.toString());
        return dmd;
    }
    /**
     * Must be called first thing since next operations
     * will affect this.
     */
    private void stackTraceToString(List<StackTraceElement> stackTraceElements) {
        final ListIterator<StackTraceElement> iter = stackTraceElements.listIterator();
        builder = new StringBuilder();
        while (iter.hasNext()) {
            builder.append(iter.next().toString());
        }
    }
    public boolean isInternal(String clazz) {
        if (clazz
                .matches(
                "^(com.company.manager|com.company.helpers|"
                        + "com.company.actions|com.company.configuration|"
                        + "com.company.demo).*")) {
            //LogerHelper.log("internal class:" + clazz);
            return true;
        }
        return false;
    }
    public boolean isJava(String clazz) {
        if (clazz.startsWith("sun") ||
                clazz.startsWith("java")) {
            //LogerHelper.log("java class:" + clazz);
            return true;
        }
        return false;
    }
    public boolean isTestClass(String clazz) {
        if (isInternal(clazz) || isJava(clazz)) {
            return false;
        }
        return true;
    }
    public boolean isExternalFramework(String clazz) {
        if (clazz.startsWith("org.testng") || clazz.startsWith("org.apache.maven.surefire")) {
            return true;
        }
        return false;
    }
    
    //TODO: Not ready yet.
//    public void excludePackages(List<String> packageNames) {
//        excludedPackages.addAll(packageNames);
//    }
//
//    boolean exclude(String clazz) {
//        for (String exclude : excludedPackages) {
//            if (clazz
//                    .matches(
//                    "^(" + exclude + ").*")) {
//                return true;
//            }
//        }
//        return false;
//    }
//
//    boolean isTestClass2(String clazz) {
//        return exclude(clazz);
//    }
}
             
 


 
 

 

 


Från: aspectj-users-bounces@xxxxxxxxxxx <aspectj-users-bounces@xxxxxxxxxxx> för Mikael Petterson <mikaelpetterson@xxxxxxxxxxx>
Skickat: den 15 mars 2018 14:35
Till: aspectj-users@xxxxxxxxxxx
Ämne: [aspectj-users] Add my aspect to different projects
 
Hi,

I have created an aspect that can be used to track calls to deprecated methods in your product. It is currently implemented in "our product" but I want to make a library out of it so we can use it for other products.

The aspect currently looks something like:
 
aspect DeprecatedMethodLogger {
 
   
    pointcut includeDeprecated():execution(public  * *.*(..)) && @annotation(Deprecated);
    pointcut excludeBeta():execution(public  * *.*(..)) && !@annotation(com.google.common.annotations.Beta);
    pointcut deprecatedMethods():  includeDeprecated() && excludeBeta();
    before() :deprecatedMethods()  {
        if (thisJoinPoint.getTarget() != null) {
            String targetClass = thisJoinPoint.getTarget().getClass().getName();
            List<StackTraceElement> stackTraceElements = Arrays.asList(Thread.currentThread().getStackTrace());
            StackTraceHelper stackTraceHelper = new StackTraceHelper(stackTraceElements);
            DeprecatedMethodData deprecatedMethodData = stackTraceHelper.extractData(targetClass);
          
 //There is also some additional connections to db and other tools here.

}

 

In stackTraceHelper there are a number of settings that can be done what is relevant in the trace when we try to find the calling class.

These settings I want to to publish so they can be set for each product using this library.

 

So question is if I in all products using it need to create another aspect that inherits this one and can set the relevant properties.

 

 

so my aspect above would have:

 

aspect DeprecatedMethodLogger implements DeprecatedMethodLoggerApi {

 

}

 

DeprecatedMethodLoggerApi contains setting that can be done.

 

Then in the products I create:

 

aspect MyDepreactedMethodLogger extends DeprecatedMethodLogger {

 

}

 

Or is there a better way? Suggestions welcome.

 

br,

 

//mike

 

 

 

 


Back to the top