which allows to remove from a command an env variable defined for current process
and remove support for null value in AbstractCommand#setEnvVariable
ProcessBuilder processBuilder = processBuilderSupplier.get();
processBuilder.command(commands);
processBuilder.directory(javaCommand.getWorkDir());
- processBuilder.environment().putAll(javaCommand.getEnvVariables());
+ Map<String, String> environment = processBuilder.environment();
+ environment.putAll(javaCommand.getEnvVariables());
+ javaCommand.getSuppressedEnvVariables().forEach(environment::remove);
processBuilder.redirectErrorStream(true);
return processBuilder;
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import java.util.Map;
+
+/**
+ * An interface allowing to wrap around static call to {@link System} class.
+ */
+public interface System2 {
+ System2 INSTANCE = new System2() {
+ @Override
+ public Map<String, String> getenv() {
+ return System.getenv();
+ }
+
+ @Override
+ public String getenv(String name) {
+ return System.getenv(name);
+ }
+ };
+
+ /**
+ * Proxy to {@link System#getenv()}
+ */
+ Map<String, String> getenv();
+
+ /**
+ * Proxy to {@link System#getenv(String)}.
+ */
+ String getenv(String name);
+}
import java.io.File;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.process.ProcessId;
+import org.sonar.process.System2;
import static java.util.Objects.requireNonNull;
private final ProcessId id;
// program arguments
private final Map<String, String> arguments = new LinkedHashMap<>();
- private final Map<String, String> envVariables = new HashMap<>(System.getenv());
+ private final Map<String, String> envVariables;
+ private final Set<String> suppressedEnvVariables = new HashSet<>();
private final File workDir;
- protected AbstractCommand(ProcessId id, File workDir) {
+ protected AbstractCommand(ProcessId id, File workDir, System2 system2) {
this.id = requireNonNull(id, "ProcessId can't be null");
this.workDir = requireNonNull(workDir, "workDir can't be null");
+ this.envVariables = new HashMap<>(system2.getenv());
}
public ProcessId getProcessId() {
return envVariables;
}
- public T setEnvVariable(String key, @Nullable String value) {
- if (value == null) {
- envVariables.remove(key);
- } else {
- envVariables.put(key, value);
- }
+ public Set<String> getSuppressedEnvVariables() {
+ return suppressedEnvVariables;
+ }
+
+ public T suppressEnvVariable(String key) {
+ requireNonNull(key, "key can't be null");
+ suppressedEnvVariables.add(key);
+ envVariables.remove(key);
+ return castThis();
+ }
+
+ public T setEnvVariable(String key, String value) {
+ envVariables.put(
+ requireNonNull(key, "key can't be null"),
+ requireNonNull(value, "value can't be null"));
return castThis();
}
}
import java.util.List;
import java.util.Properties;
import org.sonar.process.ProcessId;
+import org.sonar.process.System2;
import org.sonar.process.es.EsFileSystem;
import org.sonar.process.es.EsYmlSettings;
import org.sonar.process.jmvoptions.EsJvmOptions;
private EsYmlSettings esYmlSettings;
public EsCommand(ProcessId id, File workDir) {
- super(id, workDir);
+ super(id, workDir, System2.INSTANCE);
}
public EsFileSystem getFileSystem() {
import java.util.ArrayList;
import java.util.List;
import org.sonar.process.ProcessId;
+import org.sonar.process.System2;
import org.sonar.process.jmvoptions.JvmOptions;
public class JavaCommand<T extends JvmOptions> extends AbstractCommand<JavaCommand<T>> {
private final List<String> classpath = new ArrayList<>();
public JavaCommand(ProcessId id, File workDir) {
- super(id, workDir);
+ super(id, workDir, System2.INSTANCE);
}
public JvmOptions<T> getJvmOptions() {
", classpath=" + classpath +
", arguments=" + getArguments() +
", envVariables=" + getEnvVariables() +
+ ", suppressedEnvVariables=" + getSuppressedEnvVariables() +
'}';
}
}
import java.io.File;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Properties;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
import org.sonar.process.ProcessId;
+import org.sonar.process.System2;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
public class AbstractCommandTest {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("ProcessId can't be null");
- new AbstractCommand<AbstractCommand>(null, temp.newFolder()) {
+ new AbstractCommand<AbstractCommand>(null, temp.newFolder(), System2.INSTANCE) {
};
}
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("workDir can't be null");
- new AbstractCommand<AbstractCommand>(ProcessId.WEB_SERVER, null) {
+ new AbstractCommand<AbstractCommand>(ProcessId.WEB_SERVER, null, System2.INSTANCE) {
};
}
@Test
public void test_command_with_complete_information() throws Exception {
File workDir = temp.newFolder();
- AbstractCommand command = new AbstractCommand(ProcessId.ELASTICSEARCH, workDir) {
+ AbstractCommand command = new AbstractCommand(ProcessId.ELASTICSEARCH, workDir, System2.INSTANCE) {
};
assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
}
+ @Test
+ public void setEnvVariable_fails_with_NPE_if_key_is_null() throws IOException {
+ File workDir = temp.newFolder();
+ AbstractCommand underTest = new AbstractCommand(ProcessId.ELASTICSEARCH, workDir, System2.INSTANCE) {
+
+ };
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("key can't be null");
+
+ underTest.setEnvVariable(null, randomAlphanumeric(30));
+ }
+
+ @Test
+ public void setEnvVariable_fails_with_NPE_if_value_is_null() throws IOException {
+ File workDir = temp.newFolder();
+ AbstractCommand underTest = new AbstractCommand(ProcessId.ELASTICSEARCH, workDir, System2.INSTANCE) {
+
+ };
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("value can't be null");
+
+ underTest.setEnvVariable(randomAlphanumeric(30), null);
+ }
+
+ @Test
+ public void constructor_puts_System_getEnv_into_map_of_env_variables() throws IOException {
+ File workDir = temp.newFolder();
+ System2 system2 = Mockito.mock(System2.class);
+ Map<String, String> env = IntStream.range(0, 1 + new Random().nextInt(99)).mapToObj(String::valueOf).collect(Collectors.toMap(i -> "key" + i, j -> "value" + j));
+ when(system2.getenv()).thenReturn(env);
+ AbstractCommand underTest = new AbstractCommand(ProcessId.ELASTICSEARCH, workDir, system2) {
+
+ };
+
+ assertThat(underTest.getEnvVariables()).isEqualTo(env);
+ }
+
+ @Test
+ public void suppressEnvVariable_remove_existing_env_variable_and_add_variable_to_set_of_suppressed_variables() throws IOException {
+ File workDir = temp.newFolder();
+ System2 system2 = Mockito.mock(System2.class);
+ Map<String, String> env = new HashMap<>();
+ String key1 = randomAlphanumeric(3);
+ env.put(key1, randomAlphanumeric(9));
+ when(system2.getenv()).thenReturn(env);
+ AbstractCommand underTest = new AbstractCommand(ProcessId.ELASTICSEARCH, workDir, system2) {
+
+ };
+
+ underTest.suppressEnvVariable(key1);
+
+ assertThat(underTest.getEnvVariables()).doesNotContainKey(key1);
+ assertThat(underTest.getSuppressedEnvVariables()).containsOnly(key1);
+ }
+
}