guava icon indicating copy to clipboard operation
guava copied to clipboard

Migrate off of jsr305

Open zyxist opened this issue 7 years ago • 76 comments

There are several issues with using jsr305.jar by Guava.

JSR-305 is dormant, has been for a long while and shows no hope of ever producing an agreed set of annotations in our lifetime. Further more these annotations use javax. packages which it is not possible to use according to the Oracle Java binary licence, so applications can not use and ship these dependencies along with a JRE without violating the Oracle licence agreement.

F. JAVA TECHNOLOGY RESTRICTIONS. You may not create, modify, or change the behavior of, or authorize your licensees to create, modify, or change the behavior of, classes, interfaces, or subpackages that are in any way identified as "java", "javax", "sun", “oracle” or similar convention as specified by Oracle in any naming convention designation.

The JSR-305 group has not defined any official releases according to its jsr page so the only implementations is a seemingly random implementation provided by the FindBugs team. Even if the team where experts on the JSR (which some where) they are not official as there has been no vote and are not available from the JSR hompage - so the javax package name restriction still applies.

Using jsr305 causes additional issues, if Guava is used in a modular JDK9 applications, because it puts the annotations into javax.annotation package, which is also used by a couple of other JAR-s and a legacy JDK module java.xml.ws.annotation. If one wants to create a modular JDK9 application with two dependencies to conflicting JAR-s, Java refuses to compile and run it because of a package split. Example:

  • Guava -> forces us to require jsr305 automatic module,
  • Dagger -> forces us to require either java.xml.ws.annotation or jsr250 automatic module.

All of the modules use javax.annotation.

Findbugs has been rebooted as Spotbugs and they are going to make a switch from JSR-305 to their own internal annotations in version 4.0.0 that do not break anything:

https://github.com/spotbugs/spotbugs/pull/180

I think Guava should consider switching to them in order not to pollute application dependencies with jsr305 JAR.

zyxist avatar Oct 07 '17 09:10 zyxist

AFAICT these are the annotations from that package that we use:

  • @Nullable
  • @CheckForNull
  • @CheckReturnValue
  • @ParametersAreNonnullByDefault
  • @concurrent.GuardedBy
  • @concurrent.Immutable
  • @concurrent.NotThreadSafe
  • @concurrent.ThreadSafe

@Nullable is probably the one that we have the most options, since almost everyone treats any annotation with the simple name of Nullable as the same.

The others are less clear to me - they're technically just for static analysis, but they all have RUNTIME retention. It's not abundantly clear how we could get around using them without having ErrorProne, IDEs, and all other static analysis pipelines adopting a new set of APIs.

It sounds like we should maybe be avoiding javax.annotation.Generated entirely until we can figure this out. This will be a large project (and span beyond just Google products). Would that solve the module issue?

ronshapiro avatar Oct 15 '17 02:10 ronshapiro

I agree that this is more a long-term action. I created this issue more to raise the awareness of the problems with jsr305 and new Java. Spotbugs 4.0 is not released yet, and other tools would have to agree for a new set of annotations, and provide the sufficient support. But on the other hand, Guava is in the right position to influence a decision, which way to go.

Regarding javax.annotation.Generated:

  • avoiding it will only reduce the problem, not remove it (JSR-250 contains other annotations, too - if any of them is in use by any dependency or main project, you need to patch the modules).
  • Java 9 provides a new, unambiguous variant: javax.annotation.processing.Generated,
  • you can detect which one of them is available by the classloader, and generate the code either without any annotation, with javax.annotation.Generated, or with javax.annotation.processing.Generated. For JDK8, it would work "as usual", and for JDK9, it would depend on the required JDK module in the application module descriptor, so that the developer has a choice, which way to go.

I appreciate that you didn't forget about Generated :).

zyxist avatar Oct 16 '17 15:10 zyxist

JSR-250 contains other annotations, too

Seems to me that the others are significantly less heavily used.

