-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Arc - detect usage of unsupported @Specializes annotation and fail fast #49834
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
Conversation
Ladicek
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message is gonna be unwieldy if a lot of @Specializes occurences exist in the code base, but I that's a minor issue.
|
Hm, the CI failure is related. Apparently, we already have some
|
|
@phillip-kruger @jmartisk in SR graphql, is the In Quarkus it was just ignored up until this PR so the only usage must have been outside of Quarkus. |
|
The EDIT: actually let me shed a bit more light on that. The parent class is not a bean class in CDI Lite. It can still be a bean class in CDI Full with bean discovery mode of |
I am just running a local build of the graphql repo without the annotation (and the priority annotation) to see if there is some magic I might have missed but I agree it does look superfluous. |
|
I am happy if it works and you remove it. Probably just me or one of us being dumb :) it's also used in wildfly and open liberty, not sure if they need it. @jmartisk ? |
Thanks for quick reply, I have sent a PR against SR repo to get the CI result on WFLY testing as I am too lazy to execute those locally 😅 |
|
I'll leave this for @jmartisk |
This comment has been minimized.
This comment has been minimized.
|
Is it really needed to fail the build? What if an application is using a library that is shared with some other app that runs on a CDI container that supports it? Wouldn't a warning suffice? If we're about to start failing the build, I'd argue this should at least be marked with Nevertheless, for SmallRye GraphQL, I do believe the annotation is unnecessary. Let me run the Quarkus test suite locally with a build containing smallrye/smallrye-graphql#2299 but I assume it will work. |
Great example. That library would not work correctly on Quarkus. I think we could skip this check for known-compatible libraries, similarly to how we don't fail the build for known-compatible libraries that define bean discovery mode of |
|
I would simply log a warning, build failure is too aggressive. |
If such library actually relies on the specialized bean working, then I'd argue we might as well fail because that will lead to some unexpected state anyway.
I thought about that initially but I don't think just a log msg is very helpful as it's very easy to miss. But if others are in favor, I will of course change it. |
|
For the record, we have (for a long time; it is a shame we haven't acted upon it yet) this CDI issue jakartaee/cdi#646 that suggests detecting specialization (and usage of other unsupported APIs) and producing a deployment problem. I 100% agree with this PR (modulo perhaps the addition of known-compatible artifacts for which a deployment problem is not raised -- we already do that for |
|
My concern is that there might be users depending on some library that contains the annotation and they may be perfectly aware that the annotation doesn't work (it's in some unused code) and they won't be able to build. In that case they should at least be able to turn this off by configuration... (But adding a config property for something this trivial sounds like overkill, so that's why I would probably suggest just logging a warning instead) |
|
While I favor the fail option, I agree that we need a way for application to effectively say "I know I am breaking the rules but I am certain this will work as expected". We can move the annotation validation into This is akin to what we already have for |
| * Quarkus does not support {@link jakarta.enterprise.inject.Specializes} and will detect usage of this annotation | ||
| * and throw a build time error for any occurrence that is not registered through this build item. | ||
| */ | ||
| public final class KnownSpecializesAnnotationOccurrenceBuildItem extends MultiBuildItem { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to add a new build item instead of reusing the existing one?
I know its javadoc is focused on beans.xml, but we should be able to generalize that wording. And artifact granularity seems OK to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because they both serve a different purpose.
You can have an artifact with discovery mode all and register that as compatible but still have it contain @Specializes that is an error. Or vice versa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Furthermore, the KnownCompatibleBeanArchiveBuildItem is "only" used for a warning suppression during build whereas here we have a failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that, but I wouldn't like us add a new build item for each unsupported CDI Full feature (there's like 10 of them or so?) that we want to suppress for an archive / class / whatever. One seems like a reasonable number. Two starts feeling wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean...
I am thinking whether we could perhaps some Reason enum to KnownCompatibleBeanArchiveBuildItem that would specify what is that build item meant for. Or something along those lines.
I don't like the idea what we would consider such archive compatible in all current and future aspects.
But let's first see if we can agree on the "fail fast + build item" approach :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An enum with selection of what is known to be compatible sounds like a great idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or an enum set, actually. I guess you know that :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But let's first see if we can agree on the "fail fast + build item" approach :)
+1
Or an enum set, actually.
+1 for KnownCompatibleBeanArchiveBuildItem#ingoredUnsupportedFeatures (or a better name ;-) defaulted to BEANS_XML_ALL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for
KnownCompatibleBeanArchiveBuildItem#ingoredUnsupportedFeatures(or a better name ;-) defaulted toBEANS_XML_ALL.
I don't think we should default to BEANS_XML_ALL. I think we should:
- Add a builder API to create an instance.
- Deprecate the existing constructors and let them default to
BEANS_XML_ALL. - Everyone should use the builder API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 makes sense. I did not talk about a concrete implementation but the way of solution ;-).
|
I have reworked the PR in the following way:
The error message now looks like this:
|
This comment has been minimized.
This comment has been minimized.
|
@mkouba @Ladicek I've reworked Please re-review at your convenience :) |
This comment has been minimized.
This comment has been minimized.
| this.artifact = artifact; | ||
| } | ||
|
|
||
| public static Builder builder() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should have 2 builder() methods, one accepting groupId and artifactId, the other accepting groupId, artifactId and classifier (similarly to existing constructors that are being deprecated). These parameters are mandatory. The builder would then allow adding reasons: there may be multiple, KnownCompatibleBeanArchiveBuildItem should maintain a set, not just a single reason. Finally, build() would return a single KnownCompatibleBeanArchiveBuildItem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it should actually have a method for each, i.e. for groupId, artifactId and classifier. What's the point of KnownCompatibleBeanArchives.Key anyway?
public static class Builder {
Builder setGroupId(String groupId) {...}
Builder setArtifactId(String artifactId) {...}
Builder setClassifier(String classifier) {...}
Builder addReason(String reason) {...}
KnownCompatibleBeanArchiveBuildItem build() {...}
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that classifier, being optional, should be a method on the builder, but groupId and artifactId are mandatory. Since the number of mandatory members here is low, it feels better to enforce their setting at compile time rather than at runtime (build() would have to check if they were set). But I'd be fine with what you suggest too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they're mandatory then the right thing to do is IMO to add them as params to the builder() method and make the fields final in the builder class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not quite agree but since I am outnumbered, I have adjusted it accordingly :)
|
LGTM otherwise, but the builder looks really weird here :-) |
Repurpose KnownCompatibleBeanArchiveBuildItem to allow declaring compatibility despite using @specializes. Apply temporary fix for SR GraphQL.
This comment has been minimized.
This comment has been minimized.
Status for workflow
|
| Status | Name | Step | Failures | Logs | Raw logs | Build scan |
|---|---|---|---|---|---|---|
| ✔️ | JVM Integration Tests - JDK 17 | Logs | Raw logs | 🔍 | ||
| ✔️ | JVM Integration Tests - JDK 17 Windows | Logs | Raw logs | 🔍 | ||
| ❌ | JVM Integration Tests - JDK 21 | Build |
Failures | Logs | Raw logs | 🔍 |
You can consult the Develocity build scans.
Failures
⚙️ JVM Integration Tests - JDK 21 #
- Failing: integration-tests/observability-lgtm
📦 integration-tests/observability-lgtm
❌ Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.3:test (devmode-test) on project quarkus-integration-test-observability-lgtm:
See /home/runner/_work/quarkus/quarkus/integration-tests/observability-lgtm/target/surefire-reports for the individual test results.
See dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.
There was an error in the forked process
Flaky tests - Develocity
⚙️ JVM Tests - JDK 21
📦 extensions/smallrye-openapi/deployment
❌ io.quarkus.smallrye.openapi.test.vertx.OpenApiHttpRootPathCorsTestCase.testCorsFilterProperties - History
1 expectation failed. Expected status code <200> but was <500>.-java.lang.AssertionError
Details
java.lang.AssertionError:
1 expectation failed.
Expected status code <200> but was <500>.
at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:73)
Fixes #49812
Quarkus now detects usage of this annotation and throws an exception that looks something like this: