Validation
MittenLib ensures your configs are valid before they reach your application code. The validation system runs during deserialization and prevents your plugin from accepting bad configuration data.
Built-in Constraints
MittenLib provides several built-in annotations to validate common data types.
- Java
- YAML (Invalid Example)
import me.bristermitten.mittenlib.config.validation.*;
@Config
public interface CombatConfig {
@NotBlank
String killMessage();
@Positive
int comboWindowTicks();
@Min(1)
@Max(100)
default int maxReach() {
return 5;
}
@Range(min = 0, max = 1)
double knockbackMultiplier();
}
# This would fail validation:
killMessage: "" # Error: Cannot be blank
comboWindowTicks: -10 # Error: Must be positive
maxReach: 150 # Error: Must be 100 or less
knockbackMultiplier: 1.5 # Error: Must be between 0 and 1
Constraint Reference
| Annotation | Validates | Applies To |
|---|---|---|
@NotBlank | String is not null, empty, or whitespace | String |
@Positive | Number is greater than 0 | Numeric types |
@Negative | Number is less than 0 | Numeric types |
@Min(value) | Number is greater than or equal to value | Numeric types |
@Max(value) | Number is less than or equal to value | Numeric types |
@Range(min, max) | Number is between min and max (inclusive) | Numeric types |
The lack of a @Nullable annotation is also considered a form of constraint, which adds a check that the property is not null.
How Validation Works
When a configuration is loaded, MittenLib generates a dedicated ...Validator class. This validator is automatically invoked by the configuration provider when reading the file.
Key behaviors:
- Safe Loading: Validation runs before the final configuration object is constructed.
- Fail-Fast: If any constraint is violated, the configuration is not returned, and the loading process fails.
- Informative Effort Collection: All validation errors are collected and reported at once in a pretty format, allowing the admin to fix all issues in one go.
Custom Validators
For more complex logic that the built-in constraints can't handle, implement the Validator interface, and then mark the properties to validate with ValidateWith.
- Java
- YAML
import me.bristermitten.mittenlib.config.validation.Validator;
import org.bukkit.Material;
public class TestValidator implements Validator<String> {
@Override
public Optional<String> validate(String value) {
if (!value.equals("hunter2")) {
return Optional.of("Invalid password: " + value);
}
return Optional.empty(); // Valid
}
}
@Config
public interface SecretConfig {
@ValidateWith(TestValidator.class)
String password();
}
password: adm1n # Error: Invalid password: adm1n
Validation with Collections
Right now, validation does not work on collections and will throw an error. This will be improved in the future, but for the time being we recommend writing a custom validator if this is required.
Validation in Guice
Since config loading is always lazy by default, validation errors won't be thrown until the config is loaded, either from a ConfigProvider or provisioning an instance directly.
There are many cases where eager validation is required, e.g. to disable the plugin on startup if a critical config file is invalid.
There is currently no clean way to do this, but it is easy to approximate by simply loading your config instance (e.g. with Injector#getInstance) on startup. This will be improved in the future.
Next Steps
- Guice Integration - Automatic loading with validation
- Persistence - Saving validated configs back to files