Skip to content

ORCH-531 Extract configuration to its own module for reusability #246

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
36 changes: 30 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<modules>
<module>echo</module>
<module>sonar-orchestrator</module>
<module>sonar-orchestrator-config</module>
<module>sonar-orchestrator-junit4</module>
<module>sonar-orchestrator-junit5</module>
</modules>
Expand All @@ -41,8 +42,6 @@
<properties>
<license.name>GNU LGPL v3</license.name>
<version.surefire.plugin>3.5.2</version.surefire.plugin>
<commonsIO.version>2.7</commonsIO.version>
<commonsLang.version>2.6</commonsLang.version>
<commonsExec.version>1.3</commonsExec.version>
<guava.version>18.0</guava.version>
<junit.version>4.13.2</junit.version>
Expand All @@ -54,9 +53,7 @@

<!-- used for deployment to SonarSource Artifactory -->
<gitRepositoryName>orchestrator</gitRepositoryName>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.release>8</maven.compiler.release>
<maven.compiler.release>11</maven.compiler.release>
</properties>

<dependencyManagement>
Expand All @@ -66,16 +63,43 @@
<artifactId>jsr305</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.18.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.14.0</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.13.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.9.1</version>
<version>3.27.3</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.16.0</version>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
<version>3.13.1</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
Expand Down
47 changes: 47 additions & 0 deletions sonar-orchestrator-config/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonarsource.orchestrator</groupId>
<artifactId>orchestrator-parent</artifactId>
<version>5.6.2-SNAPSHOT</version>
</parent>
<artifactId>sonar-orchestrator-config</artifactId>
<name>Orchestrator Configuration</name>

<dependencies>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Orchestrator
* Orchestrator Configuration
* Copyright (C) 2011-2025 SonarSource SA
* mailto:info AT sonarsource DOT com
*
Expand All @@ -19,12 +19,11 @@
*/
package com.sonar.orchestrator.config;

import com.sonar.orchestrator.locator.ArtifactoryFactory;
import com.sonar.orchestrator.locator.FileLocation;
import com.sonar.orchestrator.locator.Locators;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -35,62 +34,45 @@
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.commons.text.StringSubstitutor;

import static com.sonar.orchestrator.util.OrchestratorUtils.checkState;
import static com.sonar.orchestrator.util.OrchestratorUtils.defaultIfNull;
import static com.sonar.orchestrator.util.OrchestratorUtils.isEmpty;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static org.apache.commons.io.FileUtils.getUserDirectory;
import static org.apache.commons.lang3.ObjectUtils.getIfNull;
import static org.apache.commons.lang3.StringUtils.isEmpty;

