REST+Front-End template with a ton of integrated component with many enterprise features : RBAC, Cache, modular modules
Key features:
- Convention over Code for everything: light webpack, http error code, CDI, Java9 named parameters
- RBAC with URL based authorization and dynamic roles
- Exception to REST/HTTP code mapping
- Advanced RS validation
- Tuned Jackson configuration for minified payload and validation
- Test powered with CSV data load to/from JPA entities
- TDD ready with pre-built asserts with Mockito and Wiremock, target is
100%code coverage - Optional encrypted properties support and database configuration with Jasypt
- Tuned configuration for dev, build and production
- Spring-Data extensions for performance, minimal code and exception handling
A Spring based REST architecture
- Spring Boot/Security/Web/Data
- CXF
- JPA / Hibernate
- Hibernate Validator for JPA and RS
- JUnit
- Wiremock
Requirements
- Java-25
- Maven 3.9.16
Ligoj Bootstrap (org.ligoj.bootstrap, currently 4.0.0-SNAPSHOT) is a Spring + Apache CXF JAX-RS architecture template consumed by downstream Ligoj projects as libraries and parent POMs. Requirements: Java 25, Maven 3.9.16+. The Maven parent chain goes module → parent/pom.xml → org.ligoj.parent:project (external repo) — build profiles and plugin defaults live there.
# Build everything (unit + integration tests)
mvn clean package
# All features (w/o deployment)
mvn clean package -Psources,javadoc,github,sonatype,jacoco,test
# Compile a module quickly (with its dependents)
mvn -pl bootstrap-core,bootstrap-business -am test-compile
# Tests for one module
mvn test -pl bootstrap-business
# Single test class
mvn test -pl bootstrap-business -Dtest=SystemPluginResourceTest
# Coverage (target is 100%)
mvn clean package -Pjacoco -Djacoco.includes="org.ligoj.bootstrap.*"
# Full feature build (w/o deployment)
mvn clean package -Psources,javadoc,github,sonatype,jacoco,test
# Check dependency updates
mvn versions:display-dependency-updates -Pjacoco -Dmaven.version.ignore="^(.*[.-](alpha|beta|rc|M|B|Alpha|Beta|BETA|RC|pre)[.-]?[0-9]*|[0-9]{8}.*)$"Test failures do NOT fail the build: the parent POM sets testFailureIgnore=true for both surefire and failsafe. BUILD SUCCESS is meaningless for tests — always check the Tests run: … Failures: … Errors: … lines or target/surefire-reports/.
Test naming splits unit vs integration: *Test = unit/Spring test, *IT = integration test that boots the real app on Jetty (port 6380). bootstrap-business runs them as two surefire executions via UTSuite/ITSuite (class-name regex suites); elsewhere failsafe runs *IT only with -Pit.
.mvn/jvm.config passes --sun-misc-unsafe-memory-access=allow — required for the build JVM.
Dependency layering: bootstrap-core → bootstrap-business → bootstrap-plugin, plus bootstrap-core + bootstrap-launcher → bootstrap-business-test and bootstrap-launcher → bootstrap-web-test → bootstrap-web.
- bootstrap-core — foundation library, no Spring Boot/CXF server. Bean & JPA base classes (
AbstractPersistable,AbstractAudited), system entities (org.ligoj.bootstrap.model.system.*, tables prefixedS_), CSV engine, plugin SPI +PluginsClassLoader, Jasypt crypto, Hibernate naming strategies, custom validators. - bootstrap-business — the REST/JPA runtime: CXF wiring, exception mappers, Jackson config, Spring Data extensions, RBAC filters, Hazelcast/JCache,
/system/**REST resources. - bootstrap-plugin — plugin management REST layer (
/system/plugin): install/update from Maven Central/Nexus,PluginApplicationRunListener(swaps inPluginsClassLoaderat Spring Boot startup), Javadoc→OpenAPI enrichment. - bootstrap-business-test — test-support library (compile-scope deps): the
Abstract*Testhierarchy, HSQLDB Spring contexts, RBAC CSV fixtures. - bootstrap-business-parent / bootstrap-web-parent — pom-only parents consumed by downstream backend (
org.ligoj.app:app-api) and UI (app-ui) projects; they preconfigure dependencies and resource filtering. - bootstrap-web — UI-tier library:
BackendProxyServletreverse proxy, extended Spring Security request matchers, OAuth2 client support. - bootstrap-web-test — pom-only test dependency aggregation (
<type>pom</type>). - bootstrap-launcher — two-class embedded-Jetty launcher (
org.ligoj.bootstrap.http.server.Main) used by IDE runs and*ITtests; readsjetty.properties(defaultMETA-INF/jetty/jetty-dev.properties). Production downstream apps boot via Spring Boot instead.
Spring wiring is XML-first: contexts live at src/*/resources/META-INF/spring/*.xml and compose via <import resource="classpath*:/META-INF/spring/…"/> so plugin JARs can contribute. core-context-common.xml holds the single component-scan (org.ligoj.bootstrap); default-autowire="byName" is used pervasively — bean/field names matter.
Package conventions (root is always org.ligoj.bootstrap): model.system = JPA entities, dao.system = Spring Data repositories, resource.system.<feature> = JAX-RS resources under /system/**, core.* = reusable technical code, http.* = launcher/proxy/web security. One deliberate exception: org.eclipse.jetty.util.resource.VisibleCombinedResource in bootstrap-launcher (package-private Jetty API access).
Spring Data extensions: all repositories in org.ligoj get RestRepository methods (findOneExpected, deleteAllExpected, …) through RestRepositoryFactoryBean declared in jpa-context-common.xml. jqGrid/DataTables JSON filters translate to Criteria via PaginationDao/DynamicSpecification; grid responses use TableItem/PaginationJson.
Exception → HTTP mapping: @Provider mappers in bootstrap-business/.../core/resource/mapper/ (registered in rest-context-common.xml) serialize a uniform ServerError JSON body. E.g. BusinessException/ValidationJsonException → 400, EntityNotFoundException → 404, DataIntegrityViolationException → 412, FailSafeExceptionMapper → 500 with message stripped. @OnNullReturn404 turns a null JAX-RS return into 404.
Jackson: the single objectMapper bean is ObjectMapperTrim (NON_NULL, lower-cased enums, custom date/time (de)serializers via BootstrapModule). The codebase is on Jackson 3 (tools.jackson.* packages) with jackson-annotations still 2.x — watch package names when touching JSON code.
RBAC: SystemAuthorization rows hold role + HTTP method + URL regex (type API or UI). AuthorizingFilter (last Spring Security filter) matches request path+method against a JCache-cached structure built by AuthorizationResource; RbacUserDetailsService loads roles (cache user-details). Authentication is pre-auth header based (SM_UNIVERSALID + x-api-key via ApiTokenAuthenticationFilter).
Plugin system: PluginsClassLoader scans ${ligoj.home}/plugins/*.jar (default ~/.ligoj/plugins), keeps only the newest version per artifact, optionally verifies JAR signatures. Plugins implement FeaturePlugin (key format a:b:c); SystemPluginResource diffs discovered beans against the S_PLUGIN table on context refresh and runs install()/update(), seeding entities from csv/<kebab-case-entity>.csv inside the plugin JAR.
Configuration/crypto: properties may be Jasypt-encrypted as ENC(...); password resolved from app.crypto.password/APP_CRYPTO_PASSWORD/app.crypto.file (see core-context-common.xml). ConfigurationResource resolves keys from Spring Environment first, then the S_CONFIGURATION table, cached in JCache.
Extend the chain in bootstrap-business-test (org.ligoj.bootstrap package): AbstractTest → AbstractDataGeneratorTest → AbstractSecurityTest → AbstractJpaTest → AbstractAppTest → AbstractServerTest (WireMock on port 8120). AbstractRestTest boots the real server for *IT. Canonical Spring test setup (see AbstractBootTest):
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring/application-context-test.xml")
@Rollback @Transactional- DB is in-memory HSQLDB (
jpa-context-test.xml), which imports the productionjpa-context-common.xml— repositories/auditing behave as in production. Persistence unit name is alwayspu. - Test data loads from CSV via
CsvForJpa/persistEntities("csv/system-test", Class...); header row = property names, foreign keys as dotted paths (role.name), files named<entity-kebab-case>.csv. AbstractAppTest.persistSystemEntities()seeds the RBAC tables; default principal isjunit(initSpringSecurityContext(...)to switch).src/main/resourcesandsrc/test/resourcesare Maven-filtered in bootstrap-business and downstream — literal${...}in resources will be substituted at build time.MatcherUtilasserts field/rule insideValidationJsonException;AbstractBusinessEntityTestcovers entity equals/hashCode reflectively.