ronshapiro avatar Oct 16 '17 15:10 ronshapiro

Migrating off the jsr305 annotations makes sense to me.

Hopefully most tools care only about the simple class name of the annotations, not their packages. For starters, I checked Error Prone's @CheckReturnValue and @GuardedBy checkers. They care only about the simple name. But I'm sure that some tools (inside and outside Google) will care about the package.

If we're moving off the "standard" annotations, I wonder what it makes the most sense for us to migrate to. Spotbugs is a natural choice, since we know it has all the annotations that we want. But of course there are many nullability annotations. And arguably we aren't targeting Spotbugs nowadays so much as we're targeting Error Prone. Maybe Error Prone would be interested in adding some annotations of their own. Or maybe, for some of the annotations we use less frequently, we could have our own (package-private?) versions to avoid committing to any tool.

(One other note about @Nullable: We can't use type annotations yet because we currently make our "Android" branch available to Java 7 users. This may rule out some libraries' nullness annotations.)

cpovirk avatar Oct 17 '17 21:10 cpovirk

If we're moving off the "standard" annotations, I wonder what it makes the most sense for us to migrate to.

Adopting the Checker Framework's annotations may make sense here. Considering that, AFAIK, its nullability annotations are the most plentiful and advanced out there, it may encourage more people to try the framework out.

But if its annotations count as type annotations, then we'd be unable to use them...

jbduncan avatar Oct 17 '17 21:10 jbduncan

Hopefully most tools care only about the simple class name of the annotations, not their packages.

This is not true of Kotlin, although they'll be receptive to adding your annotations to their list.

JakeWharton avatar Oct 17 '17 21:10 JakeWharton

Looking at the options, it seems to me that Checker Framework would be a good solution. Thanks for pointing me to that, @jbduncan.

kashike avatar Oct 19 '17 18:10 kashike

I'm curious what the replacement for javax.annotation.concurrent.Immutable is with Checker Framework, if there is one - do you happen to know, @jbduncan?

kashike avatar Oct 25 '17 03:10 kashike

@kashike I admit that I don't know if the Checker Framework has an Immutable annotation, but I do know that error-prone's annotations project has one. I've never tried the Checker Framework personally, so I don't know how it behaves in combination with error-prone (which, by comparison, I have used), but theoretically if one can get them to work nicely together, then we'd have a superior, statically analysed solution to javax.annotation.concurrent.Immutable.

jbduncan avatar Oct 25 '17 09:10 jbduncan

...but that's not even considering the other sorts of annotations that we're already using and will probably still want to use, like @Nullable...

This may get tricky.

jbduncan avatar Oct 26 '17 22:10 jbduncan

The Checker Framework authors themselves suggest that their own type annotations are backwards-compatible with Java 6, but only if they're written inside comments.

That's not exactly ideal...

jbduncan avatar Oct 26 '17 22:10 jbduncan

... Spotbugs is a natural choice, since we know it has all the annotations that we want. ...

Does it? I don't see everything that was listed above - it appears as if some annotations that are used by Guava are not in spotbugs.

As for Checker Framework: it appears to have most things, except @Immutable: https://gist.github.com/kashike/d8fb2007ab01041a08a2d5cf20bc2d17

~...create a custom @Immutable annotation?~ It seems as if errorprone has one already, actually.

kashike avatar Oct 26 '17 23:10 kashike

@kashike Apologies if I've not explained myself very well before, but I just want to make sure we're both on the same page with regards to the Checker Framework's annotations.

It seems to me from what @cpovirk's said already and what I personally know of the Checker Framework's annotations that, sadly, since they are type annotations, they can only be used in Java 8+ projects - JSR308, which is what type annotations are derived from, is only implemented in compilers that understand Java 8.

This is a problem because guava-android is compiled with a Android toolchain, and modern Android toolchains only understand Java 7 syntax and a small subset of Java 8 syntax - not enough to understand type annotations as legal constructs.

