๐Ÿ”ท Core

bx-sites-java-integration

Add a bx-sites (ortus-boxlang/bx-sites) site to a Java/Spring Boot project via the Gradle (io.boxlang.bxsites) or Maven (io.boxlang:bxsites-maven-plugin) plugin - build/serve/deploy tasks-and-goals, pinning the BoxLang runtime/bx-sites version, and the three Spring Boot doc generators (springdoc OpenAPI wrapper, in-process Javadoc, Spring MVC controller-scan) plus the shared BoxLang/DocBox generator. Use this whenever a user wants to build/serve/deploy a bx-sites site from Gradle or Maven instead of CommandBox, or wants generated Javadoc/OpenAPI/controller docs inside that site. For the bx-sites CLI's own docbox/coldbox verbs, use bx-sites-api-docs; for the ::: openapi ::: viewer widget, use bx-sites-content-blocks.

$ npx skills add ortus-boxlang/bx-sites-skills/skills/bx-sites-java-integration
$ coldbox ai skills install ortus-boxlang/bx-sites-skills/skills/bx-sites-java-integration
๐Ÿ”— https://skills.boxlang.io/skills/raw/ortus-boxlang/bx-sites-skills/skills~bx-sites-java-integration

BxSites Java/Gradle/Maven Integration

Lets a Java/Spring Boot project add a bx-sites site without CommandBox or a system-wide BoxLang install - either plugin downloads the BoxLang runtime and bx-sites itself into a local cache on first run. Only prerequisite: a JDK 21. Both wrap the same underlying logic, so task/goal coverage and behavior are identical between them - pick whichever matches your build tool.

Status: pre-1.0. Neither plugin is published yet (not on the Gradle Plugin Portal or Maven Central) - see gradle-plugin//maven-plugin/ in the bx-sites repo for source and current build/test instructions. What's documented here is what each plugin does once published; the mechanics are already real and verified.

Neither plugin duplicates bxsites.yaml/.toml/.json - your site's look/theme/nav/everything-else stays controlled by that one config file (see bx-sites-configuration), same as a CommandBox-run project. Both infer the content directory (docs/, falling back to src/) and always write output to <projectRoot>/site/ (not settable - bx-sites itself hardcodes it). Two caveats on that inference:

  • It lives in the plugin/core (ContentDirResolver), not in bx-sites, and ignores bxsites.yaml's source:. bx-sites itself honors source: when it builds, but the plugin's staleness checks and the Spring Boot generators' output pages still target the inferred docs/. Keep content in docs/ (or source: docs) in a plugin-driven project.
  • The src/ fallback is skipped in a Java project, since src/ is real Java source there. The "Java project" marker is the Java plugin applied (Gradle) or an existing src/main/java directory (Maven); either way it defaults to docs/.

Provisioning downloads are checksum-verified (SHA-256/SHA-512) into a shared cache at ~/.bxsites/cache (override with the BXSITES_CACHE_DIR env var), so repeat runs and other projects reuse them.

Gradle

plugins {
    id("io.boxlang.bxsites") version "<version>"
}
./gradlew bxSitesNew    # scaffold docs/ + bxsites.yaml
./gradlew bxSitesBuild   # render docs/**.md -> site/, real up-to-date checking
./gradlew bxSitesServe   # build + serve locally with live reload (foreground)

Other tasks: bxSitesClean, bxSitesSearchIndex, bxSitesLint (wired into check by default), bxSitesDeploy, bxSitesPublish (bxSites Cloud), bxSitesPackage (site.zip), bxSitesStats, bxSitesDoctor. bxSitesBuild never runs as part of assemble unless you opt in.

bxSitesProvision is Gradle-only: it downloads/reuses the BoxLang runtime and bx-sites into boxlangHomeDir, and every verb task depends on it, so you rarely run it directly. (Maven goals provision themselves.)

Passing verb flags - every verb task has an extraArgs list property whose entries go straight to the bx-sites CLI:

tasks.named<ortus.boxlang.bxsites.gradle.tasks.BxSitesDeployTask>("bxSitesDeploy") {
    extraArgs.add("--entry=production")   // deployments/production.yaml
}
bxSites {
    projectRoot.set(layout.projectDirectory)
    boxlangMiniserverVersion.set("1.18.0-snapshot")
    bxSitesVersion.set("1.0.0-snapshot")
    boxlangHomeDir.set(layout.buildDirectory.dir("bxsites/boxlang-home"))
    hookIntoAssemble.set(false)   // opt-in: run bxSitesBuild as part of assemble
    hookIntoCheck.set(true)       // wires bxSitesLint into `check` (default)
}

