AGENTS.md - AI Agent Instructions for Apache Jackrabbit Oak

Project Overview

Apache Jackrabbit Oak is a scalable, high-performance hierarchical content repository implementing the JCR (Java Content Repository) specification. It is a multi-module Maven project with ~47 modules written in Java 11.

General Guidelines

  • When working on a specific area, first read the area-specific documentation before making changes (see doc links in the module tables below)
  • New code must have >80% test coverage. Security modules (oak-security-spi, oak-auth-*, oak-authorization-*) require 100% test coverage
  • Do not weaken or remove existing test assertions to make the build pass. If a test fails after a change, investigate the root cause. Only modify existing tests when the behavioral change is intentional and explicitly requested
  • Write self-descriptive, easy-to-read code. Prefer minimal changes — AI tends to add more lines than necessary; aim to be concise
  • Avoid trivial comments. Only add comments where the logic is complex and not obvious from the code itself
  • Use feature toggles for non-trivial changes. If the change is a bug fix, the toggle should be enabled by default. If the change introduces a new feature, the toggle should be disabled by default

Build Commands

# Full build (skip tests for speed)
mvn clean install -DskipTests

# Fast build (no tests, no coverage)
mvn clean install -Pfast

# Build a single module (use -DskipTests only for modules you did not change)
mvn clean install -pl oak-core -DskipTests

# Build a module and its dependencies (use -DskipTests only for unchanged dependencies)
mvn clean install -pl oak-core -am -DskipTests

# Rebuild a module and all modules that depend on it (use after SPI/API changes)
mvn clean install -pl oak-store-spi -amd -DskipTests

# Full build with tests
mvn clean install

# Build with integration tests
mvn clean install -PintegrationTesting

Test Commands

# Run tests for a single module
mvn test -pl oak-core

# Run a specific test class
mvn test -pl oak-core -Dtest=TreeTest

# Run a specific test method
mvn test -pl oak-core -Dtest=TreeTest#testMethodName

# Run integration tests for a module
mvn verify -pl oak-store-document -PintegrationTesting

# Run with code coverage
mvn verify -pl oak-core -Pcoverage -Dskip.coverage=false

Always run tests for the modules you modified. Use -DskipTests only when building dependencies that you did not change.

Test framework: JUnit 4 (4.13.1) with Mockito 5.x (loaded as Java agent). Some modules also use EasyMock. Do not introduce JUnit 5 unless explicitly requested.

Test fixtures: Tests may run against multiple backend fixtures: SEGMENT_TAR (default), DOCUMENT_NS (MongoDB), DOCUMENT_RDB, SEGMENT_AWS, SEGMENT_AZURE.

Repository Structure

Note: Individual submodules may have their own AGENTS.md file with module-specific instructions. When working within a particular module, check for and read its AGENTS.md before making changes.

All modules grouped by area. The Description column provides additional context for understanding each module's role.

API & Core

ModulePurposeDescription
oak-apiPublic Oak APIDefines ContentRepository, ContentSession, Root, Tree and other core interfaces for accessing repository content. See: oak-api/README.md, oak-doc/src/site/markdown/oak_api/error_codes.md
oak-coreCore repository implementationMVCC-based transactional content model, node state management, commit handling, and internal kernel. See: oak-doc/src/site/markdown/archigreat l-model.md
oak-core-spiCore SPI interfacesService Provider Interfaces for plugins and storage backends (NodeStore, Commit, etc.)
oak-commonsShared utilitiesCommon utility classes used across all Oak modules
oak-jackrabbit-apiJackrabbit API extensionsAdditional API beyond the JCR spec (e.g., extended session, security). See: oak-jackrabbit-api/README.md
oak-jcrJCR API bindingImplementation of the javax.jcr (JCR 2.0) specification on top of Oak's content model
oak-parentParent POMDependency management and shared plugin settings

Node Store (Persistence)

