SortPom is a Maven plugin that sorts pom.xml files. It is a two-module Maven project:
sorter/— Core sorting library (pure business logic, no Maven dependencies)maven-plugin/— Maven plugin wrapper around the sorter
mvn verify # Full build: compile, test, integration tests
mvn test # Unit tests only
mvn fmt:format # Auto-format all Java source (Spotify fmt-maven-plugin)
mvn fmt:check # Verify formatting without modifying filesAlways run mvn fmt:format before committing, or let it run as part of the build. Code that fails the format check will break CI.
Integration tests live in maven-plugin/src/it/ and are executed via maven-invoker-plugin during verify.
- Current target: Java 17 (
maven.compiler.release=17) - Use
varfor local variable type inference where it improves readability (it is widely used in this codebase) - Java 17 features are available: sealed classes, pattern matching for
instanceof, records, text blocks - Do not use language features beyond Java 17
Formatting is enforced automatically by fmt-maven-plugin (Google Java Format). Do not manually adjust whitespace or brace placement — just run mvn fmt:format.
Naming:
- Classes:
PascalCase - Methods and fields:
camelCase - Constants:
UPPER_SNAKE_CASE - Packages: lowercase, e.g.
sortpom.wrapper.content
Javadoc: Add Javadoc to public classes and non-trivial public methods. Include @author on classes only where already present in the file.
Annotations: Use @SuppressWarnings sparingly and only with justification.
The codebase is layered; keep concerns separated:
| Layer | Location | Responsibility |
|---|---|---|
| Plugin (Mojo) | maven-plugin/.../SortMojo, VerifyMojo, AbstractParentMojo |
Maven lifecycle, parameter wiring |
| Application | sorter/.../SortPomImpl, SortPomService |
Orchestration |
| Domain | sorter/.../wrapper/, sorter/.../verify/ |
Sorting logic, XML structure |
| Infrastructure | sorter/.../util/, sorter/.../output/ |
File I/O, XML serialisation |
Key patterns in use:
- Builder —
PluginParameters.Builderfor all configuration; use this pattern for new config objects - Strategy / Wrapper hierarchy —
Wrapper<T>implementations define different sort behaviours; add new sorting strategies here - Factory —
WrapperFactory/WrapperFactoryImplcreates wrappers; register new wrapper types here - Template Method —
HierarchyWrapperOperationdefines the tree traversal hooks
Never mix Maven plugin concerns (Mojo annotations, MavenProject) into the sorter module.
- Throw
FailureException(extendsRuntimeException) for unrecoverable errors in the sorter module - The plugin layer converts
FailureExceptiontoMojoFailureExceptionviaExceptionConverter - Do not propagate checked exceptions beyond module boundaries
Coverage requirement: 100%. Every new production code path must have a corresponding test.
- JUnit 6 (Jupiter) —
@Test,@ParameterizedTest, etc. - Mockito 5 — for mocking collaborators
- Hamcrest 3 — use for all assertions:
assertThat(actual, matcher) - JUnit
Assertions— only forassertThrowsandfail(no Hamcrest equivalent)
Test classes mirror the production package structure and are named {Subject}Test.
For XML sorting tests, use the fluent test utilities rather than constructing objects manually:
// Sorting / round-trip XML tests
XmlProcessorTestUtil.create()
.predefinedSortOrder("recommended_2008_06")
.testInputAndExpected("src/test/resources/MyFeature_input.xml",
"src/test/resources/MyFeature_expected.xml");
// Full integration through SortPomImpl
SortPomImplUtil.create()
.sortDependencies("scope,groupId,artifactId")
.testFiles("src/test/resources/MyFeature_input.xml",
"src/test/resources/MyFeature_expected.xml");XML fixture files live in sorter/src/test/resources/. Name them <Feature>_input.xml and <Feature>_expected.xml.
- Normal (happy-path) cases with fixture XML files
- Edge cases: empty elements, comments, processing instructions, special characters
- Error cases: verify
FailureExceptionis thrown with the correct message - Parameter validation: invalid combinations should fail with a clear error
- Do not mock the file system or XML parser in integration-style tests; use real fixture files
- Do not use
@BeforeEachto share mutable state across unrelated tests - Do not test private methods directly; test through the public API
- Add or update the parameter in
PluginParameters(and itsBuilder) - Wire the parameter through
AbstractParentMojoif it is user-facing - Implement the logic in the sorter module (typically a new
Wrapperimplementation or a new phase in an existing wrapper) - Register the wrapper in
WrapperFactoryImplif needed - Add fixture XML files and tests covering all branches
- Run
mvn verifyto confirm 100% coverage and passing integration tests