-
Notifications
You must be signed in to change notification settings - Fork 31
Document how to declare that your JPMS module uses JSpecify #495
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
Comments
transitive and static cannot both take effect at the same time |
is that true? I would think it'd be roughly equivalent to https://www.oracle.com/corporate/features/understanding-java-9-modules.html is like, we're going to glaze over that... reading it sounds like it would be optional, but any module, if it's available? would also be able to read it based on using mine... which doesn't sound mutually exclusive. |
I think and I’m tired so this is probably wrong
The sort of reason is JSpecify annotations are retention RUNTIME. I would say less than 1% of Java devs know this so it is a good point. edit I will say I have never done requires transitive and static both so maybe there are compat issues or maybe it doesn’t work but iirc it does. I use the eclipse annotations which are CLASS so requires static is good enough. |
I guess not, but modules that depend on it will need to have jspecify in the dependency tree or they'll get a module not found error. |
I’m not sure of that when it comes to annotations. Like if you are just sniffing via reflection which is precisely why you want the transitive for nullness. That is a third party runtime library trying to deduce if a field to inject is nullable or not. |
I wasn't at my computer before but it appears on googling @lukaseder had compat issues with Maven and I believe I did as well: jOOQ/jOOQ#13619 I will need to double check but I believe mine were with Javadoc of all things. Ultimately I don't think it matters as the annotations are Later today I will try experimenting. @bowbahdoe do you have any experience with |
@agentgt Yeah - i'm not too sure of a good usage for Annotations that aren't on the module path just become invisible, so its perfectly fine to not require the jspecify annotations downstream. For me, I think thats interesting/important only when you want to have a zero dependency library. Once you have one dependency might as well have two. I don't really understand the state of tooling either. |
|
I have one further comment about when annotations currently seem to be required on the compile class path, but I've only for sure seen this coming from things that you use the automatic modules. Since most libraries only use automatic modules currently. The example would be if spring had a non-null (on my phone) annotation that had a field that supported A Jspecify annotation, then, in my experience spring needs to export in some way jspecify as well or you will get compiler warnings. This only happens of course if you're using that specific annotation from spring. I do not know and haven't tested if this is a problem when full JPMS is used. I can link an issue I opened in spring boot with this recently if it's helpful. Since the solution and Gradle would be to use the API or compile only API dependency scope. |
With You can actually see this through my current two projects code bases of jstachio and rainbowgum where I constantly have to SuppressWarnings of "exported". @SuppressWarnings("exports")
public @Nullable /* this one is fine */ Object blah(List<@Nullable /* this one causes warnings */ Object> o) {
} EDIT I think I misunderstood you. Yes you will need to Speaking of which you should never |
For clarity on warnings I'm referring to issues like this (linking to reproducer comment) spring-projects/spring-boot#39901 (comment) spring-core currently has this exact kind of issue with jsr305 |
(further discussion of this on #503) |
log4j2 has had some kind of trouble with modules and/or OSGi in apache/logging-log4j2#2929 / apache/logging-log4j2#2930, in which they cite bndtools/bnd#2713. I think maybe they're autogenerating their |
My concern with Log4j2 doing that is they may access the JSpecify annotations in the future via reflection. This is because Log4j2 uses reflection to bind configuration last I checked unlike my own logging project rainbowgum (shameful plug) which generates reflection free configuration binding code using an annotation processor (it is jspecify aware to boot 😄 ). That is they better not pull the annotation with reflection to check if the option is optional otherwise a JLinked application will fail. I suppose I should raise that issue to the log4j2 team. |
Prompted by google/guava#7732 by @jjohannes (thanks!), I am reluctantly trying to learn things :) It seems very clear that Guava's total lack of a It seems that I would still like to keep investigating whether Relative to having a So, if I annotate my library with JSpecify annotations but use As for I should probably figure out some javac command lines that I can run that demonstrate some of the different behaviors we can see with the different options. |
Just a few thoughts, as I have been fighting with this topic quite a bit recently and opened the Guava PR linked above. I think what is "correct" is not that clear for when an annotation library (like JSpecify) is used by an OSS library (like Guava), because
I think the major "drawbacks" for consumers of Guava (the library that uses JSpecify) who "do not care" are the following:
That's why in the case of Guava I would choose option (1). |
fwiw, Caffeine uses option (1) which was the JPMS team's recommendation. You may want to also suppress a javac |
at the same time them doing this is problematic I have an open bug with errorprone because they are treating jakarta.validation as a nullity annotation in the same way as an annotation like jspecify would be used. So I think that these tools doing this is probably not a good idea.
Do they only need to requires static too? I mean yeah that kind of sucks but it's not super awful, see above point. |
Downstream libraries won't need any requirement on jspecify. |
Most tools that handle JSpecify will have a dependency on I am not sure about annotation processors, since the annotation processor class path can be separate from the classpath of the compiled application. I suspect however that if the annotation processor depends on |
Thanks for all the details. I've been trying to use this as an opportunity to motivate myself to understand modules at least a tiny bit, and it seems to be helping. I now can reproduce the Much as I encourage people to keep the tiny JSpecify jar on their compile-time and runtime classpath, I agree that requiring it at runtime is too strong. So I'm with you on ruling out I am not necessarily opposed to requiring the JSpecify jar at compile time for users who are building against Guava. But I'd want to do it only if it provided some clear benefit: If a Gradle, Bazel, or Maven user (or a person who runs javac manually) really wants to reduce the compile-time classpath beyond what I'd recommend, it would be hard to justify fighting the user over it. (Notably, the problem I worry about most arises not when JSpecify annotations are missing but when types whose usages are annotated with JSpecify annotations are missing. Requiring JSpecify on the module path does not help with that problem.) So the big questions are likely around the behavior of reflection and annotation processors. At the moment, I'm poking around with reflection. As best I can tell, the only way to guarantee that
That's arguably not great, but I take the point that code should really query for specific annotations, not everything by a given name. (Hmm, that's probably bad news for I may or may not look into annotation processors. It's another good point that they have their own classpath. It's possible that the module path has little impact there, given that annotation processors at least should operate more in the |
How did you reproduce it with JSpecify? I can easily reproduce with annotation that have a value (e.g. |
Annotation processors can see the annotations fine with |
Oh, yes, sorry, to be clear, I've reproduced it only with annotations that do have parameters. (I was testing with That does suggest that users who build their modules in unusual ways could get away without even |
My 2 cents is that if you expose public types with JSpecify annotations and you are normal project and not Guava it should be The above comments with Guava seem to be making a big deal about the .00001% of users concerned with JSpecify being in the deployed application. Why is that a problem (especially because there are a plethora of workarounds)? Let me remind you that almost all applications now have SLF4J as a dependency almost always transitively (albeit through build) and the types are not even exported. The JSpecify jar has far less surface area and version change. Furthermore if you as a user do blanket I mean its like exporting For the users or library owners that have complicated reasons why they can't include the JSpecify jar as transitive they can figure that out on their own. |
I do hope to suggest I'm sure there's still more that I'll need to learn about all this.... |
One thing that might be good advise to users is how to add this for their various spotbugs configuration. At least meet we should make a note that it needs to be. Spot bugs being the only static analysis tool that looks at the runtime code. I have literally seen why having annotations that aren't preserved at runtime cause a problem because it has problems with the Jakarta annotations because they are compiled time only. I'm not actually talking about JPMS here I'm just talking about in general. However I believe that this also means that if Jspecify isn't on the module path for runtime that means it wouldn't pick it up either. Note: I see no reason this should be requires transient. In fact annotations of any kind should rarely be exposed as public API as they are intended to be consumed reflectively. In other words in order for a consumer to use them you need to open your module in the first place; another thing to consider especially with spot bugs... I don't think spotbugs works with jpms yet... It honestly almost sounds like a problem. |
You do not. Any public class with public methods is visible to reflection provided it is exported (e.g. Currently though there is not much enforcement of this and I'm not sure exactly other than JLink what sort of things verify Annotations I admit get weird treatment because they do not blindly fail with a class load exception like you would if you returned a type and I guess somewhat to your point it has to mostly be accessed by reflection but the modules do not have to be If Guava did not have such a complicated history of versioning/dependencies I would recommend they That is |
I'm not certain what libraries should do as far as their own jpms
should I have
or
I would generally think the second, but then I'm constantly plagued by warnings from java of annotations at compile time that are missing, or missing dependent fields of. Is
static
actually appropriate? I notice that the annotations are marked as "runtime" but I don't understand the use case for that.The text was updated successfully, but these errors were encountered: