Skip to main content

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.

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();
}

Constraint Reference

AnnotationValidatesApplies To
@NotBlankString is not null, empty, or whitespaceString
@PositiveNumber is greater than 0Numeric types
@NegativeNumber is less than 0Numeric types
@Min(value)Number is greater than or equal to valueNumeric types
@Max(value)Number is less than or equal to valueNumeric 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.

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();
}

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