Skip to content

Examples

A collection of complete, copy-pasteable invocations. Replace codeanalyzer-2.3.7.jar with your built JAR path (or codeanalyzer if you compiled a native binary).

Parse a project and emit the symbol table. No project build required:

Terminal window
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/commons-cli \
-a 1 \
-o ./output
# -> ./output/analysis.json with symbol_table

Symbol table plus the WALA call graph. The project is built automatically:

Terminal window
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/commons-cli \
-a 2 \
-o ./output \
-v
# -> ./output/analysis.json with symbol_table + call_graph

Single source string (no project, no build)

Section titled “Single source string (no project, no build)”

Analyze a snippet directly; output goes to stdout:

Terminal window
java -jar codeanalyzer-2.3.7.jar \
-s "public class HelloWorld { public static void main(String[] args) {} }" \
-a 1

If the project is already compiled, skip the build step for a faster level-2 run:

Terminal window
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/project \
-a 2 \
--no-build \
-o ./output

Use your own build instead of the auto build:

Terminal window
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/project \
-a 2 \
-b "mvn -q clean package -DskipTests" \
-o ./output

Re-analyze just two files and patch them into an existing analysis.json:

Terminal window
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/project \
-t src/main/java/org/apache/commons/cli/Option.java \
-t src/main/java/org/apache/commons/cli/Options.java \
-o ./output

The named files are tagged is_modified: true in the merged output. See Incremental analysis.

Analyze a submodule while building from the reactor root:

Terminal window
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/project/web-module \
-f /path/to/project/pom.xml \
-a 2 \
-o ./output

Omit -o to stream JSON to stdout and process it inline:

Terminal window
java -jar codeanalyzer-2.3.7.jar -i /path/to/project -a 1 \
| jq '.symbol_table | keys | length'
# prints the number of analyzed source files

--emit neo4j projects the same IR as analysis.json — losslessly — into a Neo4j property graph. With no Bolt URI present, the analyzer writes a self-contained, re-runnable graph.cypher snapshot to the output directory:

Terminal window
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/daytrader8 \
-a 2 \
--emit neo4j \
--app-name daytrader8 \
-o ./out
# -> ./out/graph.cypher

The snapshot is constraints + indexes, a scoped wipe of this application’s prior subgraph, then batched UNWIND ... MERGE for every node and edge. It expresses the full truth of the application — it is not incremental. Load it into a running Neo4j with cypher-shell:

Terminal window
cypher-shell -u neo4j -p "$NEO4J_PASSWORD" < ./out/graph.cypher

When a Bolt URI resolves — from --neo4j-uri or the NEO4J_URI env var — --emit neo4j pushes incrementally to a running Neo4j instead of writing a file. Prefer the NEO4J_* environment variables for credentials so secrets stay off the command line:

Terminal window
export NEO4J_URI=bolt://localhost:7687
export NEO4J_USERNAME=neo4j
export NEO4J_PASSWORD=secret
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/daytrader8 \
-a 2 \
--emit neo4j \
--app-name daytrader8
# -> pushes the daytrader8 subgraph to bolt://localhost:7687

The Bolt writer ensures constraints/indexes, diffs each compilation unit’s content_hash (SHA-256) against the live database, and replaces only changed units’ subgraphs with idempotent MERGE upserts. Shared :JPackage / :JAnnotation nodes are MERGEd in place. On a full run (no -t), units whose source file vanished are pruned.

You can also pass the flags explicitly instead of the env vars; a flag wins when both are set:

Terminal window
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/daytrader8 \
-a 2 \
--emit neo4j \
--app-name daytrader8 \
--neo4j-uri bolt://localhost:7687 \
--neo4j-user neo4j \
--neo4j-database neo4j
# password comes from NEO4J_PASSWORD; --neo4j-password also works

--app-name is the tenancy key: it sets the name of the single :JApplication anchor ((:JApplication {name})-[:J_HAS_UNIT]->(:JCompilationUnit)). Many applications coexist in one database, each rooted at its own anchor, so a wipe or re-push of daytrader8 never touches another app’s subgraph. If you omit --app-name, it defaults to the base name of the -i directory.

Combine -t with a live Bolt push to update only the units you changed. A targeted run replaces just those changed-unit subgraphs and skips orphan pruning — units for files that vanished are left alone, because a targeted run does not claim to know about the whole application:

Terminal window
export NEO4J_URI=bolt://localhost:7687
export NEO4J_PASSWORD=secret
java -jar codeanalyzer-2.3.7.jar \
-i /path/to/daytrader8 \
--emit neo4j \
--app-name daytrader8 \
-t src/main/java/com/ibm/websphere/samples/daytrader/TradeAction.java
# replaces only TradeAction's subgraph; vanished units are NOT pruned

--emit schema publishes the versioned schema contract — every node label, relationship type, and property the projector can produce — with no project analysis required. It short-circuits before any analysis runs. Write it to a file:

Terminal window
java -jar codeanalyzer-2.3.7.jar --emit schema -o ./out
# -> ./out/schema.neo4j.json

Or print it to stdout when -o is omitted — handy for diffing against a checked-in copy in CI:

Terminal window
java -jar codeanalyzer-2.3.7.jar --emit schema \
| jq '.schema_version'
# "1.0.0"

The schema_version (1.0.0) printed here is the same value stamped on the :JApplication node of every graph you emit, so consumers can assert they are reading a contract they understand. See the Neo4j graph schema for the full label and relationship catalog.

If you’d rather not manage the JAR yourself, the CLDK SDK invokes it for you:

from cldk import CLDK
from cldk.analysis import AnalysisLevel
analysis = CLDK(language="java").analysis(
project_path="commons-cli",
analysis_level=AnalysisLevel.call_graph,
)
print(len(analysis.get_classes()), "classes")
print(analysis.get_call_graph()) # -> networkx.DiGraph

See Python SDK integration for details.

Read the graph back from Python (no JAR, no JDK)

Section titled “Read the graph back from Python (no JAR, no JDK)”

Once an application is in Neo4j, the SDK can read it directly — no JDK, no native binary, and no project source on the consumer. It only needs the Bolt URI and read-only credentials. Install the driver extra (pip install cldk[neo4j]), then point CLDK at the same application_name the graph was loaded with:

# Java application — read-only Neo4j backend
from cldk import CLDK
from cldk.analysis import AnalysisLevel
from cldk.analysis.commons.backend_config import Neo4jConnectionConfig
analysis = CLDK.java(
analysis_level=AnalysisLevel.call_graph,
backend=Neo4jConnectionConfig(
uri="bolt://localhost:7687",
username="neo4j",
password="neo4j",
application_name="daytrader8", # == --app-name from the push
),
)
symbol_table = analysis.get_symbol_table() # Dict[str, JCompilationUnit]
cg = analysis.get_call_graph() # networkx.DiGraph
klass = analysis.get_class("com.example.MyService")
methods = analysis.get_methods_in_class("com.example.MyService")

The backend bulk-fetches nodes and relationships in a handful of Cypher queries and rebuilds the same canonical model objects the in-process analyzer produces — the same JType / JCallable symbol table and the same networkx call graph. Analysis is produced once, centrally, by a codeanalyzer --emit neo4j job; every consumer reads it cheaply.