skip to content

Plugin ID Metadata & java-gradle-plugin

The java-gradle-plugin DSL that declares plugin ids and implementation classes and generates the marker artifacts. Asked because it explains how a plain id(...) in a build script finds your code.

on this pageshow

questions

5

How do you declare a plugin id for a custom Gradle plugin using the java-gradle-plugin plugin, and what is the role of implementationClass?

level: juniorimportance: must knowfreq 70%

answer

  1. apply java-gradle-plugin
  2. gradlePlugin { plugins { create … } }
  3. id + implementationClass
  4. generates META-INF/gradle-plugins/<id>.properties
  5. Plugin<Project>.apply called on apply

basics

~20 s

Apply the java-gradle-plugin plugin, then in the gradlePlugin { plugins { … } } block register a plugin entry with an id and an implementationClass — the fully-qualified name of your Plugin<Project> class that Gradle instantiates when that id is applied.

solid answer

~40 s

You apply the `java-gradle-plugin` plugin to your build-logic project. It exposes the `gradlePlugin` extension where you register named plugin entries: ```kotlin gradlePlugin { plugins { create("greeting") { id = "com.acme.greeting" implementationClass = "com.acme.GreetingPlugin" } } } ``` `id` is the public identifier consumers use in `plugins { id("com.acme.greeting") }`. `implementationClass` is the fully-qualified name of the class implementing `Plugin<Project>` (or `Plugin<Settings>`). When a build applies the id, Gradle reads the descriptor, finds this class and calls its `apply` method. The `java-gradle-plugin` plugin also auto-generates the plugin descriptor file and a plugin marker artifact so the id can be resolved from a repository.

code

kotlin · 12 lines
kotlin
plugins {
  `java-gradle-plugin`
}

gradlePlugin {
  plugins {
    create("greeting") {
      id = "com.acme.greeting"
      implementationClass = "com.acme.GreetingPlugin"
    }
  }
}

go deeper

for a junior

Recall that you apply java-gradle-plugin and set id + implementationClass in gradlePlugin { plugins { … } }.

for a middle

Explain the generated descriptor file and that the entry name differs from the id; show the Kotlin DSL.

for a senior

Discuss why id and class are decoupled, reverse-DNS naming, and what the descriptor contract means at apply time.

for a principal

Frame id conventions as an org-wide contract (namespacing, stability across renames) and how that affects internal plugin catalogs.

## What a plugin id is A Gradle plugin needs a stable, public **id** so build scripts can apply it with `plugins { id("...") }`. The id is decoupled from the implementation class name on purpose: the class can move or be renamed without breaking consumers, and one published module can expose several ids. ## The `java-gradle-plugin` plugin Applying `java-gradle-plugin` to a JVM project does three things: 1. Adds the `gradleApi()` dependency to `implementation` so you can compile against Gradle types. 2. Adds the `gradlePlugin` extension (a `GradlePluginDevelopmentExtension`) for declaring plugins. 3. Wires tasks that generate the **plugin descriptor** and **plugin marker** artifacts. ## Declaring a plugin ```kotlin gradlePlugin { plugins { create("greeting") { // the NamedDomainObjectContainer entry name (arbitrary) id = "com.acme.greeting" // public id consumers apply implementationClass = "com.acme.GreetingPlugin" } } } ``` - The container entry name (`"greeting"`) is internal bookkeeping; it is **not** the id. - `id` must be a valid plugin id: lowercase, dot-separated namespaces, typically reverse-DNS to avoid collisions. - `implementationClass` is the fully-qualified class name implementing `Plugin<Project>`. ## The descriptor file From each entry, the `java-gradle-plugin` generates `META-INF/gradle-plugins/<id>.properties` on the classpath containing one line: ``` implementation-class=com.acme.GreetingPlugin ``` This is the actual runtime contract: when a build applies `com.acme.greeting`, Gradle looks up `com/acme/greeting` → wait, it looks up `META-INF/gradle-plugins/com.acme.greeting.properties`, reads `implementation-class`, loads that class and instantiates it. ## Why use the DSL instead of hand-writing the descriptor Before `java-gradle-plugin` you hand-authored the `.properties` file. The DSL is preferred because it also generates **plugin marker artifacts** (needed for `plugins {}` resolution from a repository) and feeds the metadata into validation and publishing tasks.

  • What file does java-gradle-plugin generate from a plugin declaration, and what does it contain?
    A descriptor at META-INF/gradle-plugins/<id>.properties containing a single line implementation-class=<fully-qualified class>. Gradle reads it at apply time to find and instantiate the plugin class.
  • Is the name passed to create("greeting") the same as the plugin id?
    No. That name is just the container entry key for build-logic bookkeeping. The public id is the separate id property you assign inside the block.

saying these in an interview costs you the question

  • Confusing the container entry name with the public plugin id.
  • Hand-writing the META-INF descriptor when java-gradle-plugin already generates it.
  • Claiming implementationClass is the id rather than the FQN of the Plugin<Project> class.

context

open as a page

What are Gradle plugin marker artifacts, and why are they needed for resolving plugins via the plugins {} block from a custom repository?

level: middleimportance: must knowfreq 55%

basics

~20 s

A plugin marker is a tiny published module whose coordinates are derived from the plugin id (id:id.gradle.plugin) and whose only job is to declare a dependency on the real plugin jar. It lets plugins {} resolve a plugin by id rather than by Maven coordinates.

open as a page

How do you declare multiple plugin ids from a single build-logic project, and what gets generated for each?

level: middleimportance: should knowfreq 35%

basics

~20 s

Add several create("…") entries inside gradlePlugin { plugins { … } }, each with its own id and implementationClass. Each entry produces its own descriptor file and, when publishing, its own marker artifact, all backed by the same implementation jar.

open as a page

Where do the group and version metadata for a published plugin come from, and how do they relate to the plugin id and marker?

level: middleimportance: should knowfreq 40%

basics

~20 s

The implementation module's coordinates come from the project's group and version properties. The version requested in plugins {} must match the marker version, which derives from the same project version. The id is independent of group.

open as a page

From an architecture standpoint, why does Gradle separate the public plugin id from the implementationClass, and what does that decoupling buy a large organization maintaining many internal plugins?

level: seniorimportance: should knowfreq 25%

basics

~20 s

Separating id from class makes the id a stable public contract while the implementation can be refactored, renamed, or repackaged freely. For an org, ids become a governable namespace independent of code layout, and the marker layer lets repositories resolve them uniformly.

open as a page