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 pointcut syntax #5058

Merged
merged 1 commit into from
May 9, 2024
Merged

Fix AspectJ pointcut syntax #5058

merged 1 commit into from
May 9, 2024

Conversation

mihalyr
Copy link
Contributor

@mihalyr mihalyr commented May 9, 2024

AspectJ pointcuts use the && and ! operators and the alternative word form syntax with and and not operators is only allowed for load-time weaving with aop.xml which cannot use the symbols in the XML. This fixes compile-time weaving support which failed on the and not syntax.

AspectJ pointcuts use the `&&` and `!` operators and the alternative
word form syntax with `and` and `not` operators is only allowed for
load-time weaving with `aop.xml` which cannot use the symbols in the XML.
This fixes compile-time weaving support which failed on the `and not`
syntax.
@pivotal-cla
Copy link

@mihalyr Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

The previous syntax was introduced recently in #3811

Related discussion about the correct syntax in #1149 (comment)

@pivotal-cla
Copy link

@mihalyr Thank you for signing the Contributor License Agreement!

@marcingrzejszczak marcingrzejszczak added this to the 1.13.0 milestone May 9, 2024
@marcingrzejszczak marcingrzejszczak merged commit 5e16809 into micrometer-metrics:main May 9, 2024
6 checks passed
@marcingrzejszczak marcingrzejszczak added the bug A general bug label May 9, 2024
@jonatan-ivanov
Copy link
Member

Thank you for fixing this! Do you think we should open an issue for this in AspectJ?
I mean if AspectJ could print out a warning, less people would fall into this trap.

@jonatan-ivanov jonatan-ivanov added module: micrometer-core An issue that is related to our core module module: micrometer-observation labels May 9, 2024
@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

Hi @jonatan-ivanov there isn't really a problem with AspectJ, more like Micrometer is missing tests for this functionality.

There are two ways basically how you can use the aspects with AspectJ:

Compile-time weaving

If your library was compiled (or post-compiled) with the AspectJ compiler (ajc) like the discussion we have in #1149, then the aspects could be woven into classes at compile time by others. If your project would be setup this way, you would get compile error and your build would fail with a message like this:

io/micrometer/core/aop/TimedAspect.java [error] Invalid pointcut '@within(io.micrometer.core.annotation.Timed) and not @annotation(io.micrometer.core.annotation.Timed)': org.aspectj.weaver.patterns.ParserException: unexpected pointcut element: and@45:47 at position 45

However, at the moment you don't have an AspectJ compilation step in your build process (see #1149) so you don't get any error at build time.

Load-time weaving

People can also use your aspects with AspectJ at load time. In this case the error happens at runtime when the class is loaded and presented to the AspectJ weaver at execution time. You should probably have tests for this to catch these errors. Otherwise your aspects would throw exceptions for users at runtime like this:

[AppClassLoader@4e0e2f2a] error at io/micrometer/core/aop/TimedAspect.java::0 Invalid pointcut '@within(io.micrometer.core.annotation.Timed) and not @annotation(io.micrometer.core.annotation.Timed)': org.aspectj.weaver.patterns.ParserException: unexpected pointcut element: and@45:47 at position 45

And some tests should fail as the aspect is not run. Now that I tried to repro it not sure how well these class annotations work, I got some runtime failures when I tried to use them. The method annotations should be OK, but some tests should be perhaps included to make sure users get correct metrics.

@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

And I forgot the third approach that you use in your tests actually, using Spring AspectJ proxy at runtime, which perhaps is able to deal with the alternative expression syntax. But would be good to add some AspectJ LTW tests to make sure things work as intended also if you don't use Spring proxies.

I've seen some aop.xml added in https://github.com/micrometer-metrics/micrometer/pull/5059/files probably @marcingrzejszczak was adding also some LTW tests to the project there.

@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

@marcingrzejszczak @jonatan-ivanov it seems that this change revealed some other issues with these aspects.

Before this PR my local tests with load-time weaving succeeded for @Counted and @Timed on methods, but didn't work on class-level due to the pointcut syntax error so it was just ignored. But now that I fixed the pointcut the aspect is failing because it matches things like static constructors and fails the program at runtime.