ModulePurposeDescription
oak-store-spiStorage SPIInterfaces and base classes for implementing custom NodeStore backends. See: oak-doc/src/site/markdown/nodestore/overview.md
oak-store-compositeComposite NodeStoreCombines multiple NodeStores, allowing different subtrees to be backed by different stores. See: oak-doc/src/site/markdown/nodestore/compositens.md
oak-store-documentDocument NodeStoreStores content as documents in MongoDB or relational databases (RDB). Supports clustering, revision management, and branch tracking. See: oak-doc/src/site/markdown/nodestore/documentmk.md, oak-doc/src/site/markdown/nodestore/document/mongo-document-store.md, oak-doc/src/site/markdown/nodestore/document/rdb-document-store.md
oak-segment-tarSegment/TarMK storageImmutable segment-based storage using TAR files. Includes online/offline compaction and generational garbage collection. Default for single-instance deployments. See: oak-doc/src/site/markdown/nodestore/segment/overview.md, oak-doc/src/site/markdown/nodestore/segmentmk.md, oak-segment-tar/release-howto.md
oak-segment-remoteRemote segment baseShared base for cloud segment backends (AWS, Azure)
oak-segment-awsSegment on AWS S3Remote segment store backend storing segments in Amazon S3
oak-segment-azureSegment on Azure BlobRemote segment store backend storing segments in Microsoft Azure Blob Storage. Includes Azurite test support

Blob / Binary Storage

ModulePurposeDescription
oak-blobBlob SPI & baseCore interfaces and base classes for BlobStore (binary large object) implementations. See: oak-blob/README.md, oak-doc/src/site/markdown/plugins/blobstore.md
oak-blob-cloudCloud blob baseCommon functionality for cloud-based BlobStore providers
oak-blob-cloud-azureAzure BlobStoreAzureDataStore implementation storing binaries in Azure Blob Storage with direct binary access. See: oak-doc/src/site/markdown/features/direct-binary-access.md
oak-blob-pluginsBlob pluginsAdditional BlobStore implementations and cloud integration plugins (S3, etc.)

Query & Indexing

ModulePurposeDescription
oak-query-spiQuery index SPIInterfaces for implementing custom query index providers. See: oak-doc/src/site/markdown/query/query-engine.md
oak-searchSearch frameworkHigher-level search abstraction and query optimization shared by Lucene and Elastic backends. See: oak-doc/src/site/markdown/query/query.md, oak-doc/src/site/markdown/query/indexing.md, oak-doc/src/site/markdown/query/index-management.md
oak-luceneLucene indexingEmbedded Apache Lucene full-text and property indexing with facets, suggestions, spellcheck, and excerpts. See: oak-doc/src/site/markdown/query/lucene.md, oak-doc/src/site/markdown/query/property-index.md, oak-doc/src/site/markdown/query/hybrid-index.md
oak-search-elasticElasticsearch indexingDistributed full-text search and indexing via external Elasticsearch cluster. See: oak-doc/src/site/markdown/query/elastic.md

Security

ModulePurposeDescription
oak-security-spiSecurity SPIInterfaces for authentication and authorization provider plugins. See: oak-doc/src/site/markdown/security/overview.md, oak-doc/src/site/markdown/security/introduction.md
oak-auth-externalExternal authenticationFramework for external identity providers (SAML, OAuth, etc.) with user/group synchronization. See: oak-auth-external/README.md, oak-doc/src/site/markdown/security/authentication/external/
oak-auth-ldapLDAP authenticationLDAP/Active Directory identity provider with user and group sync. See: oak-auth-ldap/README.md, oak-doc/src/site/markdown/security/authentication/ldap.md
oak-authorization-cugClosed User GroupsCUG-based authorization restricting read access to defined groups on subtrees. See: oak-doc/src/site/markdown/security/authorization/cug.md
oak-authorization-principalbasedPrincipal-based authzAccess control evaluated by principal rather than by content path. See: oak-doc/src/site/markdown/security/authorization/principalbased.md

Tools & CLI

ModulePurposeDescription
oak-runCLI toolboxRunnable JAR with commands for backup, restore, compaction, debug console, index management, migration, and more. See: oak-run/README.md, oak-doc/src/site/markdown/command_line.md, oak-doc/src/site/markdown/features/oak-run-nodestore-connection-options.md
oak-run-commonsCLI shared codeBase classes shared by oak-run commands
oak-run-elasticCLI Elastic toolsElasticsearch-specific oak-run commands
oak-upgradeMigration toolsUpgrades Jackrabbit 2.x repositories to Oak, including node store migration between backends. See: oak-doc/src/site/markdown/migration.md
oak-httpHTTP bindingREST/HTTP server exposing Oak repository operations remotely. See: oak-http/README.md

Other Modules

  • Benchmarks: oak-benchmarks, oak-benchmarks-lucene, oak-benchmarks-elastic — performance benchmarks. See: oak-benchmarks/README.md
  • Integration tests: oak-it, oak-it-osgi — cross-module and OSGi integration tests
  • Examples / training: oak-exercise, oak-examples, oak-pojosr — learning resources and OSGi test registry
  • Documentation: oak-doc (build with -Pdoc) — site source at oak-doc/src/site/markdown/

