Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decouple configuration parsing #109

Merged
merged 24 commits into from
Mar 16, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Copying logic from old classes
  • Loading branch information
kvosper committed Mar 14, 2018
commit 75006cd8b8edc071a80b136497c5dd7a27ea0cf9
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Copyright (C) 2013-2018 Expedia Inc.
*
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand All @@ -15,9 +15,13 @@
*/
package com.hotels.styx.infrastructure.configuration;

import com.google.common.collect.ImmutableList;
import com.hotels.styx.api.configuration.Configuration;
import com.hotels.styx.infrastructure.configuration.yaml.PlaceholderResolver.UnresolvedPlaceholder;

import java.util.Collection;
import java.util.Map;
import java.util.Objects;

/**
* Extensible configuration, interface is agnostic to how it is stored in memory.
Expand All @@ -29,7 +33,23 @@ public interface ExtensibleConfiguration<C extends ExtensibleConfiguration<C>> e

C withOverrides(Map<String, String> overrides);

int unresolvedPlaceholderCount();
PlaceholderResolutionResult<C> resolvePlaceholders();

class PlaceholderResolutionResult<C extends ExtensibleConfiguration<C>> {
private final C resolvedConfiguration;
private final Collection<UnresolvedPlaceholder> unresolvedPlaceholders;

public PlaceholderResolutionResult(C resolvedConfiguration, Collection<UnresolvedPlaceholder> unresolvedPlaceholders) {
this.resolvedConfiguration = Objects.requireNonNull(resolvedConfiguration);
this.unresolvedPlaceholders = ImmutableList.copyOf(unresolvedPlaceholders);
}

public C resolvedConfiguration() {
return resolvedConfiguration;
}

C resolvePlaceholders();
public Collection<UnresolvedPlaceholder> unresolvedPlaceholders() {
return unresolvedPlaceholders;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.hotels.styx.infrastructure.configuration;

import com.google.common.annotations.VisibleForTesting;
import com.hotels.styx.infrastructure.configuration.ExtensibleConfiguration.PlaceholderResolutionResult;
import com.hotels.styx.infrastructure.configuration.yaml.YamlConfig;

import java.util.Map;
Expand Down Expand Up @@ -70,18 +71,13 @@ private C deserialise(ConfigurationProvider provider) {
}

private C resolvePlaceholders(C config) {
if (config.unresolvedPlaceholderCount() == 0) {
return config;
}

int previousUnresolvedPlaceholderCount;
PlaceholderResolutionResult<C> result = config.resolvePlaceholders();

do {
previousUnresolvedPlaceholderCount = config.unresolvedPlaceholderCount();
config = config.resolvePlaceholders();
} while (config.unresolvedPlaceholderCount() < previousUnresolvedPlaceholderCount);
if (!result.unresolvedPlaceholders().isEmpty()) {
throw new IllegalStateException("Unresolved placeholders: " + result.unresolvedPlaceholders());
}

return config;
return result.resolvedConfiguration();
}

private C parent(String includePath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,68 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hotels.styx.api.configuration.ConversionException;
import com.hotels.styx.infrastructure.configuration.ExtensibleConfiguration;
import com.hotels.styx.infrastructure.configuration.yaml.PlaceholderResolver.UnresolvedPlaceholder;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;

import static com.google.common.base.Throwables.propagate;
import static com.hotels.styx.infrastructure.configuration.yaml.YamlConfigurationFormat.YAML_MAPPER;
import static java.util.Collections.emptyMap;
import static java.util.Objects.requireNonNull;

/**
*
*/
public class YamlConfiguration implements ExtensibleConfiguration<YamlConfiguration> {
private final JsonNode rootNode;
private final int unresolvedPlaceholders;

public YamlConfiguration(JsonNode rootNode) {
this.rootNode = requireNonNull(rootNode);
this.unresolvedPlaceholders = countUnresolvedPlaceholders();
}

private YamlConfiguration(JsonNode rootNode, int unresolvedPlaceholders) {
this.rootNode = requireNonNull(rootNode);
this.unresolvedPlaceholders = unresolvedPlaceholders;
@Override
public YamlConfiguration withParent(YamlConfiguration parent) {
return new YamlConfiguration(merge(parent.rootNode.deepCopy(), rootNode));
}

private static int countUnresolvedPlaceholders() {
return 0;
@Override
public YamlConfiguration withOverrides(Map<String, String> overrides) {
JsonNode newRootNode = rootNode.deepCopy();

applyExternalOverrides(newRootNode, overrides);

return new YamlConfiguration(newRootNode);
}

@Override
public YamlConfiguration withParent(YamlConfiguration parent) {
return new YamlConfiguration(merge(parent.rootNode.deepCopy(), rootNode));
public PlaceholderResolutionResult<YamlConfiguration> resolvePlaceholders() {
JsonNode newRootNode = rootNode.deepCopy();

Collection<UnresolvedPlaceholder> unresolvedPlaceholders = PlaceholderResolver.resolvePlaceholders(newRootNode, emptyMap());

return new PlaceholderResolutionResult<>(new YamlConfiguration(newRootNode), unresolvedPlaceholders);
}

@Override
public <T> Optional<T> get(String property, Class<T> tClass) {
return nodeAt(property)
.map(node -> {
if (tClass == Path.class) {
return (T) Paths.get(node.textValue());
}

return parseNodeToClass(node, tClass);
});
}

@Override
public <X> X as(Class<X> type) throws ConversionException {
return parseNodeToClass(rootNode, type);
}

private static JsonNode merge(JsonNode baseNode, JsonNode overrideNode) {
Expand All @@ -63,15 +90,6 @@ private static JsonNode merge(JsonNode baseNode, JsonNode overrideNode) {
return baseNode;
}

@Override
public YamlConfiguration withOverrides(Map<String, String> overrides) {
JsonNode newRootNode = rootNode.deepCopy();

applyExternalOverrides(newRootNode, overrides);

return new YamlConfiguration(newRootNode, unresolvedPlaceholders);
}

private static void applyExternalOverrides(JsonNode rootNode, Map<String, String> overrides) {
overrides.forEach((key, value) -> {
NodePath nodePath = new NodePath(key);
Expand All @@ -80,37 +98,6 @@ private static void applyExternalOverrides(JsonNode rootNode, Map<String, String
});
}

@Override
public int unresolvedPlaceholderCount() {
return unresolvedPlaceholders;
}

@Override
public YamlConfiguration resolvePlaceholders() {
int stillUnresolved = 0;

JsonNode newRootNode = null;

return new YamlConfiguration(newRootNode, stillUnresolved);
}

@Override
public <T> Optional<T> get(String property, Class<T> tClass) {
return nodeAt(property)
.map(node -> {
if (tClass == Path.class) {
return (T) Paths.get(node.textValue());
}

return parseNodeToClass(node, tClass);
});
}

@Override
public <X> X as(Class<X> type) throws ConversionException {
return parseNodeToClass(rootNode, type);
}

private Optional<JsonNode> nodeAt(String property) {
NodePath nodePath = new NodePath(property);

Expand Down