Interdependencies between software modules are very difficult problems to solve. When your organization grows, these interdependencies multiply, potentially leading to low-cohesion and high-coupling among software modules. This is where Jarviz can help to understand these intricate dependencies down to the field level.
Jarviz is a dependency analysis tool for Java applications. Since non-private methods in Java classes can be accessed by other classes, method calls can be intertwined creating manifold couplings among each other. Jarviz deeply analyzes Java bytecode to reveal these couplings in a user-friendly format. It scans binary artifacts using a custom classloader and generates a complete graph of dependency couplings between methods across multiple artifacts. Jarviz consists of three modules: a Java library to scan dependencies in binary artifacts, a Node.js module to generate a visual graph and a command-line-interface to run it easility in a shell.
ℹ️ | Learn more about Jarviz on the Expedia Group Technology blog |
---|
Jarviz consists of 3 components:
Jarviz Library (jarviz-lib)
This Java library scans the Java bytecode of binary artifacts using a custom classloader and generates the dependency coupling data as a JSON Lines (.jsonl) file. Currently only JAR and WAR artifact formats are supported. To find the dependency couplings, Jarviz analyzes the opcodes using ASM bytecode analysis framework.
Jarviz Graph Tool (jarviz-graph)
Jarviz Graph is a Node application to read the dependency coupling data and generate the dependency graph as an HTML file. The dependency coupling data, provided as a JSON Lines (.jsonl) input file, is generated from the Jarviz Java library.
Jarviz CLI Tool (jarviz-cli)
Jarviz CLI is a command-line tool designed for *nix systems to perform dependency analysis for Java applications. Internally it uses both the Jarviz Java library and Jarviz graph tool to provide a useful command-line interface to the user.
- Prerequisite: Verify that java, maven, node and npm are installed in the system.
- Java and Maven are required to download Java libraries.
- Node and NPM are required to download Node modules.
- Checkout the project from GitHub and change the directory to
jarviz-cli
module. - Run
./jarviz graph -f samples/filter.json -a samples/artifacts.json
- Open the generated HTML file in the browser.
{
"appSetName": "FooPortfolio",
"applicationName": "MyApp",
"artifactFileName": "foo-product-1.2.1.jar",
"artifactId": "foo-product",
"artifactGroup": "com.foo.bar",
"artifactVersion": "1.2.1",
"sourceClass": "foo.bar.MyClass",
"sourceMethod": "doFirstTask",
"targetClass": "foo.bar.YourClass",
"targetMethod": "getProductId"
}↵
...
A coupling is a dependency between two methods. Let's say method hello
of class com.xyz.Alpha
calls the method world
of class com.xyz.Beta
. Then there is a dependency coupling between those two methods and Jarviz represents that coupling this way:
Source Class | Source Method | Target Class | Target Method |
---|---|---|---|
com.xyz.Alpha |
hello |
com.xyz.Beta |
world |
There are many tools capable of generating dependency graphs either down to the class or library level. This is useful information to have when you are supporting an application. It is also very important to know the dependencies down to the field level access. Jarviz provides a way to generate dependency coupling data down to field level (assuming that the internal state of the fields are accessed via method calls). Jarviz will analyze deeply inside the methods, including lambda functions to extract the dependency data.
In the Java source code shown below, the class com.mycompany.MySource
calls methods from two classes namely com.xyz.foo.Alpha
and com.xyz.bar.Beta
.
package com.xyz.foo;
public class Alpha {
private final int val;
public Alpha(final int val) {
this.val = val;
}
public int getVal() {
return val;
}
}
package com.xyz.bar;
public class Beta {
public int getBetaIntVal(final String value) {
return Integer.parseInt(value);
}
public long getBetaLongVal(final String value) {
return Long.parseLong(value);
}
public Alpha getBetaAlphaVal(final String value) {
return new Alpha(Integer.parseInt(value));
}
}
package com.mycompany;
public class MySource {
private static int MY_STATIC_VAR = new Alpha(1).getVal();
private int myInstanceAlphaValue = new Alpha(2).getVal();
public int callBeta1() {
return new Beta().getBetaIntVal("10");
}
public int callBeta2() {
return new Beta().getBetaIntVal("20");
}
public int callAlpha1() {
return new Alpha(10).getVal();
}
public int callAlphaBeta() {
return new Beta().getBetaAlphaVal("30").getVal();
}
public long callLambda(final List<String> values) {
return values.stream()
.filter(a -> !a.isEmpty())
.map(a -> new Beta().getBetaLongVal(a))
.count();
}
}
If com.mycompany.MySource
class is analyzed with Jarviz for the usage of any classes in the com.xyz
package, it will find the following dependency couplings:
Source Class | Source Method | Target Class | Target Method |
---|---|---|---|
com.mycompany.MySource |
<clinit> |
com.xyz.foo.Alpha |
<init> |
com.mycompany.MySource |
<clinit> |
com.xyz.foo.Alpha |
getVal |
com.mycompany.MySource |
<init> |
com.xyz.foo.Alpha |
<init> |
com.mycompany.MySource |
<init> |
com.xyz.foo.Alpha |
getVal |
com.mycompany.MySource |
callBeta1 |
com.xyz.bar.Beta |
<init> |
com.mycompany.MySource |
callBeta1 |
com.xyz.bar.Beta |
getBetaIntVal |
com.mycompany.MySource |
callBeta2 |
com.xyz.bar.Beta |
<init> |
com.mycompany.MySource |
callBeta2 |
com.xyz.bar.Beta |
getBetaIntVal |
com.mycompany.MySource |
callAlpha1 |
com.xyz.foo.Alpha |
<init> |
com.mycompany.MySource |
callAlpha1 |
com.xyz.foo.Alpha |
getVal |
com.mycompany.MySource |
callAlphaBeta |
com.xyz.foo.Alpha |
getVal |
com.mycompany.MySource |
callAlphaBeta |
com.xyz.bar.Beta |
<init> |
com.mycompany.MySource |
callAlphaBeta |
com.xyz.bar.Beta |
getBetaAlphaVal |
com.mycompany.MySource |
lambda$callLambda$1 |
com.xyz.bar.Beta |
<init> |
com.mycompany.MySource |
lambda$callLambda$1 |
com.xyz.bar.Beta |
getBetaLongVal |
See the full sample file: sample_jarviz_result.jsonl
Clone the Jarviz project locally from the GitHub repo, and verify it builds properly.
Jarviz Library
$ mvn clean install
Jarviz Graph Tool
$ npm install
$ npm run build:example
Jarviz CLI Tool
$ ./jarviz
If you are enthusiastic about contributing to Jarviz development, send a pull request. Please follow the guidance in CONTRIBUTING.md.
This project is an open source software by Expedia Group and maintained by ExpediaGroup/teams/jarviz-committers. For support discussions please join Jarviz Google group.
This project is available under the Apache 2.0 License.
Copyright 2019 Expedia, Inc.