How does ModelBuilder differ from getModel, and what can you configure on it before fetching a model?
answer
- model(type) -> ModelBuilder<T>
- getModel == model(type).get()
- withArguments / setJvmArguments / progress listener
- forTasks before building model
- get() sync vs get(handler) async
basics
~10 sconnection.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().
solid answer
~30 s`getModel(type)` is a convenience for the common case. When you need control you call `connection.model(type)` to get a **`ModelBuilder<T>`** — a `LongRunningOperation` you configure fluently before fetching. You can `withArguments(...)` (Gradle CLI args like `--offline`), `setJvmArguments(...)`, `setEnvironmentVariables(...)`, set `setStandardOutput/Error`, attach `addProgressListener(...)` for build events, and `forTasks(...)` to run tasks before the model is built. Fetch synchronously with `.get()` or asynchronously with `.get(ResultHandler)`. This is also where you opt into newer behavior — for example forcing the daemon to configure with specific properties. Reuse the same `ProjectConnection`; only the per-request builder carries the customization.
code
java · 6 linesIdeaProject idea = connection.model(IdeaProject.class)
.withArguments("--offline")
.setJvmArguments("-Xmx1g")
.forTasks("generateProto") // run a task before reading the model
.addProgressListener(e -> System.out.println(e.getDisplayName()))
.get();go deeper
Know that getModel is the simple form and model(type) is the configurable one.
List the key configuration knobs (arguments, JVM args, progress listener, forTasks) and sync vs async fetch.
Explain how IDEs rely on this for offline/custom-property imports and cancellation.
Discuss LongRunningOperation as the shared base and how operation configuration stays consistent across model/build/test launchers.
## Two ways to ask for a model `ProjectConnection` exposes: - `T getModel(Class<T>)` and `getModel(Class<T>, ResultHandler<? super T>)` — fire-and-forget convenience. - `<T> ModelBuilder<T> model(Class<T>)` — returns a configurable builder. `getModel(type)` is literally shorthand for `model(type).get()`. Reach for `model(type)` whenever you need more than the default. ## What ModelBuilder lets you configure `ModelBuilder<T>` extends `LongRunningOperation` (the same base shared by `BuildLauncher`, `TestLauncher`, and `BuildActionExecuter`), so it carries the full operation-configuration surface: - **Arguments**: `withArguments("--offline", "-Pkey=value")` — passes Gradle command-line arguments. - **JVM/daemon**: `setJvmArguments("-Xmx512m")`, `setEnvironmentVariables(map)`. - **System properties / init scripts** via arguments. - **Output**: `setStandardOutput(out)`, `setStandardError(err)`, `setStandardInput(in)`, `setColorOutput(false)`. - **Events**: `addProgressListener(listener, OperationType...)` to receive task/test/configuration progress while the model is computed. - **Run tasks first**: `forTasks("clean")` makes Gradle execute those tasks before building the model (useful when a model depends on generated sources). - **Cancellation**: `withCancellationToken(token)`. Then fetch: ```java ModelBuilder<IdeaProject> builder = connection.model(IdeaProject.class); builder.withArguments("--offline") .setJvmArguments("-Xmx1g") .addProgressListener(event -> log(event.getDisplayName())); IdeaProject idea = builder.get(); // blocking // or async: builder.get(new ResultHandler<IdeaProject>() { public void onComplete(IdeaProject result) { /* ... */ } public void onFailure(GradleConnectionException e) { /* ... */ } }); ``` ## Why it matters IDEs use this to import projects exactly the way the user configured them: offline mode, custom Gradle properties, a specific JDK, or to force a `forTasks` run so generated stubs exist before the IDEA model is read. The builder is per-request; the connection is long-lived and reused. ## Async vs sync `.get()` blocks the calling thread. `.get(resultHandler)` returns immediately and invokes the handler on a Tooling-API thread — IDEs prefer this to keep the UI responsive, often combined with a `CancellationToken` so a user can abort an import.
- Why might you call forTasks(...) on a ModelBuilder before get()?So Gradle executes those tasks (e.g. code generation) before the model is built, ensuring the model reflects generated sources/classpath entries.
- How do you fetch a model without blocking the caller thread?Use the asynchronous get(ResultHandler) overload; it returns immediately and invokes onComplete/onFailure on a TAPI thread, optionally with a CancellationToken.
saying these in an interview costs you the question
- Saying getModel can take arguments — it can't; you need model(type)
- Confusing forTasks (run tasks then build model) with newBuild (run only)
- Reconnecting a new ProjectConnection per model instead of reusing one