This is a Grails Plugin that adds export functionality supporting different formats e.g. CSV, Excel (xls, xlsx), Open Document Spreadsheet, PDF and XML and can be extended to add additional formats.
- Language: Groovy 4.0.30 on Java 17
- Framework: Grails 7.x
- Build System: Gradle 8.14.4 (with wrapper)
- Current Version: 7.0.x-SNAPSHOT
- License: Apache 2.0
Detailed best practices are documented in .skills/:
| Skill File | Purpose |
|---|---|
.skills/repository-structure.md |
Canonical directory layout and architectural rules |
.skills/gradle-best-practices.md |
Gradle best practices, convention plugins, and idioms |
.skills/plugin-project.md |
Plugin project scope: source code + unit tests only |
.skills/example-apps.md |
Example app patterns: integration & functional tests |
Read these skill files before making structural changes to the repository.
- NEVER add code to the root
build.gradleto configure subprojects. Nosubprojects {},allprojects {}, orconfigure()blocks. All shared configuration goes through convention plugins inbuild-logic/. - The plugin project contains ONLY plugin code and unit tests. No integration tests, no functional tests, no example controllers or views.
- Example apps under
examples/host all integration and functional tests. They depend on the plugin viaimplementation project(':grails-export')and test it as a real consumer would. - Use Gradle convention plugins to deduplicate. If two or more subprojects share build logic, extract it into a
convention plugin in
build-logic/. - Always use lazy Gradle APIs to avoid eager initialization (
tasks.register(),tasks.named(),configureEach,provider {}).
export/
├── .skills/ # Best practice skill files
├── plugin/ # Core Grails plugin (artifact: grails-export)
│ ├── grails-app/ # Plugin services, taglibs, and conf
│ └── src/main/ # Plugin source code (exporters, builder, etc.)
├── examples/app1/ # Example Grails app
│ └── grails-app/ # Controllers and conf for integration testing
├── docs/ # Asciidoctor documentation
├── build-logic/ # Gradle convention plugins (composite build)
├── .github/workflows/ # CI, release, and release-notes workflows
├── build.gradle # Root build file (docs + root-publish ONLY)
├── settings.gradle # Multi-project settings
└── gradle.properties # Version properties
# Full build (compile + test)
./gradlew build
# Run only unit tests (plugin module)
./gradlew :grails-export:test
# Run integration tests (example app)
./gradlew :app1:integrationTest
# Skip tests
./gradlew build -PskipTests
# Run the example app
./gradlew :app1:bootRun
# Generate documentation
./gradlew docs
# Clean build
./gradlew clean build
# Run code style checks only
./gradlew codeStyle
# Skip code style checks
./gradlew build -PskipCodeStyleUse SDKMAN to install the correct tool versions (see .sdkmanrc):
- Java:
17.0.18-librca - Gradle:
8.14.4 - Groovy:
4.0.30
Run sdk env install to set up the environment.
The plugin provides a service-oriented export mechanism with a tag library for UI integration:
ExportGrailsPluginregisters theexporterFactorySpring bean on startup, and loads any custom exporter classes declared inapplication.groovyunder theexporterskey.ExportServiceis the main entry point for application code. It accepts a format type, output stream (or HTTP response), data list, field list, label map, formatter map, and parameter map. It delegates toexporterFactoryto obtain the correctExporterand callsexport().ExporterFactory/DefaultExporterFactoryresolves anExporterimplementation by format string (e.g."csv","excel","pdf") and configures it with the supplied fields, labels, formatters, and parameters.Exporterinterface /AbstractExporter— Base contract and shared logic for all format exporters.ExportTagLibexposes<export:formats />(renders an export button bar) and<export:resource />(renders the CSS<link>) for use in GSP views.
| Class / Interface | Location | Purpose |
|---|---|---|
ExportGrailsPlugin |
plugin/src/main/groovy/grails/plugins/export/ |
Plugin descriptor; registers beans and custom exporters |
ExportService |
plugin/grails-app/services/grails/plugins/export/ |
Main service; sets response headers, delegates to exporter |
ExportTagLib |
plugin/grails-app/taglib/grails/plugins/export/ |
Tag library: export:formats, export:resource |
ExporterFactory / DefaultExporterFactory |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Creates Exporter instances by type string |
Exporter / AbstractExporter |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Exporter interface and shared base class |
DefaultCSVExporter |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Exports data as CSV |
DefaultExcelExporter |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Exports data as Excel (xls / xlsx) via ExcelBuilder |
DefaultODSExporter |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Exports data as ODS (Open Document Spreadsheet) |
DefaultPDFExporter |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Exports data as PDF |
DefaultRTFExporter |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Exports data as RTF |
DefaultXMLExporter |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Exports data as XML |
ExcelBuilder |
plugin/src/main/groovy/grails/plugins/export/builder/ |
Helper for constructing Excel workbooks |
ExporterUtil |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Utility methods for exporters |
RenderUtils |
plugin/src/main/groovy/grails/plugins/export/taglib/util/ |
Utility methods for tag library resource path resolution |
ExportingException / ExporterNotFoundException |
plugin/src/main/groovy/grails/plugins/export/exporter/ |
Export error types |
Custom exporters can be registered in application.groovy (or equivalent):
exporters {
myformat = "com.example.export.MyCustomExporter"
}An existing exporter class can be overridden by setting export.<type> to a replacement class name.
MIME types for the built-in formats must be declared in grails.mime.types (application config):
| Format key | Typical MIME type |
|---|---|
csv |
text/csv |
excel |
application/vnd.ms-excel |
ods |
application/vnd.oasis.opendocument.spreadsheet |
pdf |
application/pdf |
rtf |
application/rtf |
xml |
text/xml |
Unit tests use the Spock Framework and run on JUnit Platform.
The TestController in the example app exercises ExportService with real format requests. Integration and
functional tests added here depend on the plugin as a real consumer would.
Convention plugins in build-logic/src/main/groovy/ standardize build configuration:
| Plugin | Purpose |
|---|---|
app-run.gradle |
Debug flags for bootRun |
compile.gradle |
Java/Groovy compilation settings (UTF-8, incremental, Java release from .sdkmanrc) |
docs.gradle |
Documentation aggregation (Groovydoc + Asciidoctor) |
example-app.gradle |
Example app config (grails-web, GSP, assets) |
grails-assets.gradle |
Asset pipeline with Bootstrap/jQuery WebJars |
grails-plugin.gradle |
Grails plugin application |
publish.gradle |
Per-project Maven publishing metadata |
publish-root.gradle |
Root-level Nexus publishing workaround |
testing.gradle |
Test framework config (Spock, JUnit Platform, test-logger) |
- CI (
.github/workflows/ci.yml): Builds and tests on push/PR; publishes snapshots to Maven Central Snapshots on push to release branches. - Release (
.github/workflows/release.yml): 4-stage pipeline triggered by GitHub release — stage artifacts, release to Maven Central, publish docs to GitHub Pages, bump version. - Release Notes (
.github/workflows/release-notes.yml): Auto-drafts release notes using release-drafter with category labels.
- Groovy source files use standard Grails conventions (services and taglibs in
grails-app/, other classes insrc/main/groovy/). - Use
deffor local variables where the type is inferred from the right-hand side (e.g., constructor calls, method calls, casts, factory methods). Explicit types should only be used for local variables when the type cannot be inferred or when needed for@CompileStaticcompilation. This applies to both production code and tests. - When writing Gradle, always use the latest best practices to avoid eager initialization.