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

Unused import of javax.annotation.Generated breaks annotation processing on JDK9 with Jigsaw #880

Closed
zyxist opened this issue Sep 22, 2017 · 12 comments

Comments

@zyxist
Copy link

zyxist commented Sep 22, 2017

Hello!

Today, I tried to write a modular JDK9 application with Dagger. Unfortunately, it turned out to be impossible without hacks. The problem is that the file SourceFileGenerator.java contains an unused import of javax.annotation.Generated.

This annotation is a part of java.xml.ws.annotation module, which is not loaded by default and is deprecated (to be removed in future Java versions). Historically, it was incorrectly placed in a package together with Java EE annotations. Furthermore, there exist several crappy JSR-305 JAR-s used by many libraries, that also add new annotations to the same package. In Jigsaw, the package can be owned by only one module. Java 9 refuses to compile an appplication and run it, if there are two modules that contain the same package.

Now, to actually build Dagger modular application, we need to use some hacks. First of all, if we have any JSR-305 in our dependency list, we need to physically copy the JAR somewhere and use --patch-module compiler flag:

--patch-module java.xml.ws.annotation=C:\path\to\my\jsr305.jar

The second thing is to enable that module explicitly:

--add-modules java.xml.ws.annotation

And, configure the build system to exclude all the possible variants of jsr305 (Gradle example below):

configurations {
    all*.exclude group: 'com.google.code.findbugs', module: 'jsr305'
}

However, we're not done yet. The annotation processor seems to ignore the flag --add-modules (and --add-reads, --add-open, etc.), which causes the following compiler error:

NoClassDefFoundError: javax/annotation/Generated

As a workaround, we need to use javax.annotation:jsr250-api:1.0 dependency for annotation processors. Below, there's a configuration for Gradle:

dependencies {
   // other dependencies here 

   apt 'com.google.dagger:dagger-compiler:2.11`
   apt 'javax.annotation:jsr250-api:1.0'
}

Now Dagger is generating the code, but to make it work, we must modify our module-info.java files to add the dependency on the deprecated java.xml.ws.annotation:

module com.example.mymodule {
   requires java.xml.ws.annotation;
   requires dagger;
}

As you can see, using Dagger is pretty complicated :).

You can do the following things to fix it:

  • remove that import from SourceFileGenerator.java (the code is already refering to the annotation as a String).
  • JDK9 introduces a new annotation: javax.annotation.processing.Generated which shall be used instead (module: java.compiler). So, your code generator can work like this:
    1. if javax.annotation.processing.Generated is loaded, generate the output code with this annotation,
    2. if javax.annotation.Generated is loaded, use it,
    3. otherwise, don't add any annotation to the generated code.

The changes are backward compatible.

@ronshapiro
Copy link

I believe 772374b should take care of this (and will be in the next release)

@zyxist
Copy link
Author

zyxist commented Sep 24, 2017

Hi!

No, it won't solve any problem, because the application will still have two "modules" that are trying to write to the same package javax.annotation. The only difference is that instead of patching java.xml.ws.annotation, the application has to patch jsr250 with jsr305. jsr305.jar is unfortunately used by many popular libraries (including your own Guava), so in practice, your solution will only work only for applications that do not rely on thirdparty libraries at all.

In addition, adding a dependency on JSR-250 makes little sense for a library like Dagger, because these annotations are JavaEE-specific (except Generated), and it also pollutes application dependencies with unnecessary stuff.

The right way to fix it is presented above:

  1. removing that import,
  2. point 1 removes the need to use jsr250 dependency and java.xml.ws.annotation module, so we can remove it, too,
  3. adding a piece of code that generated javax.annotation.processing.Generated for JDK9 applications - in this way the application does not have to load anything extra to work.

I'm looking forward to your feedback.

@ronshapiro
Copy link

What do you mean that

the application will still have two "modules" that are trying to write to the same package javax.annotation

I don't believe we are writing anything to that package? Are you saying that the jsr250 jar "writes" to that package, which is sealed?

@ronshapiro ronshapiro reopened this Sep 24, 2017
@zyxist
Copy link
Author

zyxist commented Sep 24, 2017

Yes. In Jigsaw, each package can be owned by exactly one module. The problem with javax.annotation package is that there exist a couple of different JAR-s that add some annotations to it.

  1. JDK adds 5 annotations, including javax.annotation.Generated,
  2. jsr250-api.jar adds the same 5 annotations, together with many more to the same javax.annotation package,
  3. jsr305.jar provides another (distinct) set of javax.annotation annotations.

Now, suppose that you have an existing application that depends both on jsr250.api and jsr305 and you want to add modules to it:

module com.example.foo {
   requires jsr250.api;
   requires jsr305;
}

