Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AspectJ load-time weaving and class-level annotations #5061

Closed
wants to merge 1 commit into from
Closed

Fix AspectJ load-time weaving and class-level annotations #5061

wants to merge 1 commit into from

Conversation

mihalyr
Copy link
Contributor

@mihalyr mihalyr commented May 9, 2024

After fixing the pointcut syntax in the previous commit it revealed that the implementation has further errors. The pointcuts matched also constructors where the code assumed it gets a MethodSignature at runtime it got a ConstructorSignature and failed the unchecked class-cast.

Extended the pointucts to only match method signatures. Tested with AspectJ load-time weaving locally and with the existing Spring AOP proxy tests.

@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

The load-time weaving test are not part of the PR as they were causing unrelated build failures. They should be added by someone familiar with this project structure, the code can be found in this draft: #5060

@@ -161,7 +161,7 @@ public TimedAspect(MeterRegistry registry, Function<ProceedingJoinPoint, Iterabl
this.shouldSkip = shouldSkip;
}

@Around("@within(io.micrometer.core.annotation.Timed) && !@annotation(io.micrometer.core.annotation.Timed)")
@Around("@within(io.micrometer.core.annotation.Timed) && !@annotation(io.micrometer.core.annotation.Timed) && execution(* *(..))")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added part matches only method signatures (with a return type) so it will not apply to constructors that caused the runtime failures.

Comment on lines -205 to 219
@Around(value = "@annotation(counted)", argNames = "pjp,counted")
@Around("execution (@io.micrometer.core.annotation.Counted * *.*(..))")
@Nullable
public Object interceptAndRecord(ProceedingJoinPoint pjp, Counted counted) throws Throwable {
public Object interceptAndRecord(ProceedingJoinPoint pjp) throws Throwable {
if (shouldSkip.test(pjp)) {
return pjp.proceed();
}

Method method = ((MethodSignature) pjp.getSignature()).getMethod();
Counted counted = method.getAnnotation(Counted.class);
if (counted == null) {
method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
counted = method.getAnnotation(Counted.class);
}

return perform(pjp, counted);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I aligned this method with TimedAspect not sure why were there two different approaches for the same, but the previous pointcut declaration wasn't working properly for me and this is also more consistent with ObservedAspect and TimedAspect too.

@mihalyr mihalyr marked this pull request as ready for review May 9, 2024 20:06
After fixing the pointcut syntax in the previous commit it revealed that
the implementation has further errors. The pointcuts matched also
constructors where the code assumed it gets a MethodSignature at runtime
it got a ConstructorSignature and failed the unchecked class-cast.

Extended the pointucts to only match method signatures. Tested with
AspectJ load-time weaving locally and with the existing Spring AOP proxy
tests.
@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

@marcingrzejszczak @jonatan-ivanov Could you please merge this? My previous changed revealed a more serious issue that is actually causing runtime failures in client-code under certain conditions.

There is some japi failure that I have no clue what it is and how to fix it. But the whole build and tests are working othewise.

@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

This is the content of japi.txt from my local run:

Comparing binary compatibility of  against 
***! MODIFIED CLASS: PUBLIC io.micrometer.core.aop.CountedAspect  (not serializable)
	===  CLASS FILE FORMAT VERSION: 52.0 <- 52.0
	===  UNCHANGED SUPERCLASS: java.lang.Object (<- java.lang.Object)
	---! REMOVED METHOD: PUBLIC(-) java.lang.Object interceptAndRecord(org.aspectj.lang.ProceedingJoinPoint, io.micrometer.core.annotation.Counted)
		---  REMOVED EXCEPTION: java.lang.Throwable
***  MODIFIED CLASS: PUBLIC io.micrometer.core.instrument.config.InvalidConfigurationException  (default serialVersionUID changed)
	===  CLASS FILE FORMAT VERSION: 52.0 <- 52.0
	===  UNCHANGED SUPERCLASS: java.lang.IllegalStateException (<- java.lang.IllegalStateException)

The first message about the removed method is expected since I changed the method to fix the implementation. The second I have no idea what is it about, I haven't touched InvalidConfigurationException...

@jonatan-ivanov
Copy link
Member

japicmp is a tool we use to have binary compatibility reporting for our builds.
It downloads an earlier minor release and it compares it with the current binary if there are breaking changes in them (minor versions should not contain source/binary incompatible changes).

The first issue in the report is the problem that broke the build, changing this:

public Object interceptAndRecord(ProceedingJoinPoint pjp, Counted counted) throws Throwable {

to this:

public Object interceptAndRecord(ProceedingJoinPoint pjp) throws Throwable {

breaks source and binary compatibility so we should not do it in a minor release. On the other hand, the whole aspect class with this method in it should not be used from user code and AspectJ will be able to deal with this change I assume so I think we can add this class to the ignore list (I can help ignoring this). Btw can these methods have default visibility or do we need to keep them public? That would fix the binary incompatibility issue but it might break AspectJ.

The second issue in the report is something we can look into but it does not fail the build, you can handle it as a warning.

Locally you can disable this with ./gradlew build -x japicmp.

@mihalyr
Copy link
Contributor Author

mihalyr commented May 10, 2024

Thank you for the details, it makes sense now. I'll take a look if default visibility works, but as you said, it's an aspect and not something a user should depend on directly...hopefully 🙂

Let me rework these PRs a little bit, first let's revert my commit from main, then I'll close these and create a PR that comes as a whole, with the pointcut fixes and LTW tests. Then on top of it we can add the compile-time weaving as the next step to have fully working aspects.

There is one thing I noticed, Metrics includes a global MeterRegistry, which can be used during weaving since it is done automatically by AspectJ without user involvement. However, for the @Observed annotation we need also a way to use a default ObservationRegistry with and add a default constructor to ObservedAspect. I think @marcingrzejszczak has something for this already in his CTW PR draft, so I could use the same approach perhaps also for the LTW. Or we can do all this together on Marcin's PR and cover LTW and CTW and the pointcut fixes too in one go of you would prefer that.

@marcingrzejszczak
Copy link
Contributor

There is one thing I noticed, Metrics includes a global MeterRegistry, which can be used during weaving since it is done automatically by AspectJ without user involvement. However, for the @observed annotation we need also a way to use a default ObservationRegistry with and add a default constructor to ObservedAspect. I think @marcingrzejszczak has something for this already in his CTW PR draft, so I could use the same approach perhaps also for the LTW. Or we can do all this together on Marcin's PR and cover LTW and CTW and the pointcut fixes too in one go of you would prefer that.

I think we can work on this in my PR. So you can create a PR to my branch and we will work on the weaving feature in one go

@mihalyr
Copy link
Contributor Author

mihalyr commented May 10, 2024

Cool, let's do that!

Could you please revert my commit from main 5e16809 ? It's not safe without some other changes which should be addressed together and don't want to risk your release.

@marcingrzejszczak
Copy link
Contributor

Done via af7093f

@mihalyr mihalyr closed this May 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants