Skip to content

Commit

Permalink
Merge pull request #80 from cfg4j/adapter-classpath
Browse files Browse the repository at this point in the history
Classpath source
  • Loading branch information
norbertpotocki committed Jun 16, 2015
2 parents 1ab6d5e + 2592da1 commit b7846b3
Show file tree
Hide file tree
Showing 12 changed files with 466 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2015 Norbert Potocki (norbert.potocki@nort.pl)
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cfg4j.source.classpath;

import static java.util.Objects.requireNonNull;

import org.cfg4j.source.ConfigurationSource;
import org.cfg4j.source.context.DefaultEnvironment;
import org.cfg4j.source.context.Environment;
import org.cfg4j.source.context.MissingEnvironmentException;
import org.cfg4j.source.git.ConfigFilesProvider;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
* {@link ConfigurationSource} reading configuration from classpath files.
*/
public class ClasspathConfigurationSource implements ConfigurationSource {

private final ConfigFilesProvider configFilesProvider;

/**
* Construct {@link ConfigurationSource} backed by classpath files. File list should by provided by
* {@link ConfigFilesProvider} and will be treated as relative paths to the environment provided in
* {@link #getConfiguration()} and {@link #getConfiguration(Environment)} calls (see corresponding javadocs
* for detail).
*
* @param configFilesProvider {@link ConfigFilesProvider} supplying a list of configuration files to use
*/
public ClasspathConfigurationSource(ConfigFilesProvider configFilesProvider) {
this.configFilesProvider = requireNonNull(configFilesProvider);
}

/**
* <b>DEPRECATED: Use {@link #getConfiguration(Environment)} with {@link DefaultEnvironment} instead.</b>
* <p>
* Get configuration set from this source in a form of {@link Properties}. Uses default environment which means
* treating paths from {@link ConfigFilesProvider} as absolute paths.
*
* @return configuration set for default environment
* @throws IllegalStateException when unable to fetch configuration
*/
@Override
public Properties getConfiguration() {
return getConfiguration(new DefaultEnvironment());
}

/**
* Get configuration set for a given {@code environment} from this source in a form of {@link Properties}.
* Path provided by {@code environment} is prepended to all file paths from {@link ConfigFilesProvider} used
* at construction time.
*
* @param environment environment to use
* @return configuration set for {@code environment}
* @throws MissingEnvironmentException when requested environment couldn't be found
* @throws IllegalStateException when unable to fetch configuration
*/
@Override
public Properties getConfiguration(Environment environment) {
Properties properties = new Properties();

String pathPrefix = getPrefixFor(environment);

URL url = ClassLoader.getSystemResource(pathPrefix);
if (url == null) {
throw new MissingEnvironmentException("Directory doesn't exist: " + environment.getName());
}

List<File> files = StreamSupport.stream(configFilesProvider.getConfigFiles().spliterator(), false)
.map(file -> new File(pathPrefix + file.getPath()))
.collect(Collectors.toList());

for (File file : files) {
try (InputStream input = ClassLoader.getSystemResourceAsStream(file.getPath())) {

if (input == null) {
throw new IllegalStateException("Unable to load properties from classpath: " + file.getPath());
}

properties.load(input);
} catch (IOException | IllegalArgumentException e) {
throw new IllegalStateException("Unable to load properties from classpath: " + file.getPath(), e);
}
}

return properties;
}

@Override
public void refresh() {
// NOP
}

private String getPrefixFor(Environment environment) {
String path = environment.getName();

if (!path.trim().isEmpty() && !path.endsWith("/")) {
path += "/";
}

if (path.startsWith("/")) {
path = path.substring(1);
}

return path;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class FilesConfigurationSource implements ConfigurationSource {
* {@link #getConfiguration()} and {@link #getConfiguration(Environment)} calls (see corresponding javadocs
* for detail).
*
* @param configFilesProvider
* @param configFilesProvider {@link ConfigFilesProvider} supplying a list of configuration files to use
*/
public FilesConfigurationSource(ConfigFilesProvider configFilesProvider) {
this.configFilesProvider = requireNonNull(configFilesProvider);
Expand Down Expand Up @@ -95,7 +95,7 @@ public Properties getConfiguration(Environment environment) {
for (File file : files) {
try (InputStream input = new FileInputStream(file.getPath())) {
properties.load(input);
} catch (IOException e) {
} catch (IOException | IllegalArgumentException e) {
throw new IllegalStateException("Unable to load properties from application.properties file", e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public Properties getConfiguration(Environment environment) {
for (File file : files) {
try (InputStream input = new FileInputStream(file.getPath())) {
properties.load(input);
} catch (IOException e) {
} catch (IOException | IllegalArgumentException e) {
throw new IllegalStateException("Unable to load properties from application.properties file", e);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright 2015 Norbert Potocki (norbert.potocki@nort.pl)
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cfg4j.source.classpath;

import static org.assertj.core.api.Assertions.assertThat;

import org.assertj.core.data.MapEntry;
import org.cfg4j.source.context.DefaultEnvironment;
import org.cfg4j.source.context.Environment;
import org.cfg4j.source.context.ImmutableEnvironment;
import org.cfg4j.source.context.MissingEnvironmentException;
import org.cfg4j.source.git.ConfigFilesProvider;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;


@RunWith(MockitoJUnitRunner.class)
public class ClasspathConfigurationSourceTest {

@Rule
public ExpectedException expectedException = ExpectedException.none();

private TempConfigurationClasspathRepo classpathRepo;
private ConfigFilesProvider configFilesProvider;
private ClasspathConfigurationSource source;

@Before
public void setUp() throws Exception {
classpathRepo = new TempConfigurationClasspathRepo();

configFilesProvider = () -> Collections.singletonList(
new File("application.properties")
);

source = new ClasspathConfigurationSource(configFilesProvider);
}

@Test
public void getConfigurationShouldReadConfigFromAbsolutePaths() throws Exception {
assertThat(source.getConfiguration()).containsOnlyKeys("some.setting");
}

@Test
public void getConfigurationShouldThrowOnMissingConfigFile() throws Exception {
configFilesProvider = () -> Collections.singletonList(
new File("nonexistent.properties")
);

source = new ClasspathConfigurationSource(configFilesProvider);

expectedException.expect(IllegalStateException.class);
source.getConfiguration();
}

@Test
public void getConfigurationShouldThrowOnMalformedConfigFile() throws Exception {
configFilesProvider = () -> Collections.singletonList(
new File("malformed.properties")
);

source = new ClasspathConfigurationSource(configFilesProvider);

expectedException.expect(IllegalStateException.class);
source.getConfiguration();
}

@Test
public void getConfigurationShouldReadFromGivenFiles() throws Exception {
configFilesProvider = () -> Arrays.asList(
new File("application.properties"),
new File("otherConfig.properties")
);

source = new ClasspathConfigurationSource(configFilesProvider);

assertThat(source.getConfiguration()).containsOnlyKeys("some.setting", "otherConfig.setting");
}

@Test
public void getConfiguration2ShouldReadFromGivenPath() throws Exception {
configFilesProvider = () -> Collections.singletonList(
new File("application.properties")
);

source = new ClasspathConfigurationSource(configFilesProvider);

Environment environment = new ImmutableEnvironment("/otherApplicationConfigs/");

assertThat(source.getConfiguration(environment)).containsOnly(MapEntry.entry("some.setting", "otherAppSetting"));
}

@Test
public void getConfiguration2ShouldReadFromGivenFiles() throws Exception {
configFilesProvider = () -> Arrays.asList(
new File("application.properties"),
new File("otherConfig.properties")
);

source = new ClasspathConfigurationSource(configFilesProvider);
assertThat(source.getConfiguration(new DefaultEnvironment())).containsOnlyKeys("some.setting", "otherConfig.setting");
}

@Test
public void getConfiguration2ShouldThrowOnMissingEnvironment() throws Exception {
expectedException.expect(MissingEnvironmentException.class);
source.getConfiguration(new ImmutableEnvironment("awlerijawoetinawwerlkjn"));
}

@Test
public void getConfiguration2ShouldThrowOnMissingConfigFile() throws Exception {
configFilesProvider = () -> Collections.singletonList(
new File("nonexistent.properties")
);

source = new ClasspathConfigurationSource(configFilesProvider);

expectedException.expect(IllegalStateException.class);
source.getConfiguration(new DefaultEnvironment());
}

@Test
public void getConfiguration2ShouldThrowOnMalformedConfigFile() throws Exception {
configFilesProvider = () -> Collections.singletonList(
new File("malformed.properties")
);

source = new ClasspathConfigurationSource(configFilesProvider);

expectedException.expect(IllegalStateException.class);
source.getConfiguration(new DefaultEnvironment());
}

@Test
public void refreshShouldUpdateGetConfigurationResults() throws Exception {
classpathRepo.changeProperty("application.properties", "some.setting", "changedValue");
source.refresh();

assertThat(source.getConfiguration()).containsOnly(MapEntry.entry("some.setting", "changedValue"));
}

@Test
public void refreshShouldUpdateGetConfiguration2OnDefaultBranch() throws Exception {
classpathRepo.changeProperty("application.properties", "some.setting", "changedValue");
source.refresh();

assertThat(source.getConfiguration(new DefaultEnvironment())).containsOnly(MapEntry.entry("some.setting", "changedValue"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2015 Norbert Potocki (norbert.potocki@nort.pl)
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cfg4j.source.classpath;

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

/**
* Classpath files repository.
*/
public class TempConfigurationClasspathRepo {

/**
* Create repository allowing access to classpath files.
*/
public TempConfigurationClasspathRepo() {
}

/**
* Change the {@code key} property to {@code value} and store it in a {@code propFilePath} properties file.
*
* @param propFilePath relative path to the properties file in this repository
* @param key property key
* @param value property value
* @throws IOException when unable to modify properties file
*/
public void changeProperty(String propFilePath, String key, String value) throws IOException {
writePropertyToFile(propFilePath, key, value);
}

private void writePropertyToFile(String propFilePath, String key, String value) throws IOException {
URL systemResource = ClassLoader.getSystemResource(propFilePath);
OutputStream out = new FileOutputStream(systemResource.getPath());
out.write((key + "=" + value).getBytes());
out.close();
}
}

0 comments on commit b7846b3

Please sign in to comment.