Both dependencies are not modularized yet, so Java turns them into automatic modules, and you have to specify both of them in your module descriptor. However, if you do this, Java refuses to compile and run the application, because you have a package split - both modules try to add annotations to the same package javax.annotation:

error: the unnamed module reads package javax.annotation from both jsr305 and jsr250.api
error: module guava reads package javax.annotation from both jsr305 and jsr250.api
error: module jsr250.api reads package javax.annotation from both jsr305 and jsr250.api
error: module jsr305 reads package javax.annotation from both jsr305 and jsr250.api
error: module error.prone.annotations reads package javax.annotation from both jsr305 and jsr250.api
error: module j2objc.annotations reads package javax.annotation from both jsr305 and jsr250.api
error: module animal.sniffer.annotations reads package javax.annotation from both jsr305 and jsr250.api
error: module javax.inject reads package javax.annotation from both jsr305 and jsr250.api
error: module dagger reads package javax.annotation from both jsr305 and jsr250.api
/dagger-example-app/framework-core/src/main/java/module-info.java:1: error: module com.zyxist.example.dagger.framework.core reads package javax.annotation from both jsr305 and jsr250.api
module com.zyxist.example.dagger.framework.core {

With Dagger, the situation is very similar. Dagger puts javax.annotation.Generated into the generated code, so you need to require either java.xml.ws.annotation JDK module, or jsr250.api. If one of your dependencies uses JSR-305, too, you are in trouble.

There is a way to workaround the problem by using --patch-modules CLI flag:

--patch-module jsr250.api=C:\path\to\my\jsr305.jar

As you can see, it's not very convenient, because you must know the physical filesystem path to JSR-305 JAR, and you must actually hardcode them both in your build script, and your run script.

I hope that this information is helpful.

Meanwhile, I managed to create a pull request with the necessary changes. You can try this out with this example Dagger application: https://github.com/zyxist/dagger-example-app/tree/jigsaw-edition

@zyxist
Copy link
Author

zyxist commented Oct 7, 2017

Hello, has anyone looked at the PR?

@zyxist
Copy link
Author

zyxist commented Oct 10, 2017

I created a Pull Request that fixes this issue 16 days ago: #882 - has anyone looked at it? Any ETA for merging it and improving Dagger cooperation with JDK9?

@LawnSounds
Copy link

Why haven't anyone looked at this? The solution is literally right here

@ronshapiro
Copy link

Things are a bit more complicated - I don't believe the tests pass on that PR. I have something mostly working, but we still rely on @javax.annotation.CheckReturnValue internally in our code and we'd need the jsr250 dep for that.

I added a version of this annotation to ErrorProne (google/error-prone@7967165) which will be in their next release. Once that's in, we can migrate to that and drop the jsr250 dep, prefer javax.annotation.processing.Generated when it's available, and fully fix this.

@tobihagemann
Copy link

Now that the issue on error-pone is closed and 2.1.3 has been released two days ago: When can we expect a new Dagger release that fixes this issue? Thank you! 😁

@ronshapiro
Copy link

ronshapiro commented Nov 30, 2017 via email

@davido
Copy link

davido commented Dec 7, 2017

So, we are running into this issue on Java 9, in Gerrit Code Review, because we use rules_closure to invoke JavaScript closure compiler to transpile ES6 to ES5, and rules_closure is using Dagger internally, and it is failing on Java 9: with [1].

I wrote this issue upstream, but I affraid that rules_closure can't help us until this issue is sorted out in Dagger?

[1] http://paste.openstack.org/show/628314

@davido
Copy link

davido commented Dec 10, 2017

Something like this diff: [1] should fix it?

[1] http://paste.openstack.org/show/628553

ronshapiro added a commit that referenced this issue Dec 13, 2017
Fixes #880
Closes #882

RELNOTES=Remove dependency on JSR 305 annotations

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=178784068
lucamilanesio pushed a commit to GerritCodeReview/gerrit that referenced this issue Feb 8, 2018
We need to upgrade rules_closure tools to be able to build with Java 9.
That's because rules_closure depends on dagger 2 and auto-common and
auto-value, that need to be updated to not depend on legacy annotations
(that are not available on Java 9). Moreover, latest rules_closure
switched to building protobuf library from the sources, creating the
next problem: Protobuf doesn't support Java 9 yet. That was fixed only
on master, so that we need to update rules_closure to consume the
protobuf dependency from HEAD.

See these issues for more background: [1],[2],[3],[4].

[1] google/auto#560
[2] bazelbuild/rules_closure#234
[3] google/dagger#880
[4] protocolbuffers/protobuf#4256

Bug: Issue 7958
Change-Id: I56f3b6101e06bd678b4e42d3a9d52157963513aa
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

No branches or pull requests

6 participants
@zyxist @davido @ronshapiro @tobihagemann @LawnSounds and others