The only workaround I've found so far (suggested by the Checker Framework authors here) is to write type annotations in comments. So, for example, instead of:

List<@Nullable String> listOfNullsAndStrings = ...;

we'd have:

List</*@Nullable*/ String> listOfNullsAndStrings = ...;

The Checker Framework understands type annotations written like this, but I somewhat doubt other tools like IntelliJ IDEA, {Find,Spot}Bugs, and Google's internal tools understand them written like this too. Thus, we may not be able to use the Checker Framework's annotations in Guava until Android catches up.

If you did understand this already, apologies! Otherwise, I hope that this has cleared things up a bit. :)

jbduncan avatar Oct 26 '17 23:10 jbduncan

Thanks for the reply, @jbduncan. I'm also looking at what to use in my own projects (which are Java 8) - sorry for not stating this.

I've been using com.google.code.findbugs:jsr305 for a long time now, but this issue has made me want to switch to something new (which, in this case, is currently going towards org.checkerframework:checker-qual) for annotations on things (I annotate pretty much everything that can be annotated with @Nonnull or @Nullable, as well as @Immutable). checker-qual seems to have everything except @Immutable.

I'm trying to find a replacement that has everything I need, but I haven't been able to yet. Perhaps I'll delay switching until the Guava team comes to a solution for this issue in Guava.

kashike avatar Oct 26 '17 23:10 kashike

Progress update + summary for people new to this:

cpovirk avatar Dec 11 '17 13:12 cpovirk

Will there be a version of guava that uses both old and new in the interim, or will this be a hard cut off between versions?

On Mon, Dec 11, 2017, 5:16 AM Chris Povirk [email protected] wrote:

Progress update + summary for people new to this:

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/google/guava/issues/2960#issuecomment-350721044, or mute the thread https://github.com/notifications/unsubscribe-auth/AAUN4khif1bh5T6FtA7pnt9PQdEyyEdYks5s_SsAgaJpZM4PxSdM .

cgruber avatar Dec 11 '17 15:12 cgruber

Hmm, good question. I have been hoping to just cut from one to the other, on the theory that tools can be updated to recognize both ahead of time. But if anyone knows of problems that we could avoid by having both present, let me know.

cpovirk avatar Dec 11 '17 15:12 cpovirk

FYI: it is impossible to use guava and java.xml.ws modules simultaneously:

module foo {
    requires com.google.common;
    requires java.xml.ws;
}

This code will not compile because there is a split package in jsr305 and javax.xml.ws.annotation modules.

orionll avatar May 17 '18 15:05 orionll

Update to my previous comment. It is not impossible, however, this requires exclusion of jsr305 dependency:

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>25.0-jre</version>
	<exclusions>
		<exclusion>
			<groupId>com.google.code.findbugs</groupId>
			<artifactId>jsr305</artifactId>
		</exclusion>
	</exclusions>
</dependency>

orionll avatar May 18 '18 05:05 orionll

I was part of the related discussion at #1018 about whether the JSR-305 should be "optional" or "provided", and thankfully (if I remember correctly) we all settled on "optional". But I remembered that the larger question came up of whether JSR-305 should be replaced altogether to accommodate Java 9, so I've been worrying about what to replace JSR-305 with in our own code at some point.

So today I did a little search and, lo and behold, it would appear from https://stackoverflow.com/a/37911663/421049 that Java changed the modules so that we can use JSR-305 in Java 9+. Does this mean that this is no longer an issue, and that we might as well stick with JSR-305? Sorry if I'm not up-to-date; maybe a little summary would help us all.

garretwilson avatar Oct 16 '18 15:10 garretwilson

JSR-305 has a common package with Common Annotations which is a platform module in Java 9-10 (java.xml.ws.annotation). In Java 11 it is not a platform module anymore, but it still shares the same package with JSR-305. So, I think, we still need to get rid of JSR-305 to avoid split packages when someone is using both guava and Common Annotations.

