We're using a class called Places
(inspired by Allen Holub's great article on DrDobbs) that resolved our program paths based on the existence of a configuration file, environment variable or neither. In the neither case, we want all paths to default to resources within our package, the .jar. Additionally, we want to force local paths if we are in a testing environment because of the complexity in handling the Lucene Database.
The original code looks like this:
private void resolvePaths() {
for (NewPlaces place : values()) {
List<String> probablePaths = new ArrayList<>();
try {
if (! IS_IN_A_UNIT_TESTING_ENVIRONMENT && !place.forceLocalPaths) {
File configuration = new File("places.properties");
if (configuration.exists()) {
LOGGER.info("The configuration file exists!");
Properties properties = new Properties();
properties.load(new FileInputStream(configuration));
probablePaths.add(place.path.replace("~", properties.getProperty("mediaplatform.home")));
}
if (System.getenv("MEDIAPLATFORM_HOME") != null) {
String environmentValue = System.getenv("MEDIAPLATFORM_HOME");
probablePaths.add(place.path.replace("~", environmentValue));
}
}
} catch (IOException e) {
throw new RuntimeException("The file could not be read.");
}
String localPath = place.path.replace("~/", "");
URL localUrl = Thread.currentThread().getContextClassLoader().getResource(localPath);
if (localUrl != null)
probablePaths.add(localUrl.getPath());
for (String probablePath : probablePaths) {
File file = new File(probablePath);
if (file.isDirectory()) {
LOGGER.info("\t{} -> {}", place.path, probablePath);
place.path = probablePath;
break;
}
}
if (place.path.contains("~"))
throw new RuntimeException("Could not find a proper path for value '" + place.path + "'");
}
}
I thought this function was long and needlessly complex to read, so I tried refactoring it into smaller methods with clearer meanings. The code ended up roughly 30 lines longer. The question is, did I make it simpler, easier to read and maintain, or did I just waste my time?
private void resolvePaths() {
for (NewPlaces place : values()) {
List<String> probablePaths = new ArrayList<>();
if (!IS_IN_A_UNIT_TESTING_ENVIRONMENT)
probablePaths.addAll(findExternalPaths(place.path));
probablePaths.add(resolveLocalPath(place.path));
place.path = findBestPath(probablePaths);
}
}
private String resolveLocalPath(String unresolvedPath) {
String localPath = unresolvedPath.replace("~/", "");
URL localUrl = Thread.currentThread().getContextClassLoader().getResource(localPath);
checkArgument(localUrl != null, "The path '%s' does not exist locally, but it should.", localPath);
return localUrl.getPath();
}
private List<String> findExternalPaths(String unresolvedPath) {
List<String> externalPaths = new ArrayList<>();
Optional<String> configurationPath = resolvePathFromConfiguration(unresolvedPath);
if (configurationPath.isPresent())
externalPaths.add(configurationPath.get());
Optional<String> environmentPath = resolvePathFromEnvironment(unresolvedPath);
if (environmentPath.isPresent())
externalPaths.add(environmentPath.get());
return externalPaths;
}
private Optional<String> resolvePathFromConfiguration(String unresolvedPath) {
File configuration = new File("places.properties");
if (!configuration.exists())
return Optional.absent();
try {
LOGGER.info("The configuration file exists!");
Properties properties = new Properties();
properties.load(new FileInputStream(configuration));
return Optional.of(unresolvedPath.replace("~", properties.getProperty("mediaplatform.home")));
} catch (IOException e) {
throw new RuntimeException("Could not open or access configuration file 'places.properties'");
}
}
private Optional<String> resolvePathFromEnvironment(String unresolvedPath) {
if (System.getenv("MEDIAPLATFORM_HOME") == null)
return Optional.absent();
String environmentValue = System.getenv("MEDIAPLATFORM_HOME");
return Optional.of(unresolvedPath.replace("~", environmentValue));
}
private String findBestPath(List<String> probablePaths) {
for (String probablePath : probablePaths) {
File file = new File(probablePath);
if (file.isDirectory())
return probablePath;
}
throw new RuntimeException("Could not find a proper path!");
}
Additional Info:
NewPlaces is an enum class with the following structure:
public enum NewPlaces {
DATA_STORE_PERSON("~/data_stores/prod/person_store"), DATA_STORE_TERM("~/data_stores/prod/term_store"), DATA_STORE_RESOURCE(
"~/data_stores/prod/resource_store"),
TESTDATA("~/testdata", ForceLocalPaths.TRUE), PROPERTIES("~/properties"), CONFIGURATION("~/configuration", ForceLocalPaths.TRUE);
private enum ForceLocalPaths {
TRUE, FALSE;
}
If a developer tries to access a directory contained in an enum for the first time, then the paths will be resolved via the above semantics:
public File directory() {
if (path.contains("~"))
resolvePaths();
File directory = new File(path);
assert directory.isDirectory() : "The directory does not exist or is not a directory.";
return directory;
}