Skip to content

Commit dfb7e88

Browse files
Merge pull request #735 from Netflix/type-error-handling
Throw ParseException when a configuration setting can't be parsed as the type requested
2 parents 3191c48 + add6cf1 commit dfb7e88

File tree

8 files changed

+432
-493
lines changed

8 files changed

+432
-493
lines changed

archaius2-api/src/main/java/com/netflix/archaius/api/PropertyFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
@Deprecated
2727
public interface PropertyFactory extends PropertyRepository {
2828
/**
29-
* Create a property for the property name.
30-
* @deprecated Use {@link PropertyRepository#get(String, Type)} instead.
29+
* Create a {@link PropertyContainer} for the given name.
30+
* @deprecated Use {@link PropertyRepository#get(String, Type)} or {@link PropertyRepository#get(String, Class)} instead.
3131
*/
3232
@Deprecated
3333
PropertyContainer getProperty(String propName);

archaius2-archaius1-bridge/src/test/java/com/netflix/archaius/bridge/DynamicPropertyTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package com.netflix.archaius.bridge;
22

3+
import com.netflix.archaius.api.PropertyRepository;
34
import org.apache.commons.configuration.AbstractConfiguration;
45

56
import com.google.inject.Guice;
67
import com.google.inject.Injector;
78
import com.google.inject.Key;
89
import com.netflix.archaius.api.Config;
910
import com.netflix.archaius.api.Property;
10-
import com.netflix.archaius.api.PropertyFactory;
1111
import com.netflix.archaius.api.config.SettableConfig;
1212
import com.netflix.archaius.api.inject.RuntimeLayer;
1313
import com.netflix.archaius.guice.ArchaiusModule;
@@ -44,7 +44,7 @@ public void after() {
4444
@Test
4545
public void settingOnArchaius2UpdateArchaius1(TestInfo testInfo) {
4646
String methodName = testInfo.getTestMethod().map(Method::getName).orElse("unknown");
47-
Property<String> a2prop = injector.getInstance(PropertyFactory.class).getProperty(methodName).asString("default");
47+
Property<String> a2prop = injector.getInstance(PropertyRepository.class).get(methodName, String.class).orElse("default");
4848
DynamicStringProperty a1prop = DynamicPropertyFactory.getInstance().getStringProperty(methodName, "default");
4949

5050
assertEquals("default", a1prop.get());
@@ -63,7 +63,7 @@ public void testNonStringDynamicProperty() {
6363
config.accept(new PrintStreamVisitor());
6464
ConfigurationManager.getConfigInstance().setProperty("foo", 123);
6565

66-
Property<Integer> prop2 = injector.getInstance(PropertyFactory.class).getProperty("foo").asInteger(1);
66+
Property<Integer> prop2 = injector.getInstance(PropertyRepository.class).get("foo", Integer.class).orElse(1);
6767

6868
DynamicIntProperty prop = DynamicPropertyFactory.getInstance().getIntProperty("foo", 2);
6969

archaius2-core/src/main/java/com/netflix/archaius/DefaultPropertyFactory.java

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.netflix.archaius.api.PropertyContainer;
77
import com.netflix.archaius.api.PropertyFactory;
88
import com.netflix.archaius.api.PropertyListener;
9+
import com.netflix.archaius.exceptions.ParseException;
910
import org.slf4j.Logger;
1011
import org.slf4j.LoggerFactory;
1112

@@ -62,6 +63,7 @@ public DefaultPropertyFactory(Config config) {
6263
this.config.addListener(this);
6364
}
6465

66+
/** @deprecated Use {@link #get(String, Type)} or {@link #get(String, Class)} instead. */
6567
@Override
6668
@Deprecated
6769
@SuppressWarnings("deprecation")
@@ -124,20 +126,24 @@ public <T> Property<T> asType(Class<T> type, T defaultValue) {
124126

125127
@Override
126128
public <T> Property<T> asType(Function<String, T> mapper, String defaultValue) {
127-
T typedDefaultValue = mapper.apply(defaultValue);
129+
T typedDefaultValue = applyOrThrow(mapper, defaultValue);
128130
return getFromSupplier(propName, null, () -> {
129131
String value = config.getString(propName, null);
130132
if (value != null) {
131-
try {
132-
return mapper.apply(value);
133-
} catch (Exception e) {
134-
LOG.error("Invalid value '{}' for property '{}'. Will return the default instead.", propName, value);
135-
}
133+
return applyOrThrow(mapper, value);
136134
}
137135

138136
return typedDefaultValue;
139137
});
140138
}
139+
140+
private <T> T applyOrThrow(Function<String, T> mapper, String value) {
141+
try {
142+
return mapper.apply(value);
143+
} catch (RuntimeException e) {
144+
throw new ParseException("Invalid value '" + value + "' for property '" + propName + "'.", e);
145+
}
146+
}
141147
};
142148
}
143149

@@ -208,23 +214,28 @@ public PropertyImpl(KeyAndType<T> keyAndType, Supplier<T> supplier) {
208214

209215
@Override
210216
public T get() {
211-
int cacheVersion = cache.getStamp();
217+
int[] cacheVersion = new int[1];
218+
T currentValue = cache.get(cacheVersion);
212219
int latestVersion = masterVersion.get();
213-
214-
if (cacheVersion != latestVersion) {
215-
T currentValue = cache.getReference();
216-
T newValue = null;
217-
try {
218-
newValue = supplier.get();
219-
} catch (Exception e) {
220-
LOG.error("Unable to get current version of property '{}'", keyAndType.key, e);
221-
}
222-
223-
if (cache.compareAndSet(currentValue, newValue, cacheVersion, latestVersion)) {
224-
// Possible race condition here but not important enough to warrant locking
220+
221+
if (cacheVersion[0] == latestVersion) {
222+
return currentValue;
223+
}
224+
225+
try {
226+
T newValue = supplier.get();
227+
228+
if (cache.compareAndSet(currentValue, newValue, cacheVersion[0], latestVersion)) {
229+
// newValue could be stale here already, if the cache was updated *again* between the CAS and this line
230+
// We don't care enough about this edge case to fix it.
225231
return newValue;
226232
}
233+
234+
} catch (RuntimeException e) {
235+
LOG.error("Unable to get current version of property '{}'", keyAndType.key, e);
236+
throw e; // Rethrow the exception, our caller should know that the property is not available
227237
}
238+
228239
return cache.getReference();
229240
}
230241

archaius2-core/src/test/java/com/netflix/archaius/ConfigManagerTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import java.util.concurrent.atomic.AtomicReference;
2121

2222
import com.netflix.archaius.api.Property;
23-
import com.netflix.archaius.api.PropertyFactory;
23+
import com.netflix.archaius.api.PropertyRepository;
2424
import com.netflix.archaius.config.DefaultCompositeConfig;
2525

2626
import com.netflix.archaius.config.MapConfig;
@@ -45,9 +45,9 @@ public void testBasicReplacement() throws ConfigException {
4545
.build()));
4646

4747

48-
PropertyFactory factory = DefaultPropertyFactory.from(config);
48+
PropertyRepository factory = DefaultPropertyFactory.from(config);
4949

50-
Property<String> prop = factory.getProperty("abc").asString("defaultValue");
50+
Property<String> prop = factory.get("abc", String.class).orElse("defaultValue");
5151

5252
// Use an AtomicReference to capture the property value change
5353
AtomicReference<String> capturedValue = new AtomicReference<>("");

archaius2-core/src/test/java/com/netflix/archaius/DefaultPropertyContainerTest.java

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)