orionll avatar Oct 16 '18 16:10 orionll

(on preview, I'm mostly agreeing with @orionll)

It's true that the module that includes javax.annotation isn't included in the "default set of root modules", but it's still part of the distribution for JDK 9 and 10 and can be enabled with --add-modules=java.xml.ws.annotation. (I think java.annotations.common was renamed after the stack overflow answer.)

So given:

class T {
  public static void main(String[] args) throws Exception {
    for (String c : args) {
      System.err.println(Class.forName(c));
    }
  }
}

This works:

$ java -cp .:jsr305.jar T javax.annotation.Nullable
interface javax.annotation.Nullable

... but this doesn't work with JDK 9 or 10:

java -cp .:jsr305.jar --add-modules=java.xml.ws.annotation T javax.annotation.Nullable
Exception in thread "main" java.lang.ClassNotFoundException: javax.annotation.Nullable

cushon avatar Oct 16 '18 16:10 cushon

So is there a consensus on what the replacement will be? Do you have specific Maven coordinates on what you'd replace it with? (Excuse the question. I know I could peruse all the threads to try to figure that out, but even that answer might not be up-to-date. So I thought maybe it would be useful just to summarize.)

garretwilson avatar Oct 16 '18 16:10 garretwilson

So is there a consensus on what the replacement will be?

You guys don't know yet, I take it?

garretwilson avatar Oct 18 '18 15:10 garretwilson

@garretwilson Please see this thread. The only remaining annotation is javax.annotation.ParametersAreNonnullByDefault which is used only in package-info.java. Any ideas how to get rid of it without breaking static analysis in IDEA?

orionll avatar Oct 18 '18 15:10 orionll

So I guess the implicit answer to my question is "Guava has decided to migrate to the Checker Framework".

I can't answer the question about javax.annotation.ParametersAreNonnullByDefault. We're mostly only using JSR 305's @Nonnull and @Nullable annotations as a means of documentation (as it is much more concise, clearer, and certainly easier than adding @throws NullPointerException … everywhere) and to encourage the developer to at least think about this when creating an API.

For our purposes it's not clear to me the Checker Framework would be an improvement; I'm not even clear on which Maven dependency to use to get just the annotations. (Update: looking inside the JARs, it appears that org.checkerframework:checker-qual is the dependency with the annotations, but wow, that includes a lot of other things as well.) Adding null-checking tools to the build might be useful, but in our projects we're not there yet.

In short, to me there doesn't seem to be as big a motivation to switch, now that JSR 305 causes fewer module problems and there still seems to be lacking a consensus on a successor. For now it seems this small, optional dependency works for us. But I'm very interested in watching how successful Guava is in switching. (The fact that it's taking so long for Guava to migrate and that there's so much confusion regarding the subject is another warning to me that it might be better for our projects to stay with JSR 305 for now.)

garretwilson avatar Oct 20 '18 17:10 garretwilson

Any new insights regarding this issue and what is a good alternative to avoid the split package problem (regardless whether it's Java 9 or 11)? I'm still searching for a proper replacement candidate (a @Nullable and @NonNull / @NotNull with runtime retention et all). My library is stuck on JDK 7, due to the target audience..

bbottema avatar Jan 03 '19 18:01 bbottema

checker-qual

kashike avatar Jan 03 '19 19:01 kashike

Agreed. checker-qual is the single-best alternative available and can be set as optional dependency. Sadly, the checker framework itself still struggles with java 8 compatiblity (not all tests are working atm), java 9-11 is a whole new story.

bmarwell avatar Jan 04 '19 09:01 bmarwell

This doesn't mean that guava will be unable to support newer versions.

@bbottema take a look at https://github.com/glowroot/glowroot/tree/master/build/checker-jdk6 (sorry for off-topic).

bmarwell avatar Jan 04 '19 10:01 bmarwell