| name | java-jackknife |
| description | Use jackknife Maven plugin to explore JARs, search types and members, decompile classes, inspect annotations, and instrument bytecode. Trigger when the user needs to: investigate a JAR or dependency; find where a specific class, interface, annotation, or package is defined; locate which JAR provides a type (ClassNotFoundException, NoSuchMethodError, classpath conflicts); decompile or inspect bytecode of a class.
|
| metadata | {"author":"Wesley Egberto","scope":"tooling","version":"2.0.0"} |
Jackknife
Instalação
Adicione o grupo do plugin ao ~/.m2/settings.xml para usar o comando curto mvn jackknife:* sem prefixo de grupo:
<settings>
<pluginGroups>
<pluginGroup>org.tomitribe.jackknife</pluginGroup>
</pluginGroups>
</settings>
Sem essa configuração, os comandos precisam do prefixo completo:
mvn org.tomitribe.jackknife:jackknife-maven-plugin:instrument ...
Uso
Two capabilities for working with Java dependencies:
- Explore — Find classes, read decompiled source, understand APIs.
No more digging through ~/.m2/repository or guessing at method signatures.
- Debug — Instrument methods to capture arguments, return values,
exceptions, and timing as structured JSON. No more adding println
statements to tests.
Exploring Dependencies
Find a class in project dependencies
Manifests list every class in every dependency jar. One class name per line.
Grep "CustomField" .jackknife/manifest/
This tells you which jar contains the class and its full package name.
Find a class anywhere in ~/.m2
If the class isn't a project dependency, search the local Maven repository:
mvn jackknife:index -Dclass=com.fasterxml.jackson.databind.ObjectMapper
This searches ~/.m2/repository using the package name to narrow the
search, finds the latest version, and indexes the jar. The class is
then available in the manifest and can be decompiled.
For broader searches across a library family:
mvn jackknife:index -Dscope=repo -Dfilter="**jackson**"
This indexes the latest version of every jar whose path matches the
filter. Use ** to match across directory boundaries.
Read source code
Check if the class has already been decompiled:
Glob .jackknife/source/**/<ClassName>.java
If found, read it directly. If not, decompile the entire jar (one-time, ~3-5s):
mvn jackknife:decompile -Dclass=com.example.MyClass
Every class in that jar is now available as a .java file. All subsequent
reads are direct file access — no Maven invocation needed.
Find resources
Grep "META-INF/services" .jackknife/manifest/
Directory structure
.jackknife/
├── manifest/ Class listings (all jars, sub-second)
│ └── <groupId>/
│ └── <artifact>-<version>.jar.manifest
├── source/ Decompiled source (per-jar, on demand)
│ └── <groupId>/
│ └── <artifact>-<version>/
│ └── com/example/MyClass.java
├── instrument/ Pending instrumentation configs
├── modified/ Patched jars (applied on next build)
└── USAGE.md This file
Debugging with Instrumentation
The workflow
- A test fails or you need to understand what a method receives and returns
- Instrument the method — jackknife injects debug output, no source changes
- Run
mvn test — structured JSON output shows exactly what happened
- Extract values from the JSON to fix assertions or understand behavior
- Clean up when done:
mvn jackknife:clean -Dpath=modified
Instrument a method
mvn jackknife:instrument -Dmethod="com.example.Foo.bar(java.lang.String,int)"
Matching granularity
| Input | What gets instrumented |
|---|
com.example.Foo.bar(String,int) | That one method |
com.example.Foo.bar | All overloads of bar |
bar(String,int) | bar(String,int) in any class |
Modes
- debug (default) — args, return value, exceptions, and timing
- timing — elapsed time and status only, no args or return values
mvn jackknife:instrument -Dmethod="com.example.Foo.bar" -Dmode=timing
Run the build
mvn test
The next build automatically applies the instrumentation. No special flags.
Instrumenting project code
Jackknife can also instrument your own classes in src/main/ or src/test/.
If the class isn't found in dependency manifests, it checks your project source:
mvn jackknife:instrument -Dclass=com.example.MyService -Dmethod=process
The enhance goal (bound to process-test-classes) modifies the compiled
bytecode in target/classes/ and target/test-classes/ in place. Tests run
against the enhanced bytecode automatically.
Reading the output
Every instrumented call produces one line prefixed with JACKKNIFE:
JACKKNIFE {"event":"register","mode":"debug","method":"org.tomitribe.util.Join.join"}
JACKKNIFE {"event":"call","time":"12.3us","class":"Join","method":"join","args":[", ",["x","y","z"]],"return":"x, y, z"}
The register line confirms instrumentation is active. If you see the
register event but no call events, the method was never called by your test.
Grep for all instrumented calls:
Grep "JACKKNIFE" target/surefire-reports/
JSON fields
| Field | Description |
|---|
event | "register" or "call" |
time | Elapsed time (ns, us, ms, s) |
class | Simple class name |
method | Method name |
args | JSON array of argument values |
return | Return value (absent on exception) |
exception | {"type":"...","message":"..."} (absent on success) |
status | "returned" or "thrown" (on file-reference lines) |
file | Capture file path (when values too large for one line) |
Extracting values for assertions
Strings are JSON-quoted, numbers are bare, null is null, booleans are bare.
Copy values directly from the "return" field into assertEquals:
assertEquals("a and b and c", result);
assertEquals(42, result);
assertNull(result);
Capture files
When arguments or return values are too large for a single line, the full
JSON event is written to a capture file:
JACKKNIFE {"event":"call","time":"2.3ms","class":"Join","method":"join","status":"returned","file":"target/jackknife/captures/capture-0012.txt"}
The console line shows timing, method, and status for quick scanning.
Read the capture file for the complete JSON event with all argument
and return values.
Cleaning up
mvn jackknife:clean # remove all of .jackknife/
mvn jackknife:clean -Dpath=modified # stop instrumentation, keep manifests
mvn jackknife:clean -Dpath=modified/org.tomitribe # remove one groupId's patches
mvn jackknife:clean -Dpath=source # clear decompile cache
After cleaning modified/, the next build uses original unmodified dependencies.