Maven

<build>
  <plugins>
    <plugin>
      <groupId>io.boxlang</groupId>
      <artifactId>bxsites-maven-plugin</artifactId>
      <version>&lt;version&gt;</version>
    </plugin>
  </plugins>
</build>
mvn bxsites:new
mvn bxsites:build
mvn bxsites:serve

The short bxsites:<goal> form needs the <plugin> block under <build><plugins> specifically (not just <pluginManagement>); otherwise use mvn io.boxlang:bxsites-maven-plugin:build. Other goals: bxsites:clean, bxsites:search-index, bxsites:lint, bxsites:deploy, bxsites:publish, bxsites:package, bxsites:stats, bxsites:doctor, bxsites:docbox. No goal is bound to any lifecycle phase by default - bind your own in <executions> if you want one to run automatically (e.g. bxsites:build to pre-site).

Build staleness checking (Maven has no Gradle-style incremental engine): bxsites:build compares the newest last-modified timestamp under the content dir/config file against the newest one already in site/, and skips the subprocess entirely when nothing is newer. Force it with mvn bxsites:build -Dbxsites.build.forceRebuild=true.

<plugin>
  <groupId>io.boxlang</groupId>
  <artifactId>bxsites-maven-plugin</artifactId>
  <configuration>
    <projectRoot>${project.basedir}</projectRoot>
    <boxlangMiniserverVersion>1.18.0-snapshot</boxlangMiniserverVersion>
    <bxSitesVersion>1.0.0-snapshot</bxSitesVersion>
    <boxlangHomeDir>${project.build.directory}/bxsites/boxlang-home</boxlangHomeDir>
  </configuration>
</plugin>

Passing verb flags - every verb goal (build, deploy, publish, ...) accepts extraArgs (a list), from the command line or the POM:

mvn bxsites:deploy -Dbxsites.extraArgs=--entry=production
<configuration>
  <extraArgs><arg>--entry=production</arg></extraArgs>
</configuration>

Spring Boot doc generators

Three generators, none wired into any lifecycle by default - opt in and, for the ones that depend on another plugin's output, bind them after it runs (e.g. tasks.named("bxSitesBuild") { dependsOn("bxSitesOpenApiDoc") } on Gradle).

OpenAPI (springdoc wrapper)

Wires an already-generated springdoc OpenAPI/Swagger spec into the site's native ::: openapi ::: widget (see bx-sites-content-blocks) - no OpenAPI parsing happens in the plugin itself, it only copies the spec file into assets/openapi/ and writes a thin wrapper page. Needs your own springdoc plugin already producing that spec file, and bxsites.yaml's openapi: true already set (fails with an actionable error otherwise, unless autoPatchConfig is on - YAML/TOML only, never JSON).

bxSites {
    springBoot {
        openApi {
            enabled.set(true)
            specFile.set(layout.buildDirectory.file("openapi/openapi.json"))
            pageTitle.set("Bookshelf API")     // default: "API Reference"
            pagePath.set("api/openapi.md")     // default
            autoPatchConfig.set(false)
        }
    }
}
mvn bxsites:openapi -Dbxsites.openapi.specFile=target/openapi/openapi.json

Gradle's specFile defaults to build/openapi/openapi.json; Maven's bxsites.openapi.specFile is required.

Known limitation: Swagger UI renders entirely client-side, so per-endpoint text never reaches the search index - only the wrapper page's own title/frontmatter is indexed.

Javadoc (in-process, JDK doclet SPI)

One Markdown page per public top-level Java type, using the JDK's own Javadoc doclet SPI in-process (no javadoc subprocess). Each page has a search box plus per-kind chip filters over its constructors, methods and fields.

Gradle has no default for sourceFiles - it must be set (as below) or there is nothing to document. Maven defaults to the project's compile source roots.

bxSites {
    springBoot {
        javadoc {
            enabled.set(true)
            sourceFiles.from(sourceSets.getByName("main").allJava)
            pagePathPrefix.set("api/javadoc")   // default
            tags.set(listOf("api", "javadoc"))  // default
        }
    }
}
mvn bxsites:javadoc

