|
| 1 | +# nitrite-bridge |
| 2 | + |
| 3 | +Inspect a running Nitrite database from a desktop client, over a paired, loopback-by-default |
| 4 | +connection. |
| 5 | + |
| 6 | +This artifact is the **Nitrite adapter only**. The wire protocol, pairing, transport and the |
| 7 | +release guard are in `org.dizitart:dbinspect-bridge`, which knows about no database at all — so a |
| 8 | +JVM developer inspecting an H2, MapDB or JDBC database takes that artifact and writes their own |
| 9 | +adapter, and nothing about that path routes through a Nitrite repository. |
| 10 | + |
| 11 | +```java |
| 12 | +BridgeServer bridge = DbInspect.start( |
| 13 | + DbInspect.options("my_app", List.of( |
| 14 | + NitriteAdapter.builder(db, "main", "app data") |
| 15 | + .repositories(orders, customers) |
| 16 | + .build()))); |
| 17 | +``` |
| 18 | + |
| 19 | +## Two things to know before you ship it |
| 20 | + |
| 21 | +**Declare `INTERNET`.** A bridge binds a socket; on Android, without the permission it fails with |
| 22 | +a `SecurityException` that reads like a bug in us. |
| 23 | + |
| 24 | +**Keep it out of your release build.** Everything below is off unless you turn it on, but the |
| 25 | +strongest guard is the artifact not being there at all: |
| 26 | + |
| 27 | +- **Desktop** — `provided` scope, *and* `-Ddbinspect.bridge.enabled=true` at launch. Two signals, |
| 28 | + neither of which survives into a production launch script by accident. |
| 29 | +- **Android** — `debugImplementation`. An application has no command line, so the system property |
| 30 | + can never be set and a guard requiring it could never be satisfied; the packaging is the guard, |
| 31 | + which is a stronger property rather than a weaker one. R8 shrinking is **not** the guard. |
| 32 | + |
| 33 | +Both example applications carry a script that greps the built artifact for the protocol strings, |
| 34 | +and both build twice so the check has a negative control. |
| 35 | + |
| 36 | +## Capabilities, and what is off by default |
| 37 | + |
| 38 | +`edit`, `snapshot` and `regex` are off unless you asked for them on that adapter — `allowWrite`, |
| 39 | +`allowSnapshot`, `allowRegex` on the builder. What is off is *absent from the reported |
| 40 | +capabilities*, not merely refused at call time, so the client greys it out rather than offering |
| 41 | +something that will fail. |
| 42 | + |
| 43 | +**A row is addressed by `_id`.** With `allowWrite(true)`, `updateRow` and `deleteRow` take the |
| 44 | +value the grid showed in that column — a `long` here, and the bracketed `[1755…]NO₂` rendering is |
| 45 | +accepted too, so an id pasted from another runtime's grid works. `_id` inside an update's `values` |
| 46 | +is refused: Nitrite merges an update document, so it would rewrite the identity of the row it just |
| 47 | +matched. An update is partial, and `changes: 0` means the row was not there, which is an answer |
| 48 | +rather than an error. |
| 49 | + |
| 50 | +`capabilities.filterOps` reports what this implementation actually has. Two notes on it: |
| 51 | + |
| 52 | +- **`exists` needs nitrite 5.0.0**, which is the floor this artifact sets. It tests presence only: |
| 53 | + a field explicitly set to null is present and matches, and "does not have the field" is `not` |
| 54 | + around it, never `exists` with `value: false`. |
| 55 | +- **`text` needs a full-text index on the field.** Nitrite's own query planner refuses a text |
| 56 | + filter it cannot serve from an index, and `filterOps` is a flat operator list that cannot say |
| 57 | + "text, but only on `bio`". The adapter refuses per field, with a message that names the fix. |
| 58 | + |
| 59 | +## Repositories are handed in; collections are discovered |
| 60 | + |
| 61 | +`listRepositories()` answers with entity names, and an entity name is not a `Class`. Turning one |
| 62 | +back into a type means loading a class named by a paired client, and the underlying document |
| 63 | +collection cannot be reached by name either — `getCollection` refuses a name a repository owns, |
| 64 | +and re-opening the map behind one would give you a second collection object with its own event |
| 65 | +bus, so `watch` would silently never fire. |
| 66 | + |
| 67 | +So pass the repositories you want inspected. One that was not passed in is not listed, rather than |
| 68 | +listed and unopenable. |
| 69 | + |
| 70 | +## Building it |
| 71 | + |
| 72 | +`dbinspect-bridge` is not on Maven Central yet, so this module is deliberately **not** in |
| 73 | +`nitrite-java`'s `<modules>` — a clean checkout could not resolve it and the whole reactor would |
| 74 | +fail. Install the core, then build this on its own: |
| 75 | + |
| 76 | +```bash |
| 77 | +mvn -f <dbinspect>/jvm/dbinspect-bridge/pom.xml install |
| 78 | +mvn -f nitrite-bridge/pom.xml test |
| 79 | +``` |
| 80 | + |
| 81 | +## Examples |
| 82 | + |
| 83 | +| Path | What it is | |
| 84 | +|---|---| |
| 85 | +| `example/` | a plain `java -jar` desktop application, and `tool/verify_release_jar.sh` | |
| 86 | +| `example-android/` | a native Android (Kotlin) application, and `tool/verify_release_apk.sh` | |
| 87 | + |
| 88 | +Both are browsable from the desktop client, and the dbinspect conformance suite passes unmodified |
| 89 | +against either. `tool/run_reference_bridge.sh` starts a bridge over a memory, MVStore or RocksDB |
| 90 | +database for the suite to be pointed at. |
0 commit comments