skip to content

Querying and Custom Tooling Models

Querying built-in models such as GradleProject and IdeaProject, and exposing custom ones with a ToolingModelBuilder. Interviewers ask because custom models are how a plugin talks to IDE tooling.

on this pageshow

questions

5

What are the built-in models the Gradle Tooling API exposes (GradleProject, EclipseProject, IdeaProject), and how do you query one from a ProjectConnection?

level: juniorimportance: must knowfreq 55%

answer

  1. GradleProject / EclipseProject / IdeaProject
  2. connection.getModel(Type.class)
  3. read-only serialized snapshot
  4. model(type) -> ModelBuilder
  5. close the connection

basics

~10 s

The Tooling API ships read-only model interfaces like GradleProject, EclipseProject and IdeaProject. You call connection.getModel(EclipseProject.class) on a ProjectConnection to fetch a snapshot describing the build's structure.

solid answer

~30 s

The Tooling API (TAPI) lets external tools introspect a Gradle build through immutable model objects. Built-in models include `GradleProject` (generic project/task tree), `EclipseProject` (classpath, source dirs for Eclipse) and `IdeaProject`/`IdeaModule` (IntelliJ-shaped view). You obtain a `ProjectConnection` via `GradleConnector`, then call `connection.getModel(EclipseProject.class)`. Gradle runs configuration in a separate daemon and returns a serialized, read-only snapshot. The returned objects are plain proxies — navigating them (e.g. `eclipseProject.getClasspath()`) does not re-run the build. Always close the connection in a finally/try-with-resources. For configuration flags use `connection.model(type)` to get a `ModelBuilder` you can customize before `get()`.

code

java · 7 lines
java
try (ProjectConnection connection = GradleConnector.newConnector()
        .forProjectDirectory(new File("my-project"))
        .connect()) {
    GradleProject root = connection.getModel(GradleProject.class);
    System.out.println("Project: " + root.getName());
    root.getTasks().forEach(t -> System.out.println("  task: " + t.getName()));
}

go deeper

for a junior

Name the three common models and show connection.getModel(Type.class) plus closing the connection.

for a middle

Explain the snapshot/serialization nature and the model() -> ModelBuilder path for customization.

for a senior

Discuss why detachment enables cross-version support and the trade-off of fixed model surface.

for a principal

Frame model querying as the contract IDEs (Buildship, IntelliJ) depend on, and the compatibility guarantees Gradle must keep.

## What the Tooling API is The **Tooling API (TAPI)** is a client library that lets an external program (an IDE, a CI tool, a script) drive and inspect a Gradle build *without* being a Gradle plugin and *without* sharing a classpath with Gradle. It talks to the **Gradle daemon** over a wire protocol. The headline use cases are: run tasks/tests programmatically, listen to build events, and **query models** — the focus here. ## Models are read-only snapshots A *model* is an interface (e.g. `org.gradle.tooling.model.GradleProject`) whose getters describe some aspect of the configured build. When you ask for a model, Gradle configures the build in the daemon, builds the requested model, **serializes** it, and hands the client a proxy. Navigating that proxy is pure in-memory traversal — no further build work happens. Built-in models: - **`GradleProject`** — generic: project path, name, build script, the task tree, child projects. Tool-agnostic. - **`EclipseProject`** — Eclipse/Buildship view: project dependencies, external classpath entries, source directories, linked resources. - **`IdeaProject`** / **`IdeaModule`** — IntelliJ view: JDK/language level, modules, content roots, dependencies. - Others: `BuildEnvironment` (Gradle/Java versions, JVM args), `BuildInvocations` (tasks + selectors). ## Querying a model ```java ProjectConnection connection = GradleConnector.newConnector() .forProjectDirectory(new File("/path/to/project")) .connect(); try { EclipseProject project = connection.getModel(EclipseProject.class); for (EclipseProjectDependency dep : project.getProjectDependencies()) { System.out.println(dep.getPath()); } } finally { connection.close(); } ``` `getModel(Class)` is the one-shot convenience. For control, use `connection.model(EclipseProject.class)` which returns a **`ModelBuilder<EclipseProject>`** — you can set JVM args, system properties, arguments, standard out/err, progress listeners, then call `.get()` (blocking) or `.get(resultHandler)` (async). ## Why a snapshot Because the model is detached and serialized, the client never holds live Gradle objects and is insulated from Gradle's classpath. That is what lets a single IDE talk to many Gradle versions. The trade-off: the model exposes only what Gradle chose to put in it — you can't navigate to arbitrary build internals. ## Lifecycle hygiene A `ProjectConnection` holds a daemon connection; always `close()` it (try-with-resources works since it's `AutoCloseable`). Reuse one connection for multiple `getModel` calls against the same project rather than reconnecting.

  • Does navigating the returned EclipseProject re-run any Gradle configuration?
    No. The model is a serialized read-only snapshot built once when you call getModel/get; traversing its getters is pure in-memory work.
  • When would you use connection.model(type) instead of getModel(type)?
    When you need to customize the request — pass JVM args, arguments, system properties, attach a progress listener, or run asynchronously — before fetching, via the returned ModelBuilder.

Querying a model is like asking a REST API for a JSON document about the build: you get a detached snapshot you can read, not a live handle into the server's memory.

saying these in an interview costs you the question

  • Claiming getModel returns a live, mutable Gradle Project object
  • Forgetting to close the ProjectConnection (leaks daemon connections)
  • Thinking each getter call triggers a fresh build

context

open as a page

When would you query EclipseProject vs IdeaProject vs GradleProject, and what does each expose?

level: middleimportance: should knowfreq 32%

basics

~10 s

GradleProject is the generic, tool-agnostic view (project tree, tasks). EclipseProject and IdeaProject are IDE-shaped views adding classpath, source dirs, dependencies, and (for IDEA) JDK/language level — used by Buildship and IntelliJ respectively.

open as a page

How does ModelBuilder differ from getModel, and what can you configure on it before fetching a model?

level: middleimportance: should knowfreq 40%

basics

~10 s

connection.model(Type.class) returns a ModelBuilder you can customize — JVM args, build arguments, system properties, progress/output listeners — before calling get(). getModel(Type.class) is the no-config shortcut equivalent to model(Type).get().

open as a page

What problem does a BuildAction solve over repeated getModel calls, and how do you use BuildActionExecuter to aggregate models across a multi-project build?

level: seniorimportance: should knowfreq 28%

basics

~10 s

A BuildAction runs your code inside the daemon with a BuildController, so you can fetch and combine many per-project models in one round trip instead of calling getModel repeatedly. You run it with connection.action(buildAction).run().

open as a page

How do you expose a custom model to Tooling API clients using ToolingModelBuilder and the ToolingModelBuilderRegistry?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Write a ToolingModelBuilder that returns your model for a given model name, register it in a plugin via the ToolingModelBuilderRegistry, and clients fetch it with connection.getModel(YourModel.class). The model interface must be on both classpaths.

open as a page