Feature Toggles

Oak has a built-in feature toggle mechanism for safely rolling out new behavior at runtime. Toggles are disabled by default and can be enabled without redeployment.

Core API (in oak-core-spi):

  • Feature — wrapper for checking toggle state. Create with Feature.newFeature(name, whiteboard), check with feature.isEnabled(), clean up with feature.close(). See: oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/Feature.java
  • FeatureToggle — the underlying toggle registered on the OSGi Whiteboard. State is managed via setEnabled(boolean) using an AtomicBoolean (thread-safe). See: oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/FeatureToggle.java

Naming convention: toggle names follow the pattern FT_OAK-<issue> or FT_<DESCRIPTION>_OAK-<issue> (e.g., FT_OAK-11949, FT_CLASSIC_MOVE_OAK-10147).

How it works:

  1. A component creates a toggle: Feature ft = Feature.newFeature("FT_OAK-XXXXX", whiteboard)
  2. The toggle is registered on the Whiteboard for runtime discovery
  3. Code checks the toggle: if (feature != null && feature.isEnabled()) { ... }
  4. Admin tooling can discover and flip toggles via the Whiteboard at runtime

Examples in the codebase:

  • Query engine toggles (FT_OAK-11949, FT_OAK-12007) registered in oak-core/.../Oak.java
  • Document store toggles (throttling, merge lock) in oak-store-document/.../DocumentNodeStoreBuilder.java (embedded verification and full GC are controlled only via OSGi embeddedVerificationEnabled and fullGCEnabled, not runtime toggles)
  • Some features also support a system property fallback (e.g., oak.classicMove)

Code Conventions

Style

  • No wildcard imports (import each class individually)
  • Follow existing code style in the module you are modifying
  • OSGi bundle compliance is required - modules produce OSGi bundles
  • Never use regular expression parsing and find / replace for JSON or XML data

License Header

Every Java file must start with the Apache 2.0 license header. The build enforces this via the Apache RAT plugin. Use this exact header:

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

Annotations

  • Use @NotNull / @Nullable from org.jetbrains.annotations for API contracts
  • Use OSGi annotations (@Component, @Activate, etc.) for service components

OSGi exports and baseline

  • A package is exported (and baseline-checked by maven-bundle-plugin, failOnError: true in oak-parent) when it appears in the bundle manifest Export-Package — typically via package-info.java (@Version from org.osgi.annotation.versioning) in SPI/API modules, or via an explicit <Export-Package> list in the module pom.xml (e.g. oak-core).
  • Packages absent from Export-Package — including typical *.impl.* implementation packages in SPI modules — are bundle-internal even when classes are public.
  • Baseline applies when you change an exported package contract, not every public class in a bundle module.
  • To verify exports: inspect Export-Package in the module manifest or target/baseline.xml after mvn process-classes bundle:manifest -pl <module>.

Git Workflow

  • Main branch: trunk (not master/main). Never commit directly to trunk
  • Issue tracker: Apache Jira, project key OAK (e.g., OAK-12345)
  • Branch naming: issue/OAK-<issue_number> (e.g., issue/OAK-12345). If no Jira issue is specified, ask the user what the branch name should be
  • Commit message format: Start with Jira issue key: OAK-XXXXX: Description of change
  • PR target: PRs should target trunk
  • All changes must be committed to the issue branch, never directly to trunk
  • Code review: After pushing changes, remind the user to request a review from committers who have previously contributed to the affected modules

CI

  • GitHub Actions: Runs on PRs and pushes to trunk. Builds with Java 11, runs full test suite with SEGMENT_TAR and DOCUMENT_NS fixtures.
  • Jenkins: Parallel module testing pipeline.
  • SonarCloud: Code quality analysis runs after build.
  • Commit checks: PR commit messages and branch names are validated automatically.

Common Pitfalls

  • Always build oak-parent first if dependencies are missing: mvn install -pl oak-parent -DskipTests
  • MongoDB must be running locally (port 27017) for DOCUMENT_NS fixture tests
  • Some modules have long-running integration tests; use -DskipTests or -Pfast for quick iteration
  • OSGi baseline checks may fail if you change an exported public API (see OSGi exports and baseline above) — this is intentional to prevent accidental breaking changes