.jar` and a `.sha256` file holding the JAR's
-SHA-256. The SDK is not published to a Maven repository. Use it like this:
-
-1. Download the JAR during your plugin's build, never at runtime.
-2. Check the JAR against the SHA-256 you pinned in your repository.
-3. Shade it into your backend JAR, as the sample's `pom.xml` does.
-
-A plugin package is installed read-only and must run offline, so the backend
-JAR it starts must hold everything it loads.
-
-## API
-
-| API | Purpose |
-| --- | --- |
-| `Backend.create(name, version[, capabilities])` | Creates the backend. `capabilities` are standard LSP server capabilities. `experimental.litheBuild` and `experimental.litheFunctions` are reserved for the SDK. |
-| `onModel(handler)`, `onDependencies(handler)` | Implement the typed build requests. The SDK stamps `format` and `version`. Core validates the document. `litheBuild` is answered only when a handler exists. |
-| `function(id, handler)` | Declares a function offered in `initialize`. |
-| `registerFunction(id, handler)`, `unregisterFunction(id)` | Add or withdraw a function. Called before the `initialize` answer (for example from `onInitialize`), they change the initial list. Later calls send `lithe/function/register` or `lithe/function/unregister`, in order. Both return a `CompletableFuture`. Withdrawing a function revokes its running calls. |
-| `onInitialize(hook)`, `onShutdown(hook)` | Lifecycle hooks. `onInitialize` receives the `initialize` params and what was negotiated. |
-| `onRequest(method, handler)`, `onNotification(method, handler)` | Ordinary LSP methods. Lifecycle and `lithe/*` methods are reserved. |
-| `sendRequest`, `sendNotification` | Ordinary LSP messages to the host. `lithe/*` methods are reserved. |
-| `log(message[, error])` | Writes to stderr, which the host keeps in its log. |
-| `listen()` | Serves stdio until `exit` or the end of input, then exits the JVM with the protocol's exit code. `listen(in, out, err)` serves other streams and returns the code instead. |
-| `PluginError(code, message)` | A failure meant for your own UI. |
-| `Processes.run(command[, options])` | Runs a process without a shell. Its stdin is closed. `Options` set the directory, environment, output limits, a cancellation, and output callbacks. |
-| `StateStore.open(directory)` | A small atomic JSON store in a host-provided directory. |
-
-Handlers run on their own threads. Every handler receives a `CallContext`:
-
-| Member | Meaning |
-| --- | --- |
-| `cancellation()`, `isCancelled()` | Cancelled when any of these happens: Lithe cancels the call, its deadline passes, the function is withdrawn, the session ends, or the handler finishes. Finishing the handler stops the work it left running. |
-| `throwIfCancelled()` | Throws `CancelledException` once the call is cancelled. |
-| `run(command[, options])` | `Processes.run` bound to the call: cancelling or ending the call stops the process. |
-| `log(message)` | Same as `Backend.log`. |
-| `functionId()`, `operationId()` | Functions only. `operationId` is opaque; never derive anything from it. |
-| `progress(message, percentage)` | Functions only. Either argument may be `null`. The message is cut to 1024 bytes and the percentage is clamped to 0–100. Reports sent after the call ends are dropped. |
-
-Results and errors map exactly as in the Node.js SDK:
-
-- A returned value becomes `result.value`.
-- `PluginError` becomes `plugin.function.failed` with your message and
- `pluginErrorCode`.
-- Any other exception is reported with a generic message, and its stack trace
- goes to stderr.
-- `CancelledException` ends the call as cancelled.
-- A result larger than 1 MiB of JSON fails like an unexpected error.
-
-## Rules that keep Lithe in charge
-
-These are the [Node.js SDK's rules](../node/README.md#rules-that-keep-lithe-in-charge).
-In Java:
-
-1. **Domain capabilities stay typed.** Build models and dependency trees go
- through `onModel` and `onDependencies`, never through a function.
-2. **No calls back into Lithe's dispatch.** The SDK refuses to send any
- `lithe/*` method.
-3. **stdout belongs to the protocol.** `listen()` keeps the real stdout for
- protocol frames and points `System.out` at stderr. Never write to
- `FileDescriptor.out`.
-4. **The package is read-only.** Keep state under the directories Lithe
- passes through launch templates (`${stateDir}`, `${workspaceStateDir}`).
- `StateStore.open` refuses a directory inside the plugin package. It finds
- the package by walking up from the SDK's own JAR to `plugin.json`.
-5. **Work ends with its call.** Start processes with `context.run`, and check
- `isCancelled()` in long loops. On cancellation the SDK first asks the
- process to stop, then stops it forcibly after a grace period.
- - **macOS and Linux, every Java version:** each tool leads its own process
- group, as in the Node SDK. The SDK starts it through the system `perl`,
- which calls `setpgrp` and then becomes the tool. A stop reaches the whole
- group, and when the tool exits on its own, whatever it left running in
- its group is killed.
- - **Windows, or no `perl`:** on Java 9 or newer the SDK stops the
- descendants still attached to the tool; Java 8 stops only the direct
- child.
-
- A descendant that leaves the group (`setsid`) or that the fallback cannot
- reach is ended by Lithe's process scope together with the backend.
- A cancellation listener that throws is logged and does not keep the other
- listeners or the reply from running.
-
- On `shutdown`, `exit`, or the end of input the SDK ends every process it
- started. `Processes.run` keeps Lithe's process-scope markers even when
- `Options.environment` replaces the environment.
-6. **Errors stay presentable.** Throw `PluginError` for failures the user
- should see; every other exception is reported generically.
-
-## Testing
-
-```bash
-mvn -B -f sdk/java/pom.xml verify
-```
-
-This builds the SDK and the sample for Java 8 with any newer JDK, then runs
-the JUnit tests. Add `-Djvm=/path/to/java8/bin/java` to run the tests on a
-Java 8 runtime, as CI does.
diff --git a/sdk/java/examples/sample-build/plugin.json b/sdk/java/examples/sample-build/plugin.json
deleted file mode 100644
index 8e93ebc10a..0000000000
--- a/sdk/java/examples/sample-build/plugin.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- "schemaVersion": 2,
- "id": "org.example.sample-build-java",
- "displayName": "Sample Build (Java)",
- "version": "0.1.0",
- "pluginAPIVersion": 3,
- "requires": {
- "buildProviders": 1,
- "buildSystems": 1,
- "serverProcess": 1
- },
- "vendor": {
- "id": "org.example",
- "displayName": "Example",
- "signatureRequirement": "publisherPackage"
- },
- "permissions": [
- "languageServer.launch",
- "process.build"
- ],
- "activationEvents": [
- "onBuildSystem:sample"
- ],
- "languages": [],
- "buildSystems": [
- {
- "id": "sample",
- "displayName": "Sample",
- "detection": {
- "markers": [
- "lithe-sample.json"
- ],
- "nesting": "outermost"
- },
- "invocation": {
- "executables": [
- {
- "kind": "command",
- "command": "java"
- }
- ]
- },
- "provider": {
- "launch": {
- "kind": "runtime",
- "tool": "java",
- "minimumVersion": "8",
- "entry": "backend/sample-build.jar"
- },
- "arguments": [
- "--state",
- "${workspaceStateDir}"
- ],
- "executesProjectCode": true,
- "mayUseNetwork": false
- },
- "model": {
- "source": "provider"
- },
- "tasks": [
- {
- "id": "build",
- "displayName": "Build",
- "group": "build",
- "scopes": [
- "workspace"
- ],
- "arguments": [
- "-version"
- ]
- }
- ],
- "dependencies": {
- "source": "provider",
- "scopes": [
- "workspace",
- "project"
- ]
- },
- "sync": {
- "watch": [
- "lithe-sample.json"
- ]
- }
- }
- ]
-}
diff --git a/sdk/java/examples/sample-build/pom.xml b/sdk/java/examples/sample-build/pom.xml
deleted file mode 100644
index cacbb00d09..0000000000
--- a/sdk/java/examples/sample-build/pom.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
- 4.0.0
-
-
- io.github.lithe-idea
- lithe-plugin-sdk-java-parent
- 0.1.0
- ../../pom.xml
-
-
- sample-build
- Lithe sample build provider (Java)
-
-
-
- io.github.lithe-idea
- lithe-plugin-sdk-java
- ${project.version}
-
-
-
-
- sample-build
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
-
-
- package
-
- shade
-
-
- false
-
-
- org.example.samplebuild.SampleBuild
-
-
-
-
-
-
-
-
-
diff --git a/sdk/java/examples/sample-build/src/main/java/org/example/samplebuild/SampleBuild.java b/sdk/java/examples/sample-build/src/main/java/org/example/samplebuild/SampleBuild.java
deleted file mode 100644
index 492f9e210d..0000000000
--- a/sdk/java/examples/sample-build/src/main/java/org/example/samplebuild/SampleBuild.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package org.example.samplebuild;
-
-import io.github.litheidea.plugin.sdk.Backend;
-import io.github.litheidea.plugin.sdk.CallContext;
-import io.github.litheidea.plugin.sdk.Json;
-import io.github.litheidea.plugin.sdk.PluginError;
-import io.github.litheidea.plugin.sdk.Processes;
-import io.github.litheidea.plugin.sdk.StateStore;
-import java.io.IOException;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Sample Lithe plugin backend built on the official Java SDK; the Java twin of
- * {@code sdk/node/examples/sample-build}.
- *
- * It is the build provider of the fictional "sample" build system, whose
- * workspace file {@code lithe-sample.json} lists projects:
- *
- *
- * { "projects": [{ "name": "app", "path": "app",
- * "build": ["/usr/bin/make"],
- * "dependencies": [{ "name": "left-pad", "version": "1.3.0" }] }] }
- *
- *
- * It shows the three things a backend does:
- *
- * - answers Lithe's typed build requests ({@code lithe/build/model},
- * {@code lithe/build/dependencies}), whose results Lithe itself understands;
- * - offers plugin functions for its own UI: {@code workspace.build} runs a
- * multi-step build (prepare, run each project's tool, record a snapshot),
- * and {@code snapshot.read} returns the last snapshot, also after a restart;
- * - registers {@code snapshot.clear} at runtime once there is a snapshot to clear.
- *
- *
- * A packaged plugin shades the SDK into its own JAR, as this module's POM does.
- */
-public final class SampleBuild {
- private final Backend backend = Backend.create("sample-build", "0.1.0");
- private final String stateDirectory;
- private volatile Path workspaceRoot;
- private volatile StateStore store;
-
- private SampleBuild(String stateDirectory) {
- this.stateDirectory = stateDirectory;
- }
-
- public static void main(String[] arguments) {
- new SampleBuild(argument(arguments, "--state")).serve();
- }
-
- private void serve() {
- backend.onInitialize((params, negotiated) -> {
- // `Paths.get(URI)` accepts the trailing slash a directory URI may carry.
- workspaceRoot = Paths.get(URI.create((String) params.get("rootUri"))).toAbsolutePath().normalize();
- // `--state ${workspaceStateDir}` in plugin.json: the host's writable
- // directory for this workspace. The package itself is read-only.
- store = StateStore.open(Paths.get(stateDirectory));
- if (store.read("snapshot", null) != null) {
- offerClear();
- }
- });
-
- backend.onModel((params, context) -> {
- List