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).
Symbol table only (fast, no build)
Section titled “Symbol table only (fast, no build)”Parse a project and emit the symbol table. No project build required:
java -jar codeanalyzer-2.3.7.jar \ -i /path/to/commons-cli \ -a 1 \ -o ./output# -> ./output/analysis.json with symbol_tableFull analysis with call graph
Section titled “Full analysis with call graph”Symbol table plus the WALA call graph. The project is built automatically:
java -jar codeanalyzer-2.3.7.jar \ -i /path/to/commons-cli \ -a 2 \ -o ./output \ -v# -> ./output/analysis.json with symbol_table + call_graphSingle source string (no project, no build)
Section titled “Single source string (no project, no build)”Analyze a snippet directly; output goes to stdout:
java -jar codeanalyzer-2.3.7.jar \ -s "public class HelloWorld { public static void main(String[] args) {} }" \ -a 1Pre-built project (skip the build)
Section titled “Pre-built project (skip the build)”If the project is already compiled, skip the build step for a faster level-2 run:
java -jar codeanalyzer-2.3.7.jar \ -i /path/to/project \ -a 2 \ --no-build \ -o ./outputCustom build command
Section titled “Custom build command”Use your own build instead of the auto build:
java -jar codeanalyzer-2.3.7.jar \ -i /path/to/project \ -a 2 \ -b "mvn -q clean package -DskipTests" \ -o ./outputIncremental analysis (target files)
Section titled “Incremental analysis (target files)”Re-analyze just two files and patch them into an existing analysis.json:
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 ./outputThe named files are tagged is_modified: true in the merged output. See Incremental analysis.
Multi-module project
Section titled “Multi-module project”Analyze a submodule while building from the reactor root:
java -jar codeanalyzer-2.3.7.jar \ -i /path/to/project/web-module \ -f /path/to/project/pom.xml \ -a 2 \ -o ./outputPipe stdout into a tool
Section titled “Pipe stdout into a tool”Omit -o to stream JSON to stdout and process it inline:
java -jar codeanalyzer-2.3.7.jar -i /path/to/project -a 1 \ | jq '.symbol_table | keys | length'# prints the number of analyzed source filesEmit a Neo4j property graph (snapshot)
Section titled “Emit a Neo4j property graph (snapshot)”--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:
java -jar codeanalyzer-2.3.7.jar \ -i /path/to/daytrader8 \ -a 2 \ --emit neo4j \ --app-name daytrader8 \ -o ./out# -> ./out/graph.cypherThe 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:
cypher-shell -u neo4j -p "$NEO4J_PASSWORD" < ./out/graph.cypherPush a live graph over Bolt (incremental)
Section titled “Push a live graph over Bolt (incremental)”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:
export NEO4J_URI=bolt://localhost:7687export NEO4J_USERNAME=neo4jexport 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:7687The 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:
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.
Targeted incremental re-push
Section titled “Targeted incremental re-push”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:
export NEO4J_URI=bolt://localhost:7687export 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 prunedEmit the schema contract
Section titled “Emit the schema contract”--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:
java -jar codeanalyzer-2.3.7.jar --emit schema -o ./out# -> ./out/schema.neo4j.jsonOr print it to stdout when -o is omitted — handy for diffing against a checked-in copy in CI:
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.
From Python via CLDK (local analysis)
Section titled “From Python via CLDK (local analysis)”If you’d rather not manage the JAR yourself, the CLDK SDK invokes it for you:
from cldk import CLDKfrom 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.DiGraphSee 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 backendfrom cldk import CLDKfrom cldk.analysis import AnalysisLevelfrom 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.DiGraphklass = 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.