JCenter Shutdown Impact on Gradle Builds

On February 3 2021, JFrog announced that they will be shutting down Bintray and JCenter. This post tells you what you need to know and do to avoid disruptions to your build pipelines.

Your build may be affected by this shutdown in several ways:

Additionally, you should be aware of the security considerations when moving from one repository to another.

UPDATE: JFrog has decided to keep JCenter as a read-only repository indefinitely. New package and versions are no longer accepted on JCenter. All Bintray services have been shutdown.

Background

JCenter is a central artifact repository, like Maven Central. Software projects use JCenter to distribute their software to other people. JCenter also serves as a mirror for Maven Central, so any dependencies available on Maven Central are also available on JCenter (but not vice versa).

Bintray is a management layer that software projects use to publish and promote packages to JCenter. Bintray also allowed users to create public user-specific repositories that were isolated from JCenter.

Both of these services are affected by the shutdown.

Impact to builds

By default, Gradle does not add an artifact repository to your project; however, Gradle does provide a convenient API for using the JCenter repository.

The Gradle Build Init plugin produces build templates that use the JCenter repository to resolve dependencies. In many code examples and documentation, JCenter is used as an example repository.

It’s very likely that you have builds that rely on JCenter. We’ve found over a million Git repositories on GitHub that use JCenter with Gradle.

To discourage new projects from using JCenter, we will be removing JCenter from our samples and init templates. The new default will be Maven Central. Gradle itself has no inherent tie to JCenter or Maven Central, so you can always switch any other repository of your choice. This change will be effective with the next Gradle release – Gradle 7.0.

Gradle 7.0 will also deprecate the use of jcenter() to resolve dependencies. You will still be able to use JCenter as a repository, but Gradle will emit a deprecation warning. The jcenter() method will be removed in the next major release.

Example of deprecation message in a build scan

Packages that are hosted on JCenter will need to find an alternative repository to provide updates after March 31 2021. Many packages will likely migrate to Maven Central, but some packages may not migrate at all and some may only publish new versions. Abandoned projects could change their coordinates as other maintainers take over. This means the transition from JCenter to another repository will be more difficult than simply using a different repository URL.

Based on the current timeline, builds that use JCenter will be able to resolve dependencies until February 1, 2022 without changes. After that date, there are no guarantees that you will be able to build your software if you continue to use JCenter.

What do you do?

Follow the steps below to prepare your build for the JCenter shutdown:

  1. Determine if your build uses JCenter or not
  2. Remove JCenter from your build and replace it with Maven Central. Gradle provides a convenient API for that repository too.
  3. Run your build pipeline to see if everything works still.

How do you know if you’re using JCenter?

There are a few different ways you can check if you’re using JCenter.

JCenter has been the default repository for Android projects for many years. If you’re building an Android application, you’re probably using JCenter.

The Gradle Plugin Portal implicitly mirrors JCenter currently. If you’re using the Plugin Portal (via gradlePluginPortal() or the URL plugins.gradle.org/m2) to resolve your application’s dependencies, you may be relying on JCenter. You should avoid using the Plugin Portal as a repository, except for Gradle plugin projects.

Please note that Bintray also allowed any user to run their own public repository with a custom URL. This is also affected by the shutdown. You should check if you are using a repository with a URL starting with dl.bintray.com. Since these repositories can contain anything, you’ll need to figure out which dependencies you use from these repositories and where they may be migrating to.

Check your build scan

If you’re using Gradle Enterprise or the public build scans service, you can check which repository was used to resolve your dependencies:

Repositories in a build scan

This will help you identify which dependencies are most likely to be affected by the shutdown.

Check your build files

If you declare your repositories in your build scripts, you can look for jcenter() or the URL jcenter.bintray.com.

You’re looking for something like

repositories {
    jcenter()
}

or

repositories {
    maven {
       url = "https://jcenter.bintray.com"
    }
}

or

repositories {
    maven {
       url = "https://dl.bintray.com/<some user name>"
    }
}

Check your plugins

Any custom plugins could add a repository to your project. Built-in Gradle plugins (like java-library) do not add repositories to your project.

You’re looking for plugins that add JCenter as a repository programmatically with the jcenter() API.

Check with your team

You may not find any references to JCenter in your build scripts or plugins, but you may still be using JCenter. If you have an internal mirror or corporate proxy repository that you use, you need to check if that repository uses JCenter.

The impact to paid JFrog Cloud customers may be different, so please contact JFrog directly to understand if you can continue to use JCenter if your internal mirror is provided by JFrog.

Troubleshooting dependencies from JCenter