class org.aspectj.runtime.reflect.ConstructorSignatureImpl cannot be cast to class org.aspectj.lang.reflect.MethodSignature (org.aspectj.runtime.reflect.ConstructorSignatureImpl and org.aspectj.lang.reflect.MethodSignature are in unnamed module of loader 'app')
java.lang.ClassCastException: class org.aspectj.runtime.reflect.ConstructorSignatureImpl cannot be cast to class org.aspectj.lang.reflect.MethodSignature (org.aspectj.runtime.reflect.ConstructorSignatureImpl and org.aspectj.lang.reflect.MethodSignature are in unnamed module of loader 'app')
	at io.micrometer.core.aop.TimedAspect.timedClass(TimedAspect.java:171)
	at io.micrometer.MeasuredClass.<init>(MeasuredClass.java:7)
	at io.micrometer.MeasuredClassTest.setUp(MeasuredClassTest.java:25)

Here is my test code https://github.com/mihalyr/micrometer/commit/427fd4f579ff1c58794045347c6266467aae4610

@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

@marcingrzejszczak @jonatan-ivanov so there is a more serious bug that was just revealed by this PR. Before this PR the class-level annotation pointcut was just ignored due to the syntax issue with AspectJ LTW. After this PR however, the pointcut started to work and matches also constructors that cause runtime failures of the user programs when calling a constructor on such annotated classes, because the Timed/Counted aspect implementations assume a MethodSignature but they get a ConstructorSignature instance.

I have a fix in this draft PR including AspectJ LTW tests that will help you find these issues next time. #5060

However, I don't really have time to help you clean this up and adjust for your project, especially the tests are very problematic there, because they need AspectJ and the current version will not work with JDK11 that you also build for.

@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

I think, I'll just extract the pointcut fixes from #5060 and send it separately, that should work and then you can later add some tests according to your priorities and in a way that suits the project.

@mihalyr
Copy link
Contributor Author

mihalyr commented May 9, 2024

Sent a fix in #5061

@jonatan-ivanov
Copy link
Member

Hi @jonatan-ivanov there isn't really a problem with AspectJ

From my perspective (user of AspectJ), there is: if AspectJ knows (I assume) that I'm doing something that I should not, it could warn me but I would not call this a "problem" but it would be a nice gesture towards the users.

Based on the other things you wrote and since we are post-RC and our GA release is on Monday, I think we should revert your fix in gh-5058 and apply it with your other fixes after the release. Please don't get me wrong, I think we should fix these things and I really appreciate your help on this but I think we should do this in the next release so that we have more time to find issues and write tests. /cc @marcingrzejszczak @shakuzen

@mihalyr
Copy link
Contributor Author

mihalyr commented May 10, 2024

Hi @jonatan-ivanov the AspectJ compiler/weaver really tells you what is wrong, it is showing you errors and the code doesn't work. But you don't have tests for that, because you use Spring AOP proxies and not AspectJ CTW/LTW in your tests, and the proxies seem to work differently from the actual compiler/weaver. That's why I suggested that you should add also AspectJ tests to have these things covered and then you would see the errors and get test failures too.

Regarding the revert, yes, that's the safe decision. I didn't know what stage you are in right now and I wanted to suggest that if you can't merge the other fixes, then please revert my change from main as an ignored aspect does not harm that much as a failing one.

Thank you for your quick response and really appreciate taking the time to help out with this.

marcingrzejszczak added a commit that referenced this pull request May 10, 2024
@jonatan-ivanov jonatan-ivanov removed this from the 1.13.0 milestone May 10, 2024
@jonatan-ivanov
Copy link
Member

jonatan-ivanov commented May 10, 2024

The changes in this PR were reverted in af7093f we are planning to apply AspectJ fixes after our release (2024-05-13).

@mihalyr
Copy link
Contributor Author

mihalyr commented May 10, 2024

@jonatan-ivanov I have all the fixes now in #5064 that goes into Marcin's branch with all LTW/CTW stuff together. The binary compatibility issue is also fixed in that PR, so once we are able to get the Spring fix from 6.1.7-SNAPSHOT for the build, it should all be good for the next release hopefully.

marcingrzejszczak added a commit that referenced this pull request May 13, 2024
* Revert "Fix AspectJ pointcut syntax (#5058)"

This reverts commit 5e16809.

* Fix AspectJ load-time weaving and class-level annotations

Fix AspectJ pointcut syntax to use `&& !` instead `and not` which is
invalid for the AspectJ compiler/weaver and only works with the Spring
AOP implementation. Also add `&& execution(* *.*(..))` to match only
methods, because the implementation assumes it gets only
MethodSignatures and crashes on ConstructorSignature at runtime.

Fixed the thread-safety and mutability issues with the singleton
Observations class, so changes are propagated to aspects that are
initialized only once.

Added AspectJ load-time weaving tests to make sure that the further
issues with pointcuts and aspects are noticed at build time.

* Add more AspectJ compile-time tests

