org.sonar.application.command.CommandFactoryImplTest.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.application.command.CommandFactoryImplTest.java

Source

/*
 * SonarQube
 * Copyright (C) 2009-2018 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.application.command;

import ch.qos.logback.classic.spi.ILoggingEvent;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
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.application.es.EsInstallation;
import org.sonar.process.ProcessId;
import org.sonar.process.ProcessProperties;
import org.sonar.process.Props;
import org.sonar.process.System2;
import org.sonar.application.logging.ListAppender;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

public class CommandFactoryImplTest {

    @Rule
    public ExpectedException expectedException = ExpectedException.none();
    @Rule
    public TemporaryFolder temp = new TemporaryFolder();

    private File homeDir;
    private File tempDir;
    private File logsDir;
    private ListAppender listAppender;

    @Before
    public void setUp() throws Exception {
        homeDir = temp.newFolder();
        tempDir = temp.newFolder();
        logsDir = temp.newFolder();
    }

    @After
    public void tearDown() {
        if (listAppender != null) {
            ListAppender.detachMemoryAppenderToLoggerOf(CommandFactoryImpl.class, listAppender);
        }
    }

    @Test
    public void constructor_logs_no_warning_if_env_variable_JAVA_TOOL_OPTIONS_is_not_set() {
        System2 system2 = Mockito.mock(System2.class);
        when(system2.getenv(anyString())).thenReturn(null);
        attachMemoryAppenderToLoggerOf(CommandFactoryImpl.class);

        new CommandFactoryImpl(new Props(new Properties()), tempDir, system2);

        assertThat(listAppender.getLogs()).isEmpty();
    }

    @Test
    public void constructor_logs_warning_if_env_variable_JAVA_TOOL_OPTIONS_is_set() {
        System2 system2 = Mockito.mock(System2.class);
        when(system2.getenv("JAVA_TOOL_OPTIONS")).thenReturn("sds");
        attachMemoryAppenderToLoggerOf(CommandFactoryImpl.class);

        new CommandFactoryImpl(new Props(new Properties()), tempDir, system2);

        assertThat(listAppender.getLogs()).extracting(ILoggingEvent::getMessage)
                .containsOnly("JAVA_TOOL_OPTIONS is defined but will be ignored. "
                        + "Use properties sonar.*.javaOpts and/or sonar.*.javaAdditionalOpts in sonar.properties to change SQ JVM processes options");
    }

    @Test
    public void createEsCommand_throws_ISE_if_es_binary_is_not_found() throws Exception {
        expectedException.expect(IllegalStateException.class);
        expectedException.expectMessage("Cannot find elasticsearch binary");

        newFactory(new Properties()).createEsCommand();
    }

    @Test
    public void createEsCommand_for_unix_returns_command_for_default_settings() throws Exception {
        System2 system2 = Mockito.mock(System2.class);
        when(system2.isOsWindows()).thenReturn(false);
        prepareEsFileSystem();

        Properties props = new Properties();
        props.setProperty("sonar.search.host", "localhost");

        AbstractCommand esCommand = newFactory(props, system2).createEsCommand();
        EsInstallation esConfig = esCommand.getEsInstallation();

        assertThat(esCommand).isInstanceOf(EsScriptCommand.class);
        assertThat(esConfig.getClusterName()).isEqualTo("sonarqube");
        assertThat(esConfig.getHost()).isNotEmpty();
        assertThat(esConfig.getPort()).isEqualTo(9001);
        assertThat(esConfig.getEsJvmOptions().getAll())
                // enforced values
                .contains("-XX:+UseConcMarkSweepGC", "-server", "-Dfile.encoding=UTF-8")
                // default settings
                .contains("-Xms512m", "-Xmx512m", "-XX:+HeapDumpOnOutOfMemoryError");
        File esConfDir = new File(tempDir, "conf/es");
        assertThat(esCommand.getEnvVariables())
                .contains(entry("ES_JVM_OPTIONS", new File(esConfDir, "jvm.options").getAbsolutePath()))
                .containsKey("JAVA_HOME");
        assertThat(esConfig.getEsYmlSettings()).isNotNull();

        assertThat(esConfig.getLog4j2Properties())
                .contains(entry("appender.file_es.fileName", new File(logsDir, "es.log").getAbsolutePath()));

        assertThat(esCommand.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
    }

    @Test
    public void createEsCommand_for_windows_returns_command_for_default_settings() throws Exception {
        System2 system2 = Mockito.mock(System2.class);
        when(system2.isOsWindows()).thenReturn(true);
        prepareEsFileSystem();

        Properties props = new Properties();
        props.setProperty("sonar.search.host", "localhost");

        AbstractCommand esCommand = newFactory(props, system2).createEsCommand();
        EsInstallation esConfig = esCommand.getEsInstallation();

        assertThat(esCommand).isInstanceOf(JavaCommand.class);
        assertThat(esConfig.getClusterName()).isEqualTo("sonarqube");
        assertThat(esConfig.getHost()).isNotEmpty();
        assertThat(esConfig.getPort()).isEqualTo(9001);
        assertThat(esConfig.getEsJvmOptions().getAll())
                // enforced values
                .contains("-XX:+UseConcMarkSweepGC", "-server", "-Dfile.encoding=UTF-8")
                // default settings
                .contains("-Xms512m", "-Xmx512m", "-XX:+HeapDumpOnOutOfMemoryError");
        File esConfDir = new File(tempDir, "conf/es");
        assertThat(esCommand.getEnvVariables())
                .contains(entry("ES_JVM_OPTIONS", new File(esConfDir, "jvm.options").getAbsolutePath()))
                .containsKey("JAVA_HOME");
        assertThat(esConfig.getEsYmlSettings()).isNotNull();

        assertThat(esConfig.getLog4j2Properties())
                .contains(entry("appender.file_es.fileName", new File(logsDir, "es.log").getAbsolutePath()));

        assertThat(esCommand.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
    }

    @Test
    public void createEsCommand_returns_command_for_overridden_settings() throws Exception {
        prepareEsFileSystem();

        Properties props = new Properties();
        props.setProperty("sonar.search.host", "localhost");
        props.setProperty("sonar.cluster.name", "foo");
        props.setProperty("sonar.search.port", "1234");
        props.setProperty("sonar.search.javaOpts", "-Xms10G -Xmx10G");

        AbstractCommand esCommand = newFactory(props).createEsCommand();
        EsInstallation esConfig = esCommand.getEsInstallation();

        assertThat(esConfig.getClusterName()).isEqualTo("foo");
        assertThat(esConfig.getPort()).isEqualTo(1234);
        assertThat(esConfig.getEsJvmOptions().getAll())
                // enforced values
                .contains("-XX:+UseConcMarkSweepGC", "-server", "-Dfile.encoding=UTF-8")
                // user settings
                .contains("-Xms10G", "-Xmx10G")
                // default values disabled
                .doesNotContain("-XX:+HeapDumpOnOutOfMemoryError");
    }

    @Test
    public void createWebCommand_returns_command_for_default_settings() throws Exception {
        JavaCommand command = newFactory(new Properties()).createWebCommand(true);

        assertThat(command.getClassName()).isEqualTo("org.sonar.server.app.WebServer");
        assertThat(command.getWorkDir().getAbsolutePath()).isEqualTo(homeDir.getAbsolutePath());
        assertThat(command.getClasspath()).containsExactlyInAnyOrder("./lib/common/*", "./lib/server/*");
        assertThat(command.getJvmOptions().getAll())
                // enforced values
                .contains("-Djava.awt.headless=true", "-Dfile.encoding=UTF-8")
                // default settings
                .contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath(), "-Dfile.encoding=UTF-8")
                .contains("-Xmx512m", "-Xms128m", "-XX:+HeapDumpOnOutOfMemoryError");
        assertThat(command.getProcessId()).isEqualTo(ProcessId.WEB_SERVER);
        assertThat(command.getEnvVariables()).isNotEmpty();
        assertThat(command.getArguments())
                // default settings
                .contains(entry("sonar.web.javaOpts", "-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError"))
                .contains(entry("sonar.cluster.enabled", "false"));

        assertThat(command.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
    }

    @Test
    public void createCeCommand_returns_command_for_default_settings() throws Exception {
        JavaCommand command = newFactory(new Properties()).createCeCommand();

        assertThat(command.getClassName()).isEqualTo("org.sonar.ce.app.CeServer");
        assertThat(command.getWorkDir().getAbsolutePath()).isEqualTo(homeDir.getAbsolutePath());
        assertThat(command.getClasspath()).containsExactlyInAnyOrder("./lib/common/*", "./lib/server/*",
                "./lib/ce/*");
        assertThat(command.getJvmOptions().getAll())
                // enforced values
                .contains("-Djava.awt.headless=true", "-Dfile.encoding=UTF-8")
                // default settings
                .contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath(), "-Dfile.encoding=UTF-8")
                .contains("-Xmx512m", "-Xms128m", "-XX:+HeapDumpOnOutOfMemoryError");
        assertThat(command.getProcessId()).isEqualTo(ProcessId.COMPUTE_ENGINE);
        assertThat(command.getEnvVariables()).isNotEmpty();
        assertThat(command.getArguments())
                // default settings
                .contains(entry("sonar.web.javaOpts", "-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError"))
                .contains(entry("sonar.cluster.enabled", "false"));

        assertThat(command.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
    }

    @Test
    public void createWebCommand_configures_command_with_overridden_settings() throws Exception {
        Properties props = new Properties();
        props.setProperty("sonar.web.port", "1234");
        props.setProperty("sonar.web.javaOpts", "-Xmx10G");
        JavaCommand command = newFactory(props).createWebCommand(true);

        assertThat(command.getJvmOptions().getAll())
                // enforced values
                .contains("-Djava.awt.headless=true", "-Dfile.encoding=UTF-8")
                // default settings
                .contains("-Djava.io.tmpdir=" + tempDir.getAbsolutePath(), "-Dfile.encoding=UTF-8")
                // overridden values
                .contains("-Xmx10G").doesNotContain("-Xms128m", "-XX:+HeapDumpOnOutOfMemoryError");
        assertThat(command.getArguments())
                // default settings
                .contains(entry("sonar.web.javaOpts", "-Xmx10G")).contains(entry("sonar.cluster.enabled", "false"));

        assertThat(command.getSuppressedEnvVariables()).containsOnly("JAVA_TOOL_OPTIONS");
    }

    @Test
    public void createWebCommand_adds_configured_jdbc_driver_to_classpath() throws Exception {
        Properties props = new Properties();
        File driverFile = temp.newFile();
        props.setProperty("sonar.jdbc.driverPath", driverFile.getAbsolutePath());

        JavaCommand command = newFactory(props).createWebCommand(true);

        assertThat(command.getClasspath()).containsExactlyInAnyOrder("./lib/common/*", "./lib/server/*",
                driverFile.getAbsolutePath());
    }

    private void prepareEsFileSystem() throws IOException {
        FileUtils.touch(new File(homeDir, "elasticsearch/bin/elasticsearch"));
        FileUtils.touch(new File(homeDir, "elasticsearch/bin/elasticsearch.bat"));
    }

    private CommandFactoryImpl newFactory(Properties userProps) throws IOException {
        return newFactory(userProps, System2.INSTANCE);
    }

    private CommandFactoryImpl newFactory(Properties userProps, System2 system2) {
        Properties p = new Properties();
        p.setProperty("sonar.path.home", homeDir.getAbsolutePath());
        p.setProperty("sonar.path.temp", tempDir.getAbsolutePath());
        p.setProperty("sonar.path.logs", logsDir.getAbsolutePath());
        p.putAll(userProps);

        Props props = new Props(p);
        ProcessProperties.completeDefaults(props);
        return new CommandFactoryImpl(props, tempDir, system2);
    }

    private <T> void attachMemoryAppenderToLoggerOf(Class<T> loggerClass) {
        this.listAppender = ListAppender.attachMemoryAppenderToLoggerOf(loggerClass);
    }

}