You can use gradle dependencies (or build scans) to determine which dependencies were not resolved or how they may have changed. The Gradle build will also fail when resolution errors if it’s unable to resolve a dependency from any repository.

In the example below, you can see that Gradle failed to resolve the trove4j dependency, after switching to Maven Central.

On the console: Unresolved dependencies on the console

In a build scan: Unresolved dependencies in a build scan

At this point, if the package is only available on JCenter, your options are to:

  • Wait for the maintainer to migrate to another repository (e.g., Maven Central)
  • Find another equivalent package that is available on Maven Central
  • Remove your dependency on the package entirely
  • Copy the package to your own internal repository

If your build can resolve most of its dependencies from another repository, you could continue to use JCenter for the few packages that are only available there. You can prevent new packages that are only available on JCenter from being introduced into your build, by using a content filter. This prevents Gradle from looking in JCenter for all packages.

For instance, many Android projects depend on the Trove4J library that at the time of writing is only available from JCenter. We can use a content filter to only allow artifacts in the org.jetbrains.trove4j group to come from JCenter. We also put jcenter() last so that Maven Central is searched first.

repositories {
    mavenCentral()
    jcenter {
        content {
            //  org.jetbrains.trove4j is only available in JCenter
            includeGroup("org.jetbrains.trove4j")
        }
    }
}

Impact to Gradle plugins

Behind the scenes, the Gradle Plugin Portal uses JCenter to resolve dependencies of plugins. We will be migrating the Plugin Portal away from JCenter before the final shutdown. Builds will not need to make changes while the Plugin Portal migrates away from JCenter.

Resolving existing plugins

Existing plugins will continue to resolve in the same way they do today. No changes should need to be made to your builds.

Publishing new or updated plugins

If you are publishing your plugin with the com.gradle.plugin-publish, you are not affected. Just double check that your build does not use JCenter directly.

If you are publishing your plugin to Bintray, you will need to use the com.gradle.plugin-publish plugin to publish new versions of your plugin. The sooner you can do this, the better. We can help if you have questions or problems.

We will automatically migrate all plugins published to Bintray on March 31, 2021. After that date, we will no longer synchronize plugins published to Bintray. This migration should be transparent to users resolving dependencies.

Impact to packages publishing to Bintray

If you are publishing your builds to Bintray using the com.jfrog.bintray plugin, you’ll need to find another repository to host your package. You may be able to publish using the built-in maven-publish.

If you’re migrating to Maven Central, the io.github.gradle-nexus.publish-plugin plugin automates publishing to Nexus and Maven Central.

Security considerations

There are a few security considerations you should make before migrating from JCenter to another repository.

As we have seen in recent years, dependency confusion and namespace collisions can be serious dangers for supply chain attacks. Each of these vulnerabilities relied on untrusted artifacts being used in place of trusted ones.

When you change the list of repositories you use to resolve dependencies, you may inadvertently expose yourself to these kinds of attacks.

For instance, assume you have a build that uses JCenter and another less popular repository (a.k.a, LessPopularCenter) to resolve dependencies (in that order) and your build has a dependency on com.example:foo:1.0. Package com.example:foo was only published to JCenter, but someone else has managed to put a copy of com.example:foo:1.0 on LessPopularCenter. As long as JCenter is the first place Gradle checks for dependencies, your build would always get the official package.

If you remove JCenter and replace it with Maven Central (which doesn’t have a copy of com.example:foo), your build may still be able to resolve com.example:foo because LessPopularCenter has a copy of it. Unfortunately, there are no guarantees that the copy on LessPopularCenter is the same as the official package.

If you want a strong assurance that the artifacts that you rely on now are the same or come from the same trusted party, you should enable dependency verification. Once enabled, Gradle automatically performs dependency verification for all dependencies resolved from repositories. This will help you detect unexpected changes to a dependency’s artifacts, compromised artifacts or new untrusted sources of artifacts.

Dependency verification requires some up-front setup. You need to specify a list of trusted keys to use when verifying signed artifacts. For unsigned artifacts, you need to specify expected checksums for each artifact.

Dependency verification failures don’t necessarily mean that something nefarious has happened. Dependency metadata and artifacts are messy. There are sometimes legitimate reasons for a package to have different artifacts from two repositories for the same version. If the package was published to JCenter and Maven Central separately, it could have been built twice and produced different artifacts that behave the same. This means that even when you resolve the “same” version from both repositories, when you compare the artifacts byte-for-byte, you will get different results. In that case, you could tell Gradle to accept both checksums as valid.

Feedback

Let us know if you have any questions on our forums or Gradle Community Slack.