Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,24 @@ Or in Maven:

📖 **For complete documentation, examples, and all available options, see the [Configuration Guide](docs/CONFIGURATION.md).**

#### Strict mode (fail the build on generation errors)

By default, if generation fails (for a builder or for a Jackson module) the processor emits a **compiler warning** and continues, so the build still succeeds. This keeps local/iterative development smooth.

For high-reliability builds (e.g. CI/release pipelines) you can opt in to **strict mode**, which promotes generation failures — including Jackson module generation failures — to **compiler errors** that fail the build:

```bash
javac -Asimplebuilder.strict=true YourClass.java
```

```xml
<compilerArgs>
<arg>-Asimplebuilder.strict=true</arg>
</compilerArgs>
```

Strict mode is **disabled by default** (`false`); the default behavior (warnings only, build does not fail) is unchanged.

## Examples

The `example` module contains real-world examples demonstrating various builder configurations and features. You can explore the source DTOs and their generated builders:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ public class BuilderProcessor extends AbstractProcessor {
private JacksonModuleGenerator jacksonModuleGenerator;
private boolean supportedJdk = true;

/**
* Opt-in strict/fail-fast mode. When enabled (via {@code -Asimplebuilder.strict=true}),
* generation failures are reported as compiler errors that fail the build. Defaults to {@code
* false} (warnings only, build does not fail).
*/
private boolean strict = false;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
Expand All @@ -78,6 +85,9 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
BuilderConfiguration globalConfig = reader.readBuilderConfiguration();
logger.debug("Loaded global configuration from compiler arguments: %s", globalConfig);

this.strict = reader.readBooleanValue(CompilerArgumentsEnum.STRICT);
logger.debug("Strict generation mode: %s", this.strict);

this.context = new ProcessingContext(logger, globalConfig, processingEnv);
this.codeGenerator = new RoasterCodeGenerator(processingEnv, logger);
this.jacksonModuleGenerator = new JacksonModuleGenerator(processingEnv, logger);
Expand Down Expand Up @@ -116,9 +126,17 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
try {
codeGenerator.generateClass(moduleClassDef);
} catch (BuilderException e) {
context.warning(
"simple-builders: Error generating Jackson module for package %s: %s",
packageName, e.getMessage());
// By default Jackson module generation failures are warnings. In opt-in strict mode
// they are promoted to errors that fail the build.
if (strict) {
context.error(
"simple-builders: Error generating Jackson module for package %s: %s",
packageName, e.getMessage());
} else {
context.warning(
"simple-builders: Error generating Jackson module for package %s: %s",
packageName, e.getMessage());
}
}
}
// Reset indentation after Jackson module generation as well
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,15 @@ public enum CompilerArgumentsEnum {

// === Logging ===
/** Option for verbose logging output. */
VERBOSE("verbose");
VERBOSE("verbose"),

// === Error Handling ===
/**
* Option for strict/fail-fast generation mode. When enabled, builder (and Jackson module)
* generation failures are reported as compiler errors that fail the build instead of warnings.
* Defaults to disabled (warnings only, build does not fail).
*/
STRICT("strict");

/** Compiler option prefix for all simple-builders options. */
private static final String OPTION_PREFIX = "simplebuilder.";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* MIT License
*
* Copyright (c) 2026 Andreas Igel
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package org.javahelpers.simple.builders.processor;

import static com.google.testing.compile.CompilationSubject.assertThat;
import static org.javahelpers.simple.builders.processor.testing.ProcessorTestUtils.createCompiler;
import static org.javahelpers.simple.builders.processor.testing.ProcessorTestUtils.simpleBuilderClass;

import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import javax.tools.JavaFileObject;
import org.junit.jupiter.api.Test;

/**
* Tests that the opt-in strict/fail-fast mode ({@code -Asimplebuilder.strict=true}) also governs
* Jackson module generation failures.
*
* <p>A failure is induced by providing a hand-written class named {@code
* SimpleBuildersJacksonModule} in the DTO's package, which collides with the module the processor
* generates at the end of processing and makes generation fail with a "already exists" error.
*/
class JacksonModuleStrictModeTest {

private static final String PACKAGE = "pkg.jacksonstrict";

private JavaFileObject dto() {
return simpleBuilderClass(
PACKAGE,
"JacksonStrictDto",
"""
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
""");
}

/** A hand-written class colliding with the generated Jackson module for the package. */
private JavaFileObject collidingModule() {
return JavaFileObjects.forSourceLines(
PACKAGE + ".SimpleBuildersJacksonModule",
"package " + PACKAGE + ";",
"public class SimpleBuildersJacksonModule {}");
}

@Test
void defaultMode_jacksonModuleFailure_isWarning_andCompilationSucceeds() {
Compilation compilation =
createCompiler()
.withOptions(
"-Asimplebuilder.generateJacksonModule=true",
"-Asimplebuilder.usingJacksonDeserializerAnnotation=true")
.compile(dto(), collidingModule());

assertThat(compilation).succeeded();
assertThat(compilation).hadWarningContaining("Error generating Jackson module");
}

@Test
void strictMode_jacksonModuleFailure_isError_andCompilationFails() {
Compilation compilation =
createCompiler()
.withOptions(
"-Asimplebuilder.generateJacksonModule=true",
"-Asimplebuilder.usingJacksonDeserializerAnnotation=true",
"-Asimplebuilder.strict=true")
.compile(dto(), collidingModule());

assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("Error generating Jackson module");
}
}
Loading