
io.dropwizard.configuration.BaseConfigurationFactory Maven / Gradle / Ivy
The newest version!
package io.dropwizard.configuration;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.fasterxml.jackson.databind.node.TreeTraversingParser;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import org.jspecify.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
/**
* A generic factory class for loading configuration files, binding them to configuration objects, and
* validating their constraints. Allows for overriding configuration parameters from system properties.
*
* @param the type of the configuration objects to produce
*/
public abstract class BaseConfigurationFactory implements ConfigurationFactory {
private static final Pattern ESCAPED_COMMA_PATTERN = Pattern.compile("\\\\,");
private static final Pattern ESCAPED_COMMA_SPLIT_PATTERN = Pattern.compile("(? klass;
private final String propertyPrefix;
/**
* The object mapper to use for mapping configuration files to objects.
*/
protected final ObjectMapper mapper;
private final ConfigurationMetadata configurationMetadata;
@Nullable
private final Validator validator;
private final String formatName;
private final JsonFactory parserFactory;
/**
* Creates a new configuration factory for the given class.
*
* @param parserFactory the factory that creates the parser used
* @param formatName the name of the format parsed by this factory (used in exceptions)
* @param klass the configuration class
* @param validator the validator to use
* @param objectMapper the object mapper to use
* @param propertyPrefix the system property name prefix used by overrides
*/
protected BaseConfigurationFactory(JsonFactory parserFactory,
String formatName,
Class klass,
@Nullable Validator validator,
ObjectMapper objectMapper,
String propertyPrefix) {
this.klass = klass;
this.formatName = formatName;
this.propertyPrefix = propertyPrefix.endsWith(".") ? propertyPrefix : propertyPrefix + '.';
this.mapper = objectMapper;
this.parserFactory = parserFactory;
this.validator = validator;
this.configurationMetadata = new ConfigurationMetadata(mapper, klass);
}
@Override
public T build(ConfigurationSourceProvider provider, String path) throws IOException, ConfigurationException {
try (InputStream input = provider.open(requireNonNull(path))) {
final JsonNode node = mapper.readTree(createParser(input));
if (node == null) {
throw ConfigurationParsingException
.builder("Configuration at " + path + " must not be empty")
.build(path);
}
return build(node, path);
} catch (JsonParseException e) {
throw ConfigurationParsingException
.builder("Malformed " + formatName)
.setCause(e)
.setLocation(e.getLocation())
.setDetail(e.getMessage())
.build(path);
}
}
/**
* Constructs a {@link JsonParser} to parse the contents of the provided {@link InputStream}.
*
* @param input the input to parse
* @return the JSON parser for the given input
* @throws IOException if the parser creation fails due to an I/O error
*/
protected JsonParser createParser(InputStream input) throws IOException {
return parserFactory.createParser(input);
}
@Override
public T build() throws IOException, ConfigurationException {
try {
final T instance = klass.getDeclaredConstructor().newInstance();
final JsonNode node = mapper.valueToTree(instance);
return build(node, "default configuration");
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | SecurityException
| NoSuchMethodException | InvocationTargetException e) {
throw new IllegalArgumentException("Unable to create an instance " +
"of the configuration class: '" + klass.getCanonicalName() + "'", e);
}
}
/**
* Loads, parses, binds, and validates a configuration object for a given {@link JsonNode}.
*
* @param node the json node to parse the configuration from
* @param path the path of the configuration file
* @return a validated configuration object
* @throws IOException if there is an error reading the file
* @throws ConfigurationException if there is an error parsing or validating the file
*/
protected T build(JsonNode node, String path) throws IOException, ConfigurationException {
for (Map.Entry
© 2015 - 2025 Weber Informatics LLC | Privacy Policy