Added more class-level annotation tests and moved the module to
'micrometer-test-aspectj-ctw' to align with
'micrometer-test-aspectj-ltw'.

* Revert CountedAspect method change with simpler syntax

A fix kindly provided by @kriegaex that avoids changing the method
signature and thus breaking binary compatibility and still fixes the
problem with double counting in AspectJ.

See the explanation in
#1149 (comment)

---------

Co-authored-by: Marcin Grzejszczak <marcin@grzejszczak.pl>
marcingrzejszczak added a commit that referenced this pull request May 15, 2024
* Revert "Fix AspectJ pointcut syntax (#5058)"

This reverts commit 5e16809.

* Fix AspectJ load-time weaving and class-level annotations

Fix AspectJ pointcut syntax to use `&& !` instead `and not` which is
invalid for the AspectJ compiler/weaver and only works with the Spring
AOP implementation. Also add `&& execution(* *.*(..))` to match only
methods, because the implementation assumes it gets only
MethodSignatures and crashes on ConstructorSignature at runtime.

Fixed the thread-safety and mutability issues with the singleton
Observations class, so changes are propagated to aspects that are
initialized only once.

Added AspectJ load-time weaving tests to make sure that the further
issues with pointcuts and aspects are noticed at build time.

* Add more AspectJ compile-time tests

Added more class-level annotation tests and moved the module to
'micrometer-test-aspectj-ctw' to align with
'micrometer-test-aspectj-ltw'.

* Revert CountedAspect method change with simpler syntax

A fix kindly provided by @kriegaex that avoids changing the method
signature and thus breaking binary compatibility and still fixes the
problem with double counting in AspectJ.

See the explanation in
#1149 (comment)

---------

Co-authored-by: Marcin Grzejszczak <marcin@grzejszczak.pl>
marcingrzejszczak added a commit that referenced this pull request May 20, 2024
* Revert "Fix AspectJ pointcut syntax (#5058)"

This reverts commit 5e16809.

* Fix AspectJ load-time weaving and class-level annotations

Fix AspectJ pointcut syntax to use `&& !` instead `and not` which is
invalid for the AspectJ compiler/weaver and only works with the Spring
AOP implementation. Also add `&& execution(* *.*(..))` to match only
methods, because the implementation assumes it gets only
MethodSignatures and crashes on ConstructorSignature at runtime.

Fixed the thread-safety and mutability issues with the singleton
Observations class, so changes are propagated to aspects that are
initialized only once.

Added AspectJ load-time weaving tests to make sure that the further
issues with pointcuts and aspects are noticed at build time.

* Add more AspectJ compile-time tests

Added more class-level annotation tests and moved the module to
'micrometer-test-aspectj-ctw' to align with
'micrometer-test-aspectj-ltw'.

* Revert CountedAspect method change with simpler syntax

A fix kindly provided by @kriegaex that avoids changing the method
signature and thus breaking binary compatibility and still fixes the
problem with double counting in AspectJ.

See the explanation in
#1149 (comment)

---------

Co-authored-by: Marcin Grzejszczak <marcin@grzejszczak.pl>
@mihalyr mihalyr mentioned this pull request May 20, 2024
shakuzen pushed a commit that referenced this pull request Jun 19, 2024
* Revert "Fix AspectJ pointcut syntax (#5058)"

This reverts commit 5e16809.

* Fix AspectJ load-time weaving and class-level annotations

Fix AspectJ pointcut syntax to use `&& !` instead `and not` which is
invalid for the AspectJ compiler/weaver and only works with the Spring
AOP implementation. Also add `&& execution(* *.*(..))` to match only
methods, because the implementation assumes it gets only
MethodSignatures and crashes on ConstructorSignature at runtime.

Fixed the thread-safety and mutability issues with the singleton
Observations class, so changes are propagated to aspects that are
initialized only once.

Added AspectJ load-time weaving tests to make sure that the further
issues with pointcuts and aspects are noticed at build time.

* Add more AspectJ compile-time tests

Added more class-level annotation tests and moved the module to
'micrometer-test-aspectj-ctw' to align with
'micrometer-test-aspectj-ltw'.

* Revert CountedAspect method change with simpler syntax

A fix kindly provided by @kriegaex that avoids changing the method
signature and thus breaking binary compatibility and still fixes the
problem with double counting in AspectJ.

See the explanation in
#1149 (comment)

---------

Co-authored-by: Marcin Grzejszczak <marcin@grzejszczak.pl>
@frankjkelly
Copy link

Hi Folks - the above has me a little confused - after upgrading from Spring boot 3.2.2 to 3.3.1 I am getting the following

