Skip to content

Commit

Permalink
Flag (present => "true", absent => "false") and environment variable …
Browse files Browse the repository at this point in the history
…support to the config package.

The metrics.conf bundled file is no longer needed as default values are now included in the ConfigurationKey annotation.

Configuration source priority is the following:
1. system property
2. environment variable
3. metrics.conf external file
4. default value
  • Loading branch information
tsegismont committed Jun 5, 2015
1 parent 4fadd57 commit 83c1d75
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.EnumMap;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
Expand All @@ -38,26 +36,35 @@
public class ConfigurableProducer {
static final String METRICS_CONF = "metrics.conf";

// Values need to be Optional as ConcurrentHashMap does not accept null values
private ConcurrentMap<String, Optional<String>> effectiveConfiguration;
private EnumMap<ConfigurationKey, String> effectiveConfig;

@PostConstruct
void init() {
effectiveConfiguration = new ConcurrentHashMap<>(ConfigurationKey.values().length);
effectiveConfig = new EnumMap<>(ConfigurationKey.class);

Properties defaultProperties = defaultProperties();
Properties fileProperties = configurationFile().map(this::configurationFileProperties).orElse(new Properties());
Properties configFileProperties = new Properties();
File configurationFile = findConfigurationFile();
if (configurationFile != null) {
load(configFileProperties, configurationFile);
}

for (ConfigurationKey configKey : ConfigurationKey.values()) {
String name = configKey.toString();
String envName = configKey.toEnvString();

for (ConfigurationKey configurationKey : ConfigurationKey.values()) {
String k = configurationKey.getExternalForm();
String v = System.getProperty(k);
if (v == null) {
v = fileProperties.getProperty(k);
if (v == null) {
v = defaultProperties.getProperty(k);
}
String value = System.getProperty(name);
if (value == null && envName != null) {
value = System.getenv(envName);
}
if (value == null) {
value = configFileProperties.getProperty(name);
}

if (configKey.isFlag()) {
effectiveConfig.put(configKey, String.valueOf(value != null));
} else {
effectiveConfig.put(configKey, value != null ? value : configKey.defaultValue());
}
effectiveConfiguration.put(k, Optional.ofNullable(v));
}
}

Expand All @@ -66,31 +73,26 @@ void init() {
String getConfigurationPropertyAsString(InjectionPoint injectionPoint) {
ConfigurationProperty configProp = injectionPoint.getAnnotated().getAnnotation(ConfigurationProperty.class);
if (configProp == null) {
String message = "Any field or parameter annotated with @Configurable "
+ "must also be annotated with @ConfigurationProperty";
String message = "Any field or parameter annotated with @" + Configurable.class.getSimpleName()
+ " must also be annotated with @" + ConfigurationProperty.class.getSimpleName();
throw new IllegalArgumentException(message);
}
String propertyName = configProp.value().getExternalForm();
return lookupConfigurationProperty(propertyName);
}

private String lookupConfigurationProperty(String propertyName) {
return effectiveConfiguration.get(propertyName).orElse(null);
return effectiveConfig.get(configProp.value());
}

private Optional<File> configurationFile() {
private File findConfigurationFile() {
String configurationFilePath = System.getProperty(METRICS_CONF);
if (configurationFilePath != null) {
File file = new File(configurationFilePath);
checkExplicitConfigurationFile(file);
return Optional.of(file);
return file;
}
File file = new File(System.getProperty("user.home"), ".metrics.conf");
if (!file.exists()) {
return Optional.empty();
return null;
}
checkConfigurationFile(file);
return Optional.of(file);
return file;
}

private void checkExplicitConfigurationFile(File file) {
Expand All @@ -109,21 +111,9 @@ private void checkConfigurationFile(File file) {
}
}

private Properties defaultProperties() {
try (InputStream input = getClass().getClassLoader().getResourceAsStream("META-INF/metrics.conf")) {
Properties properties = new Properties();
properties.load(input);
return properties;
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private Properties configurationFileProperties(File file) {
try (FileInputStream input = new FileInputStream(file)) {
Properties properties = new Properties();
private void load(Properties properties, File file) {
try (InputStream input = new FileInputStream(file)) {
properties.load(input);
return properties;
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,67 @@
*/
package org.hawkular.metrics.api.jaxrs.config;

import static com.google.common.base.Preconditions.checkArgument;

/**
* Application configuration keys.
* Parameter and flags definitions.
*
* @author Thomas Segismont
* @see Configurable
*/
public enum ConfigurationKey {

CASSANDRA_CQL_PORT("hawkular-metrics.cassandra-cql-port"),
CASSANDRA_NODES("hawkular-metrics.cassandra-nodes"),
CASSANDRA_KEYSPACE("cassandra.keyspace");
CASSANDRA_NODES("hawkular-metrics.cassandra-nodes", "127.0.0.1", "CASSANDRA_NODES", false),
CASSANDRA_CQL_PORT("hawkular-metrics.cassandra-cql-port", "9042", "CASSANDRA_CQL_PORT", false),
CASSANDRA_KEYSPACE("cassandra.keyspace", "hawkular_metrics", null, false),
CASSANDRA_RESETDB("cassandra.resetdb", null, null, true);

private final String name;
private final String env;
private final String defaultValue;
private final boolean flag;

/**
* @param name string representation when set in file or as system property
* @param defaultValue default value for parameters, null for flags
* @param env string representation when set in environment
* @param flag true if the value does not matter
*/
ConfigurationKey(String name, String defaultValue, String env, boolean flag) {
checkArgument(name != null, "name is null");
checkArgument(!flag || defaultValue == null, "Config flags can't have a default value");
this.name = name;
this.env = env;
this.defaultValue = defaultValue;
this.flag = flag;
}

private String externalForm;
/**
* @return name when set in file or as system property
*/
@Override
public String toString() {
return name;
}

/**
* @return the default value, or null if none, or the parameter is just a flag
*/
public String defaultValue() {
return defaultValue;
}

ConfigurationKey(String externalForm) {
this.externalForm = externalForm;
/**
* @return name when set in environment, or null if env binding is not supported
*/
public String toEnvString() {
return env;
}

public String getExternalForm() {
return externalForm;
/**
* @return true if the value does not matter
*/
public boolean isFlag() {
return flag;
}
}
22 changes: 0 additions & 22 deletions api/metrics-api-jaxrs/src/main/resources/META-INF/metrics.conf

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@
package org.hawkular.metrics.api.jaxrs.config;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;

import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.InjectionPoint;

Expand All @@ -39,8 +41,6 @@
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import com.google.common.io.Resources;

/**
* @author Thomas Segismont
*/
Expand All @@ -53,76 +53,80 @@ public class ConfigurableProducerTest {
public TemporaryFolder tempFolder = new TemporaryFolder();

@Mock
private InjectionPoint injectionPoint;
private InjectionPoint parameterInjectionPoint;
@Mock
private InjectionPoint flagInjectionPoint;

private final ConfigurableProducer configurableProducer = new ConfigurableProducer();

@Before
public void before() throws IOException {
parameterInjectionPoint = configureInjectionPoint(ConfigurationKey.CASSANDRA_KEYSPACE);
flagInjectionPoint = configureInjectionPoint(ConfigurationKey.CASSANDRA_RESETDB);
}

private InjectionPoint configureInjectionPoint(ConfigurationKey configurationKey) {
Annotated annotated = mock(Annotated.class);
ConfigurationProperty configurationProperty = mock(ConfigurationProperty.class);
when(configurationProperty.value()).thenReturn(ConfigurationKey.CASSANDRA_KEYSPACE);
when(configurationProperty.value()).thenReturn(configurationKey);
when(annotated.getAnnotation(eq(ConfigurationProperty.class))).thenReturn(configurationProperty);
injectionPoint = mock(InjectionPoint.class);
InjectionPoint injectionPoint = mock(InjectionPoint.class);
when(injectionPoint.getAnnotated()).thenReturn(annotated);
return injectionPoint;
}

@After
public void after() {
System.clearProperty(ConfigurableProducer.METRICS_CONF);
System.clearProperty(ConfigurationKey.CASSANDRA_KEYSPACE.getExternalForm());
System.clearProperty(ConfigurationKey.CASSANDRA_KEYSPACE.toString());
System.clearProperty(ConfigurationKey.CASSANDRA_RESETDB.toString());
}

@Test
public void shouldFetchDefaultValue() throws Exception {
// Create an empty config file in case the user running tests has a config file in <user.home>
Properties emptyProperties = new Properties();
File configFile = tempFolder.newFile();
emptyProperties.store(new FileOutputStream(configFile), null);
new Properties().store(new FileOutputStream(configFile), null);
System.setProperty(ConfigurableProducer.METRICS_CONF, configFile.getAbsolutePath());

configurableProducer.init();
String value = configurableProducer.getConfigurationPropertyAsString(injectionPoint);

Properties properties = new Properties();
URL resource = Resources.getResource("META-INF/metrics.conf");
properties.load(Resources.asByteSource(resource).openStream());
String value = configurableProducer.getConfigurationPropertyAsString(parameterInjectionPoint);

assertEquals(properties.getProperty(ConfigurationKey.CASSANDRA_KEYSPACE.getExternalForm()), value);
assertEquals(ConfigurationKey.CASSANDRA_KEYSPACE.defaultValue(), value);
}

@Test
public void shouldFetchValueFromConfigFile() throws Exception {
Properties properties = new Properties();
properties.setProperty(ConfigurationKey.CASSANDRA_KEYSPACE.getExternalForm(), "marseille");
properties.setProperty(ConfigurationKey.CASSANDRA_KEYSPACE.toString(), "marseille");
File configFile = tempFolder.newFile();
properties.store(new FileOutputStream(configFile), null);
System.setProperty(ConfigurableProducer.METRICS_CONF, configFile.getAbsolutePath());

configurableProducer.init();
String value = configurableProducer.getConfigurationPropertyAsString(injectionPoint);
String value = configurableProducer.getConfigurationPropertyAsString(parameterInjectionPoint);

assertEquals("marseille", value);
}

@Test
public void shouldFetchValueFromSystemProperty() throws Exception {
Properties properties = new Properties();
properties.setProperty(ConfigurationKey.CASSANDRA_KEYSPACE.getExternalForm(), "marseille");
properties.setProperty(ConfigurationKey.CASSANDRA_KEYSPACE.toString(), "marseille");
File configFile = tempFolder.newFile();
properties.store(new FileOutputStream(configFile), null);
System.setProperty(ConfigurableProducer.METRICS_CONF, configFile.getAbsolutePath());

System.setProperty(ConfigurationKey.CASSANDRA_KEYSPACE.getExternalForm(), "mare nostrum");
System.setProperty(ConfigurationKey.CASSANDRA_KEYSPACE.toString(), "mare nostrum");

configurableProducer.init();
String value = configurableProducer.getConfigurationPropertyAsString(injectionPoint);
String value = configurableProducer.getConfigurationPropertyAsString(parameterInjectionPoint);

assertEquals("mare nostrum", value);
}

@Test
public void shouldThrowsIllegalArgumentExceptionIfAnnotationIsMissing() throws Exception {
public void shouldThrowIllegalArgumentExceptionIfAnnotationIsMissing() throws Exception {
// Override default: simulate a missing config property annotation
Annotated annotated = mock(Annotated.class);
when(annotated.getAnnotation(eq(ConfigurationProperty.class))).thenReturn(null);
Expand All @@ -135,4 +139,22 @@ public void shouldThrowsIllegalArgumentExceptionIfAnnotationIsMissing() throws E
expectedException.expectMessage(message);
configurableProducer.getConfigurationPropertyAsString(injectionPoint);
}

@Test
public void shouldInjectTrueWhenFlagIsPresent() throws Exception {
System.setProperty(ConfigurationKey.CASSANDRA_RESETDB.toString(), "");

configurableProducer.init();
String value = configurableProducer.getConfigurationPropertyAsString(flagInjectionPoint);

assertTrue(Boolean.parseBoolean(value));
}

@Test
public void shouldInjectFalseWhenFlagIsAbsent() throws Exception {
configurableProducer.init();
String value = configurableProducer.getConfigurationPropertyAsString(flagInjectionPoint);

assertFalse(Boolean.parseBoolean(value));
}
}

0 comments on commit 83c1d75

Please sign in to comment.