Skip to content

Latest commit

 

History

History
253 lines (177 loc) · 10 KB

File metadata and controls

253 lines (177 loc) · 10 KB

Developing

Tip

Contributor documentation lives in docs/, including Architecture Decision Records under docs/adr/.

Prerequisites

  • 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.

Core Technologies

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

Architecture Constraints

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.

REST API v1 is in maintenance mode

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.

Persistence: prefer JDBI and raw SQL

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.

Throughput over latency

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.

Strong consistency by default

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.

Simple and pragmatic over speculative future-proofing

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.

Strong cohesion, loose coupling

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.

Long-running work must be interruptible

See docs/INTERRUPTIBILITY.md.

Nullability is declared with JSpecify and enforced by NullAway

See docs/NULLABILITY.md.

Building

Build the project:

make build

Tip

(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-image

This produces the image ghcr.io/dependencytrack/apiserver:local.

Code Style

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-java

Most findings can be fixed automatically using:

make format-java

Run 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

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.

Testing

Run all tests:

make test

Run a single test class:

make test-single MODULE=apiserver TEST=FooTest

Run 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-e2e

Dev Mode

Dev 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-dev

The 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.

Container Reuse

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-containers

DataNucleus Bytecode Enhancement

Classes 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-enhance

Then re-run the test. Ensure your IDE is not cleaning the target directory before execution.

Database Migrations

See docs/DATABASE_MIGRATIONS.md.

Build Cache

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