io/micrometer/core/aop/TimedAspect.java [error] Invalid pointcut '@within(io.micrometer.core.annotation.Timed) and not @annotation(io.micrometer.core.annotation.Timed)': org.aspectj.weaver.patterns.ParserException: unexpected pointcut element: and@45:47 at position 45

Right now is there anything I can do to work-around this?
Should I pin the aspectj version or micrometer version or do I need to wait for a new Spring release?
Thanks in advance!

@mihalyr
Copy link
Contributor Author

mihalyr commented Jul 2, 2024

@frankjkelly the changes from this PR were originally reverted from the 1.13.1 release, but a more complete implementation including this patch and proper AspectJ CTW/LTW support has been already pushed to main in 763e1f7 which is now available as 1.14.0-SNAPSHOT and probably will be released soon if everything goes fine.

Not sure why do you see this error only now, it was there all along if you used AspectJ LTW, it was showing the error at startup also on Spring Boot 3.2.x. But if you don't use @Timed annotations on class-level, you can probably safely just ignore it. The method-level @Timed annotations should still work, those don't have the invalid syntax in the pointcuts.

@frankjkelly
Copy link

@mihalyr Thanks for getting back to me - maybe my problem is similar but not this one as I am getting a compilation at the compile step with the following. So it does appear to be AspectJ post compile weaving complaining about something.
Seems to be the same message?

Note: /runner/_work/platform2-***/platform2-***/platform-***-service/src/main/java/com/cogito/platform/***/dao/ApiClientFactory.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: /runner/_work/platform2-***/platform2-***/platform-***-service/src/main/java/com/cogito/platform/***/dao/RoutingConfigDaoEntityImpl.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
5 warnings
Error: ometer/core/aop/TimedAspect.java [error] Invalid pointcut '@within(io.micrometer.core.annotation.Timed) and not @annotation(io.micrometer.core.annotation.Timed)': org.aspectj.weaver.patterns.ParserException: unexpected pointcut element: and@45:47 at position 45
	

1 error

> Task :platform-***-service:compileJava FAILED

@mihalyr
Copy link
Contributor Author

mihalyr commented Jul 2, 2024

@frankjkelly yes, that's the same issue, Micrometer doesn't work with compile-time weaving, the fix is in the commit waiting to be released I mentioned before. With load-time weaving you would get the error during runtime and could ignore, but CTW will fail the build on the syntax error. I had the same issue when I first switched to Micrometer from Dropwizard which led to this patch.

See also #1149 which was the main issue tracking AspectJ compile-time weaving support.

@mihalyr
Copy link
Contributor Author

mihalyr commented Jul 2, 2024

@frankjkelly
I forgot to mention that this pointcut syntax issue was introduced recently with this commit a7a0274

So you can revert to a Micrometer version before that commit until the fix from 1.14.0-SNAPSHOT is released.

@mihalyr
Copy link
Contributor Author

mihalyr commented Jul 2, 2024

@frankjkelly now I realized why you saw this with the Spring upgrade. That bad commit was added in Micrometer 1.13.0 which is what came with Spring Boot 3.3. Now, it makes sense. So, yeah, your choice for now is to stay with Micrometer 1.12.x, not sure how it works with SB 3.3, though. Alternatively, you can switch to load-time weaving to ignore the error, that's what I did as a workaround until waiting for the fix to be released.

@jonatan-ivanov
Copy link
Member

fyi: I'm backporting that change to 1.13.x: #5285

jonatan-ivanov added a commit that referenced this pull request Jul 3, 2024
Backport of: "Fix AspectJ pointcut syntax (#5058)"

AspectJ pointcuts use the `&&` and `!` operators and the alternative
word form syntax with `and` and `not` operators is only allowed for
load-time weaving with `aop.xml` which cannot use the symbols in the XML.
This fixes compile-time weaving support which failed on the `and not`
syntax.

Closes gh-5285
Backport-Issue: gh-5058

Co-authored-by: Jonatan Ivanov <jonatan.ivanov@gmail.com>
@mihalyr
Copy link
Contributor Author

mihalyr commented Jul 4, 2024

@jonatan-ivanov please do not backport this, it was reverted for a reason: #5058 (comment)

My complete fix is in #5064 that was merged to main in 763e1f7

Please use the changes from 763e1f7 instead for the Aspect classes. And consider the LTW tests at least.

@jonatan-ivanov
Copy link
Member

I reverted the change, sorry for jumping too quickly: #5285 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A general bug module: micrometer-core An issue that is related to our core module module: micrometer-observation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants