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

Update to modern guava #247

Closed
bootstraponline opened this issue Jul 13, 2018 · 17 comments
Closed

Update to modern guava #247

bootstraponline opened this issue Jul 13, 2018 · 17 comments
Assignees

Comments

@bootstraponline
Copy link

bootstraponline commented Jul 13, 2018

compile 'com.google.guava:guava:18.0' is ancient and causes method missing errors with more modern Java software (for example GoogleContainerTools/jib#591). This was highlighted recently in a Google cloud blog post:

I had a bit of trouble dealing with dependency conflicts between different Java client libraries. Each wanted a different version of a set of common dependencies. I’m sure there must be a good reason, but several libraries come with a dependency on com.google.guava/guava-jdk5 which you’ll want to exclude and instead use an up-to-date Guava version.

https://www.deps.co/blog/google-cloud-platform-good-bad-ugly/

It'd be really nice if the Google Java projects played well together and used modern dependencies. Guava 18 was released on Aug 25, 2014. That's four years ago!

@zpencer
Copy link
Contributor

zpencer commented Jul 13, 2018

@zhangkun83 would shading guava make sense here? None of the guava classes appear in public APIs

@ejona86
Copy link
Collaborator

ejona86 commented Jul 13, 2018

@bootstraponline, why do you think this plugin is the cause of issue? The plugin should work fine with newer versions of Guava, and Gradle should resolve the newer of the versions necessary. As long as plugins work with newer versions of Guava, "things are fine." Is there any reason to believe that's not the case?

@zpencer, bumping the Gradle version is easy. Why not do so?

I came across https://discuss.gradle.org/t/guava-in-gradles-classpath/24861/9 which led me to gradle/gradle#3698 . I don't think the jib issue is caused by this plugin; I think it may be caused by Gradle itself.

@bootstraponline
Copy link
Author

bootstraponline commented Jul 13, 2018

image

I built a local snapshot of protobuf-gradle-plugin that uses modern guava (which also required building all the dependencies). I'm still getting the same guava method not found error even after updating protobuf-gradle-plugin. I think protobuf-gradle-plugin should update however it doesn't seem related to this issue.

@ejona86
Copy link
Collaborator

ejona86 commented Jul 13, 2018

@bootstraponline, thanks for the investigation. I agree that updating is a good idea, it just didn't seem like the current state should cause breakages.

@lazystone
Copy link

Actually current state does create breakages - at the moment I can not use protobuf-gradle-plugin alongside with spotbugs gradle plugin, since latter depends on newer guava version but somehow older version from protobuf plugin gets on the way.

It would be nice to either update guava library or better to repackage it, so it won't create any problems in the future.

@lazystone
Copy link

Also there is a PR #269 - that would be even better.

@shevek
Copy link

shevek commented Aug 29, 2019

     26     classpath('com.google.protobuf:protobuf-gradle-plugin:0.8.8') {
     27         exclude group: 'com.google.guava'
     28     }

This appears to work on Gradle 5.6. But please fix the bug.

@rpalcolea
Copy link
Contributor

Hi @ejona86

Any chance this could be fixed, either by upgrading guava or shading it?

@ejona86
Copy link
Collaborator

ejona86 commented Oct 2, 2019

I was fine with bumping the version of guava, "just because." But I don't think the peanut gallery's view of the situation is entirely accurate as to what is going on. While upgrading the guava dependency might fix this specific issue just this specific time, I don't think that's the whole story. Something more fundamental is going on for a guava version to be downgraded. Gradle should use the newest of any dependencies. If it is not, this problem is ripe to repeat continually as new versions of dependencies are released and used. When this problem is fixed I want it to be fixed for the future, not just for today.

But I'll also point out there is an easy workaround (in addition to excluding the dependency as seen above). Note that this shows that gradle does choose the newest version amongst plugins.

buildscript {
    dependencies {
        classpath("com.google.guava:guava:27.1-android")
    }
}

Spotbugs claims not to use guava (spotbugs/spotbugs-gradle-plugin#119 (comment)), and that's what it looks like from my perspective. I see no Guava dependency in gradle buildEnvironment, nor in gradle dependencies, nor in the repos
https://github.com/spotbugs/spotbugs-gradle-plugin and https://github.com/spotbugs/spotbugs . Looking at the full error makes me think this is gradle itself breaking.

$ gradle build --include-build .. --stacktrace
...snip...
Caused by: org.gradle.api.reflect.ObjectInstantiationException: Could not create an instance of type com.github.spotbugs.internal.spotbugs.SpotBugsExecutor.
        at org.gradle.internal.instantiation.DependencyInjectingInstantiator.newInstance(DependencyInjectingInstantiator.java:54)
        at org.gradle.process.internal.worker.request.WorkerAction.execute(WorkerAction.java:68)
        at org.gradle.process.internal.worker.request.WorkerAction.execute(WorkerAction.java:40)
        at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:91)
        at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:34)
        at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
        at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:70)
        at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:68)
        at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:73)
Caused by: java.lang.NoSuchMethodError: com.google.common.collect.ImmutableList.builderWithExpectedSize(I)Lcom/google/common/collect/ImmutableList$Builder;
        at org.gradle.internal.instantiation.AbstractClassGenerator$AbstractInjectedPropertyHandler.getInjectedServices(AbstractClassGenerator.java:1123)
        at org.gradle.internal.instantiation.AbstractClassGenerator.generateUnderLock(AbstractClassGenerator.java:237)
        at org.gradle.internal.instantiation.AbstractClassGenerator.access$000(AbstractClassGenerator.java:93)
        at org.gradle.internal.instantiation.AbstractClassGenerator$1.transform(AbstractClassGenerator.java:111)
        at org.gradle.internal.instantiation.AbstractClassGenerator$1.transform(AbstractClassGenerator.java:108)
        at org.gradle.cache.internal.DefaultCrossBuildInMemoryCacheFactory$AbstractCrossBuildInMemoryCache.get(DefaultCrossBuildInMemoryCacheFactory.java:124)
        at org.gradle.internal.instantiation.AbstractClassGenerator.generate(AbstractClassGenerator.java:150)
        at org.gradle.internal.instantiation.AsmBackedClassGenerator.generate(AsmBackedClassGenerator.java:109)
        at org.gradle.internal.instantiation.Jsr330ConstructorSelector$1.transform(Jsr330ConstructorSelector.java:59)
        at org.gradle.internal.instantiation.Jsr330ConstructorSelector$1.transform(Jsr330ConstructorSelector.java:54)
        at org.gradle.cache.internal.DefaultCrossBuildInMemoryCacheFactory$AbstractCrossBuildInMemoryCache.get(DefaultCrossBuildInMemoryCacheFactory.java:124)
        at org.gradle.internal.instantiation.Jsr330ConstructorSelector.forType(Jsr330ConstructorSelector.java:54)
        at org.gradle.internal.instantiation.Jsr330ConstructorSelector.forParams(Jsr330ConstructorSelector.java:49)
        at org.gradle.internal.instantiation.DependencyInjectingInstantiator.newInstance(DependencyInjectingInstantiator.java:46)
        ... 8 more

Looking at AbstractInjectedPropertyHandler.getInjectedServices, I see:

        public List<Class<?>> getInjectedServices() {
            ImmutableList.Builder<Class<?>> services = ImmutableList.builderWithExpectedSize(serviceInjectionProperties.size());

So spotbugs is just using an API in Gradle that is broken by the guava version. Figuring out what version of Guava Gradle is using was a bit strange, since I see lots of guava dependencies throughout the codebase, many of them v18.0. But based on its name, gradle/dependencies.gradle looks like it might be the best source of truth, and it uses 27.1-android and has a comment that 27.1-jre causes a large regression. That's good to know!

It seems we should upgrade our version of Guava to 27.1-android to match Gradle and file a bug against Gradle.

@ejona86
Copy link
Collaborator

ejona86 commented Oct 2, 2019

Aaand upgrading to 27.1-android breaks Android builds (ugly output because I'm pasting directly from the test xml output). And this is probably because the Guava -android doesn't have CacheBuilder:

Caused by: java.lang.NoSuchMethodError: com.google.common.cache.CacheBuilder.build(Lcom/google/common/cache/CacheLoader;)Lcom/google/common/cache/LoadingCache;
&#9;at com.android.builder.profile.NameAnonymizer.&lt;init&gt;(NameAnonymizer.java:37)
&#9;at com.android.builder.profile.ProcessProfileWriter.&lt;init&gt;(ProcessProfileWriter.java:90)
&#9;at com.android.builder.profile.ProcessProfileWriterFactory.get(ProcessProfileWriterFactory.java:179)
&#9;at com.android.builder.profile.ProcessProfileWriterFactory.initialize(ProcessProfileWriterFactory.java:90)
&#9;at com.android.build.gradle.internal.profile.ProfilerInitializer.init(ProfilerInitializer.java:55)
&#9;at com.android.build.gradle.BasePlugin.apply(BasePlugin.java:230)
&#9;at com.android.build.gradle.AppPlugin.apply(AppPlugin.java:114)
&#9;at com.android.build.gradle.AppPlugin.apply(AppPlugin.java:46)

That was with com.android.tools.build:gradle:2.3.0 which the tests are using right now. But v3.5.0 seems it would also break. v3.5.0 is using guava 27.0.1-jre, which is probably what we need to use instead. That is still downgrading the guava version which is a problem, but we're a bit between a rock and a hard place.

Just that change by itself breaks some tests. There are some guava hacks in the tests already, but a cursory attempt to disable them didn't yield better results.

@voidzcy
Copy link
Collaborator

voidzcy commented Oct 9, 2019

After gathering all the context and pain points of leaked Guava dependency by protobuf-gradle-plugin. Shading guava-18.0 would be a better solution than bumping the version (we could bump it to 27.0.1-jre as well). This would fix the problem for the future as new versions of dependencies are released and used. People might still get the issue for Gradle versions earlier than 5.6 (by Gradle itself gradle/gradle#3698), but they would be aware that's not caused by protobuf-gradle-plugin.

@ejona86
Does this sound good to you?

@ejona86
Copy link
Collaborator

ejona86 commented Oct 9, 2019

@voidzcy, we should bump the version of Guava. Guava 18 is really old. If we also want to shade, fine.

But we do need to file a bug against Gradle. #3698 is not what we are seeing, as the fix was in Gradle 5.6 and I still saw this issue with Gradle 5.6. The current bug would mean we shouldn't use any dependencies that Gradle itself uses, which is not appropriate.

In other words, this is not a "Guava problem." This feels more like a run-of-the-mill, "something has a broken ClassLoader" problem.

@rpalcolea
Copy link
Contributor

agree with the above ^^

Upgrading guava would only help to reduce the pain/friction when using gradle. Shading would to the same but there's an underlying problem there that should be solved at some point. Thanks for looking into this folks

@voidzcy
Copy link
Collaborator

voidzcy commented Oct 11, 2019

Seems it's not easy to bump Guava version. It looks like Gradle, Gradle Android Plugin and Guava are twisting together. Since we are supporting different versions of Gradle Android Plugin, each of them has different dependency requirement for Guava. Guava keeps deprecating APIs as it evolves.

Here are a couple examples:

This means we did have reason to depend on Guava 18.0 even though it is ancient (we do have hacks in android tests for newer version of Gradle Android Plugin). Gradle Android Plugin has requirements for version compatibilities with Gradle. Seems the same applies for Guava. So the protobuf-gradle-plugin cannot easily upgrade Guava version.

But all this matters for Android users only. For normal Java users, you can still force to depend on a newer version of Guava.

For dependency leaking, we can shade Guava in protobuf-gradle-plugin to make the problem never come from the plugin. If Gradle fixes its #3698, then the leaking issue should be completely fixed.

We might consider getting rid of Guava dependency in protobuf-gradle-plugin as the Android compatibility issue is subtle and usage of Guava is minor.

@ejona86
Copy link
Collaborator

ejona86 commented Oct 11, 2019

I just realized com.google.common.collect.ImmutableList.builderWithExpectedSize is a @Beta API. That means Gradle must not use it that API, or it has to hide Guava from plugins so they can choose a different version.

CharMatcher.JAVA_LETTER_OR_DIGIT was always @Beta API, so it was wrong of the Android plugin to use it. Any library/plugin using @Beta APIs should get a bug filed against it, because it is doing it wrong and causing pain to the ecosystem. That said, I don't know if the current version of the Android plugin is using Beta APIs.

com.android.tools.build:gradle:3.4.0 used Guava 26.0-jre, which seems new enough to "fix" the current problem. So that would be a worst-case: we upgrade to Guava 26.0-jre; Android users would need to use android plugin version 3.4.0 or later. Maybe 3.3.0 or earlier happens to work with newer versions of Guava (it was using Guava 22, which is too old for newer Gradles).

As I mentioned earlier, gradle/gradle#3698 was fixed in Gradle 5.6 and I still saw this problem happening in 5.6.

I don't mind dropping Guava, but we should do it for the right reasons. If the ecosystem is broken, we should make sure there are tracking bugs for them to fix themselves. The Gradle problem is not a Guava problem; if you say "drop Guava to make the pain go away" you are really saying "don't use any libraries that Gradle may choose to use" which is not acceptable. And for the Android plugin, if they are using @Beta APIs, they should be the ones to shade, not us. These are not immovable objects; we should spend our effort improving the ecosystem instead of working around it. (We have done this a lot in gRPC, and the world is significantly better off.)

@rpalcolea
Copy link
Contributor

agree with the above, but it seems that it will be considered in planning to be removed from Beta -> google/guava#3591 (comment)

@ejona86
Copy link
Collaborator

ejona86 commented Oct 17, 2019

@voidzcy pointed me to spotbugs/spotbugs-gradle-plugin#166 and had trouble reproducing this issue without spotbugs, so it does seem it is a spotbugs bug and not a Gradle bug. I'm very grateful to those who identified the root cause.

There's been a reasonable amount of time spent on this behind-the-scenes. We were dealing with a bit of a perfect storm of issues, independent of spotbugs, that were preventing us from testing properly. It took three of us, but we think we've found the root cause for enough of the issues to have a path forward.

@ejona86 ejona86 closed this as completed Jul 20, 2020
ejona86 added a commit to ejona86/protobuf-gradle-plugin that referenced this issue Sep 26, 2022
We like Guava, but the plugin has little need for it. As a side-benefit
dropping it avoids systems-not-handling-classpaths-correctly issues
like google#247. We wouldn't drop Guava just for that reason (as the ecosystem
should fix its bugs), but instead of just removing it from the API
surface, let's remove it entirely. Similarly, we could continue using it
in our tests, but we really don't need it.

Fixes google#572
ejona86 added a commit that referenced this issue Oct 2, 2022
We like Guava, but the plugin has little need for it. As a side-benefit
dropping it avoids systems-not-handling-classpaths-correctly issues
like #247. We wouldn't drop Guava just for that reason (as the ecosystem
should fix its bugs), but instead of just removing it from the API
surface, let's remove it entirely. Similarly, we could continue using it
in our tests, but we really don't need it.

Fixes #572
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

7 participants