Deliberately scoped down for v1 - not a complete Javadoc-to-Markdown converter. Covers: public/protected constructors, methods and fields (a "Fields" section) of public top-level types (nested and package-private types and non-public members are skipped), each member's own (not inherited) doc comment, and @param/@return/@throws/@deprecated. Doesn't yet: convert inline HTML in doc comments (best-effort text extraction only); resolve {@link}/{@see} as real hyperlinks (renders as inline code); generate an index/nav page (wiring pages into nav is yours). Runs against source directly, so a record's compiler-generated accessors/toString/equals/hashCode show up too, matching the standard javadoc tool.

Controller scan (Spring MVC fallback)

Reflection-scans compiled classes for @Controller/@RestController classes and emits one page per controller listing its mapped endpoints - the fallback for a project without OpenAPI generation on. Runs in a forked JVM against the project's own runtime classpath (Spring included), never in the plugin's own JVM.

bxSites {
    springBoot {
        controllerScan {
            // enabled defaults true unless springBoot.openApi.enabled is true
            // classesDir and runtimeClasspath have NO default in Gradle - set both
            classesDir.set(layout.buildDirectory.dir("classes/java/main"))
            runtimeClasspath.from(configurations.getByName("runtimeClasspath"))
            pagePathPrefix.set("api/controllers")     // default
            tags.set(listOf("api", "controllers"))    // default
        }
    }
}
mvn bxsites:controller-scan

Maven's goal defaults classesDir to ${project.build.outputDirectory} and resolves the runtime classpath itself (requiresDependencyResolution=RUNTIME)

  • bind it after compile (e.g. to process-classes) if wiring it into your own build. Parameters: -Dbxsites.controllerScan.classesDir, .pagePathPrefix, .tags. Deliberately scoped down: only directly-annotated @Controller/@RestController classes are recognized (a custom stereotype annotation built on one isn't); only directly-annotated @RequestMapping/@GetMapping/@PostMapping/@PutMapping/ @DeleteMapping/@PatchMapping methods; nested classes skipped; no per-endpoint description text (reflection has no access to source-level doc comments).

BoxLang doc generation (DocBox)

bxSitesDocBoxDoc/bxsites:docbox generate a BoxLang/CFML API reference for a JVM project whose sources include .bx/.cfc classes - a thin wrapper over the bx-sites docbox verb (same implementation both build tools drive, so it can't drift between them). Requires bx-docbox installed in the provisioned BoxLang runtime. Only options you actually set are passed through; anything left out falls through to bxsites.yaml - the config file stays the single source of truth.

bxSites {
    boxlang {
        docbox {
            enabled.set(true)
            mappings.put("models", "models")
            projectTitle.set("Bookshelf API")   // default: site name + " API"
            excludes.set("tests|build")
            pagePathPrefix.set("api/docbox")    // default
            tags.set(listOf("api", "docbox"))   // default
            jsonDir.set("build/docbox-json")    // optional: keep DocBox's JSON output
        }
    }
}
<configuration>
  <mappings><models>models</models></mappings>
  <projectTitle>Bookshelf API</projectTitle>
  <excludes>tests|build</excludes>
  <pagePathPrefix>api/docbox</pagePathPrefix>
  <tags><tag>api</tag><tag>docbox</tag></tags>
  <jsonDir>target/docbox-json</jsonDir>  <!-- optional: keep DocBox's JSON output -->
</configuration>

(-Dbxsites.docbox.projectTitle, .excludes, .pagePathPrefix, .tags and .jsonDir also work; mappings is POM-only.) The // default values above are the bxsites.yaml defaults, not plugin conventions: the plugin passes only what you set.

See bx-sites-api-docs for what the generated pages look like. There is deliberately no ColdBox task/goal - a ColdBox app is built and run through CommandBox, never Gradle/Maven, so bxSites coldbox (see bx-sites-api-docs) stays a CLI-only concern.

Detecting failure

Each task/goal fails the build when the bx-sites subprocess misbehaves. A run counts as successful only if all hold: the exit code is 0, no output line starts with Error: (the CLI's error prefix), and - for build only - site/index.html exists and is non-empty. The subprocess has a 30-minute timeout. When a build fails, read the captured output in the Gradle/Maven failure message first.

What's not built yet

bxSitesServe/bxsites:serve's live output streaming currently buffers output with a 30-minute timeout - both wrong for a task meant to run indefinitely.