public class Configuration {
private static final String ENV_SHARED_DIR = "SONAR_IT_SOURCES";
private static final String PROP_SHARED_DIR = "orchestrator.it_sources";

private final Map<String, String> props;
private final FileSystem fileSystem;
private final Locators locators;

private Configuration(File homeDir, Map<String, String> props) {
private Configuration(Path homeDir, Map<String, String> props) {
this.props = Collections.unmodifiableMap(new HashMap<>(props));
this.fileSystem = new FileSystem(homeDir, this);
this.locators = new Locators(this.fileSystem, ArtifactoryFactory.createArtifactory(this));
}

public FileSystem fileSystem() {
return fileSystem;
public static Configuration createEnv() {
return builder().addEnvVariables().addSystemProperties().build();
}

public Locators locators() {
return locators;
public static Configuration create(Properties properties) {
return builder().addProperties(properties).build();
}

/**
* File located in the shared directory defined by the system property orchestrator.it_sources or environment variable SONAR_IT_SOURCES.
* Example : getFileLocationOfShared("javascript/performancing/pom.xml")
*/
public FileLocation getFileLocationOfShared(String relativePath) {
// try to read it_sources
// in the System.getProperties
// in the prop file (from orchestrator.properties file)
// in the environment variable
String rootPath;
rootPath = System.getProperty(PROP_SHARED_DIR);
if (rootPath == null) {
rootPath = props.get(PROP_SHARED_DIR);
}
if (rootPath == null) {
rootPath = System.getenv(ENV_SHARED_DIR);
}
requireNonNull(rootPath, format("Property '%s' or environment variable '%s' is missing", PROP_SHARED_DIR, ENV_SHARED_DIR));
public static Configuration create(Map<String, String> properties) {
return builder().addProperties(properties).build();
}

public static Configuration create() {
return builder().build();
}

File rootDir = new File(rootPath);
checkState(rootDir.isDirectory() && rootDir.exists(),
"Please check the definition of it_sources (%s or %s) because the directory does not exist: %s", PROP_SHARED_DIR, ENV_SHARED_DIR, rootDir);
public static Builder builder() {
return new Builder();
}

return FileLocation.of(new File(rootDir, relativePath));
public FileSystem fileSystem() {
return fileSystem;
}

public String getString(String key) {
Expand All @@ -102,7 +84,7 @@ public String getString(String key) {
}

public String getString(String key, @Nullable String defaultValue) {
return defaultIfNull(props.get(key), defaultValue);
return getIfNull(props.get(key), defaultValue);
}

@CheckForNull
Expand Down Expand Up @@ -133,32 +115,24 @@ public Map<String, String> asMap() {
return props;
}

public static Configuration createEnv() {
return builder().addEnvVariables().addSystemProperties().build();
}

public static Configuration create(Properties properties) {
return builder().addProperties(properties).build();
}

public static Configuration create(Map<String, String> properties) {
return builder().addProperties(properties).build();
}

public static Configuration create() {
return builder().build();
}

public static Builder builder() {
return new Builder();
}

public static final class Builder {
private Map<String, String> props = new HashMap<>();
private final Map<String, String> props = new HashMap<>();

private Builder() {
}

private static Map<String, String> interpolateProperties(Map<String, String> map) {
Map<String, String> copy = new HashMap<>();
for (Map.Entry<String, String> entry : map.entrySet()) {
copy.put(entry.getKey(), interpolate(entry.getValue(), map));
}
return copy;
}

private static String interpolate(String prop, Map<String, String> with) {
return StringSubstitutor.replace(prop, with, "${", "}");
}

public Builder addConfiguration(Configuration c) {
return addMap(c.asMap());
}
Expand All @@ -180,9 +154,7 @@ public Builder addProperties(Properties p) {
}

public Builder addProperties(Map<String, String> p) {
for (Map.Entry<String, String> entry : p.entrySet()) {
props.put(entry.getKey(), entry.getValue());
}
props.putAll(p);
return this;
}

Expand All @@ -198,30 +170,30 @@ public Builder setProperty(String key, @Nullable String value) {
return this;
}

public Builder setProperty(String key, File file) {
props.put(key, file.getAbsolutePath());
public Builder setProperty(String key, Path file) {
props.put(key, file.toAbsolutePath().toString());
return this;
}

private File loadProperties() {
File homeDir = Stream.of(
private Path loadProperties() {
Path homeDir = Stream.of(
props.get("orchestrator.home"),
props.get("ORCHESTRATOR_HOME"),
props.get("SONAR_USER_HOME"))
.filter(s -> !isEmpty(s))
.findFirst()
.map(File::new)
.orElse(new File(getUserDirectory(), ".sonar/orchestrator"));
.map(Path::of)
.orElse(getUserDirectory().toPath().resolve(".sonar/orchestrator"));

String configUrl = Stream.of(
props.get("orchestrator.configUrl"),
props.get("ORCHESTRATOR_CONFIG_URL"))
.filter(s -> !isEmpty(s))
.findFirst()
.orElseGet(() -> {
File file = new File(homeDir, "orchestrator.properties");
Path file = homeDir.resolve("orchestrator.properties");
try {
return file.exists() ? file.getAbsoluteFile().toURI().toURL().toString() : null;
return Files.exists(file) ? file.toAbsolutePath().toUri().toURL().toString() : null;
} catch (MalformedURLException e) {
throw new IllegalStateException("Unable to read configuration file", e);
}
Expand All @@ -245,20 +217,8 @@ private File loadProperties() {
return homeDir;
}

private static Map<String, String> interpolateProperties(Map<String, String> map) {
Map<String, String> copy = new HashMap<>();
for (Map.Entry<String, String> entry : map.entrySet()) {
copy.put(entry.getKey(), interpolate(entry.getValue(), map));
}
return copy;
}

private static String interpolate(String prop, Map<String, String> with) {
return StrSubstitutor.replace(prop, with, "${", "}");
}

public Configuration build() {
File homeDir = loadProperties();
Path homeDir = loadProperties();
Map<String, String> interpolatedProperties = interpolateProperties(props);
return new Configuration(homeDir, interpolatedProperties);
}
Expand Down
Loading