Tip
Contributor documentation lives in docs/, including Architecture Decision
Records under docs/adr/.
- JDK 25+ (Temurin distribution recommended)
- Maven 3.9+
- Docker or Podman (required for tests and dev mode)
- Buf CLI for linting and formatting Protobuf
- A Java IDE (IntelliJ recommended)
Tip
We recommend sdkman for managing JDK and Maven installations,
and mvnd for faster builds.
The Makefile automatically uses mvnd when available, falling back to mvn.
Note
This guide uses make commands for brevity,
and we recommend that you use make if you prefer CLI-centric workflows.
If using make is not an option, you can inspect the full commands in Makefile
and use them for your own custom workflows.
For IDE-centric workflows, we provide equivalent IntelliJ run configurations.
| Technology | Purpose |
|---|---|
| Jakarta REST (JAX-RS) | REST API specification |
| Jersey | JAX-RS implementation |
| OpenAPI | API specification |
| JDO | Persistence specification |
| DataNucleus | JDO implementation |
| JDBI | Database access |
| Flyway | Database migrations |
| MicroProfile Config | Configuration |
| Jetty | Servlet container |
| PostgreSQL | Database |
| Testcontainers | Integration testing |
| Protocol Buffers | Serialization |
The following constraints apply project-wide. They exist to keep the codebase coherent
as it evolves and to avoid steering changes in directions we are actively moving away from.
For substantial changes, see also the Architecture Decision Record
process in CONTRIBUTING.md.
New endpoints must be added to API v2, which lives in the api module and follows
a spec-first OpenAPI workflow. API v1 (in apiserver/src/main/java/org/dependencytrack/resources/v1)
is code-first and uses Swagger annotations on JAX-RS resources. Touch v1 only when extending
or fixing existing endpoints.
API v1 also reuses persistence models as REST DTOs. Do not propagate that pattern into v2. New endpoints must keep the API contract decoupled from the persistence layer.
JDO and DataNucleus are being phased out. New persistence code should use JDBI with raw SQL. Avoid touching JDO entities unless the change genuinely requires it, and do not build new features on top of the JDO layer.
See docs/PERSISTENCE.md.
The system processes large volumes of components, vulnerabilities, and analyses. Optimize for throughput. Batch work, minimize network round trips, and avoid per-record hot paths that issue one query, request, or message at a time.
Default to strong consistency. Eventual consistency is acceptable only when the use case explicitly demands it (typically for scale or availability reasons) and the trade-off is documented.
Solve the problem in front of you. Avoid extra abstractions, configuration knobs, or extension points introduced for hypothetical future needs. It is cheaper to add an abstraction when a second concrete use case appears than to maintain one that has none.
Modules should be small and focused, with narrow, intentional interfaces between them. Reach across module boundaries through well-defined APIs rather than by importing internals. The ongoing modularization effort moves the codebase in this direction.
See docs/NULLABILITY.md.
Build the project:
make buildTip
(Re-) building the entire project via make build is cheap due to build caching.
You generally don't need to build modules selectively.
The resulting JAR is placed in ./apiserver/target as dependency-track-apiserver.jar.
It ships with an embedded Jetty server, there's no need to deploy it in an application
server like Tomcat or WildFly.
Build a container image:
make build-imageThis produces the image ghcr.io/dependencytrack/apiserver:local.
Java sources are formatted with palantir-java-format
and checked with Spotless, which also enforces the license header,
import order, and removal of unused imports. POMs are formatted with SortPom.
Both run in Maven's validate phase, so make lint-java and CI cover them:
make lint-javaMost findings can be fixed automatically using:
make format-javaRun this before committing. IntelliJ's built-in formatter does not match palantir-java-format,
so reformatting from the IDE alone will fail the check. Install the
palantir-java-format plugin,
which IntelliJ offers on first open. Enable it per project under
Settings > palantir-java-format Settings. .idea/codeStyles sets the import order.
make format-java is the source of truth.
Static analysis happens for every build using Error Prone.
This covers make build, make test, and builds from the IDE. It is not part of make lint-java.
We exclusively use ERROR-level checks, so findings fail compilation. Test sources are not checked.
Fix findings instead of suppressing them. For false positives, suppress the check on the smallest possible scope and consider including a comment as to why the check is wrong if the reason is not obvious.
Error Prone's bug patterns documentation includes details and suppression guidance.
Run all tests:
make testRun a single test class:
make test-single MODULE=apiserver TEST=FooTestRun multiple test classes:
make test-single MODULE=apiserver TEST="FooTest,BarTest"Run a single test method:
make test-single MODULE=apiserver TEST="FooTest#testFoo"Run e2e tests:
make test-e2eDev mode launches the API server with auto-provisioned containers for PostgreSQL and the frontend. Containers are created on startup and, unless reuse is enabled (see Container Reuse), disposed of on shutdown.
make apiserver-devThe API server will be available at http://localhost:8080.
Frontend and PostgreSQL ports are logged during startup.
Dev mode specific configuration can be made in application-dev.properties.
Dev mode is configured to reuse its containers across restarts, so PostgreSQL
state (schema and data) is preserved and startup is faster. Reuse only takes
effect once it has been opted into globally, by setting either testcontainers.reuse.enable=true
in ~/.testcontainers.properties, or the TESTCONTAINERS_REUSE_ENABLE=true environment variable.
Without it, containers are disposed on shutdown as usual.
See the Testcontainers reuse docs.
To remove reused (or otherwise stale) dev services containers, e.g. to start from a clean slate, run:
make apiserver-dev-remove-containersClasses annotated with @PersistenceCapable must be
enhanced
post-compilation. Maven handles this automatically, but IDEs run their own builds
and may skip the enhancement step.
If you see NucleusUserException: Found Meta-Data for class ... but this class is either not enhanced
when running tests from your IDE, run:
make datanucleus-enhanceThen re-run the test. Ensure your IDE is not cleaning the target directory before execution.
See docs/DATABASE_MIGRATIONS.md.
We use Maven build caching to speed up builds. If you encounter stale or unexplainable build issues, try clearing the cache and see if it resolves your issues:
make clean-build-cache