]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11720 Set different memory defaults for EE+
authorMichal Duda <michal.duda@sonarsource.com>
Thu, 16 May 2019 10:36:17 +0000 (12:36 +0200)
committerSonarTech <sonartech@sonarsource.com>
Tue, 21 May 2019 18:21:07 +0000 (20:21 +0200)
17 files changed:
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
server/sonar-main/src/main/java/org/sonar/application/config/AppSettingsLoaderImpl.java
server/sonar-main/src/test/java/org/sonar/application/command/CommandFactoryImplTest.java
server/sonar-main/src/test/java/org/sonar/application/config/AppSettingsLoaderImplTest.java
server/sonar-main/src/test/java/org/sonar/application/config/TestAppSettings.java
server/sonar-main/src/test/java/org/sonar/application/es/EsSettingsTest.java
server/sonar-process/build.gradle
server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java
server/sonar-process/src/test/java/org/sonar/process/ProcessPropertiesTest.java
sonar-application/build.gradle
sonar-application/src/main/assembly/conf/sonar.properties
sonar-application/src/main/java/org/sonar/application/App.java
sonar-core/src/main/java/org/sonar/core/extension/CoreExtension.java
sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsLoader.java
sonar-core/src/main/java/org/sonar/core/extension/ServiceLoaderWrapper.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionTest.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionsLoaderTest.java

index 3494bc34fc69d216b50f2ff3202347ea43f5640c..62ea3f86c442b099acddeea5a4dd6ecb67c5f18a 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.ce.container;
 
+import com.google.common.collect.ImmutableSet;
 import java.io.File;
 import java.io.IOException;
 import java.util.Date;
@@ -38,6 +39,7 @@ import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.System2;
 import org.sonar.ce.CeDistributedInformationImpl;
 import org.sonar.ce.StandaloneCeDistributedInformation;
+import org.sonar.core.extension.ServiceLoaderWrapper;
 import org.sonar.db.DbTester;
 import org.sonar.db.property.PropertyDto;
 import org.sonar.process.ProcessId;
@@ -48,6 +50,7 @@ import org.sonar.server.property.InternalProperties;
 import static java.lang.String.valueOf;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
 import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
 import static org.sonar.process.ProcessProperties.Property.JDBC_PASSWORD;
@@ -68,9 +71,12 @@ public class ComputeEngineContainerImplTest {
   public DbTester db = DbTester.create(System2.INSTANCE);
 
   private ComputeEngineContainerImpl underTest;
+  private ServiceLoaderWrapper serviceLoaderWrapper = mock(ServiceLoaderWrapper.class);
+  private ProcessProperties processProperties = new ProcessProperties(serviceLoaderWrapper);
 
   @Before
   public void setUp() {
+    when(serviceLoaderWrapper.load()).thenReturn(ImmutableSet.of());
     underTest = new ComputeEngineContainerImpl();
     underTest.setComputeEngineStatus(mock(ComputeEngineStatus.class));
   }
@@ -148,7 +154,10 @@ public class ComputeEngineContainerImplTest {
   }
 
   private Properties getProperties() throws IOException {
-    Properties properties = ProcessProperties.defaults();
+    Properties properties = new Properties();
+    Props props = new Props(properties);
+    processProperties.completeDefaults(props);
+    properties = props.rawProperties();
     File homeDir = tempFolder.newFolder();
     File dataDir = new File(homeDir, "data");
     dataDir.mkdirs();
index 92811eb78dab194538a60a3f06ebd92f3cb0fad2..64f5a32c38019b88b992b9980b692381d44db192 100644 (file)
@@ -29,12 +29,13 @@ import java.util.Arrays;
 import java.util.Properties;
 import java.util.function.Consumer;
 import org.slf4j.LoggerFactory;
+import org.sonar.core.extension.ServiceLoaderWrapper;
 import org.sonar.process.ConfigurationUtils;
 import org.sonar.process.NetworkUtilsImpl;
+import org.sonar.process.ProcessProperties;
 import org.sonar.process.Props;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.sonar.process.ProcessProperties.completeDefaults;
 import static org.sonar.process.ProcessProperties.Property.PATH_HOME;
 
 public class AppSettingsLoaderImpl implements AppSettingsLoader {
@@ -42,15 +43,16 @@ public class AppSettingsLoaderImpl implements AppSettingsLoader {
   private final File homeDir;
   private final String[] cliArguments;
   private final Consumer<Props>[] consumers;
+  private final ServiceLoaderWrapper serviceLoaderWrapper;
 
-  public AppSettingsLoaderImpl(String[] cliArguments) {
-    this(cliArguments, detectHomeDir(),
-      new FileSystemSettings(), new JdbcSettings(), new ClusterSettings(NetworkUtilsImpl.INSTANCE));
+  public AppSettingsLoaderImpl(String[] cliArguments, ServiceLoaderWrapper serviceLoaderWrapper) {
+    this(cliArguments, detectHomeDir(), serviceLoaderWrapper, new FileSystemSettings(), new JdbcSettings(), new ClusterSettings(NetworkUtilsImpl.INSTANCE));
   }
 
-  AppSettingsLoaderImpl(String[] cliArguments, File homeDir, Consumer<Props>... consumers) {
+  AppSettingsLoaderImpl(String[] cliArguments, File homeDir, ServiceLoaderWrapper serviceLoaderWrapper, Consumer<Props>... consumers) {
     this.cliArguments = cliArguments;
     this.homeDir = homeDir;
+    this.serviceLoaderWrapper = serviceLoaderWrapper;
     this.consumers = consumers;
   }
 
@@ -69,7 +71,7 @@ public class AppSettingsLoaderImpl implements AppSettingsLoader {
     // supports decryption of values, so it must be used when values
     // are accessed
     Props props = new Props(p);
-    completeDefaults(props);
+    new ProcessProperties(serviceLoaderWrapper).completeDefaults(props);
     Arrays.stream(consumers).forEach(c -> c.accept(props));
 
     return new AppSettingsImpl(props);
index 1f3a059a34c692737ba346e3024446003fb924b2..babc1e2204b44c7a4d0626b62dca5cf356db98e6 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.application.command;
 
 import ch.qos.logback.classic.spi.ILoggingEvent;
+import com.google.common.collect.ImmutableSet;
 import java.io.File;
 import java.io.IOException;
 import java.util.Properties;
@@ -33,6 +34,7 @@ import org.junit.rules.TemporaryFolder;
 import org.mockito.Mockito;
 import org.sonar.application.es.EsInstallation;
 import org.sonar.application.logging.ListAppender;
+import org.sonar.core.extension.ServiceLoaderWrapper;
 import org.sonar.process.ProcessId;
 import org.sonar.process.ProcessProperties;
 import org.sonar.process.Props;
@@ -40,6 +42,7 @@ import org.sonar.process.System2;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.entry;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 public class CommandFactoryImplTest {
@@ -320,7 +323,9 @@ public class CommandFactoryImplTest {
     p.putAll(userProps);
 
     Props props = new Props(p);
-    ProcessProperties.completeDefaults(props);
+    ServiceLoaderWrapper serviceLoaderWrapper = mock(ServiceLoaderWrapper.class);
+    when(serviceLoaderWrapper.load()).thenReturn(ImmutableSet.of());
+    new ProcessProperties(serviceLoaderWrapper).completeDefaults(props);
     return new CommandFactoryImpl(props, tempDir, system2, javaVersion);
   }
 
index 482040aef5d3eb9dc9f80f02c5152ed7a0057830..2aaa3eb458b8ed427f1130561353d5165d8b9d3b 100644 (file)
  */
 package org.sonar.application.config;
 
+import com.google.common.collect.ImmutableSet;
 import java.io.File;
 import org.apache.commons.io.FileUtils;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
+import org.sonar.core.extension.ServiceLoaderWrapper;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.data.MapEntry.entry;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class AppSettingsLoaderImplTest {
 
@@ -36,13 +41,20 @@ public class AppSettingsLoaderImplTest {
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
 
+  private ServiceLoaderWrapper serviceLoaderWrapper = mock(ServiceLoaderWrapper.class);
+
+  @Before
+  public void setup() {
+    when(serviceLoaderWrapper.load()).thenReturn(ImmutableSet.of());
+  }
+
   @Test
   public void load_properties_from_file() throws Exception {
     File homeDir = temp.newFolder();
     File propsFile = new File(homeDir, "conf/sonar.properties");
     FileUtils.write(propsFile, "foo=bar");
 
-    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[0], homeDir);
+    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[0], homeDir, serviceLoaderWrapper);
     AppSettings settings = underTest.load();
 
     assertThat(settings.getProps().rawProperties()).contains(entry("foo", "bar"));
@@ -53,7 +65,7 @@ public class AppSettingsLoaderImplTest {
     File homeDir = temp.newFolder();
     File propsFileAsDir = new File(homeDir, "conf/sonar.properties");
     FileUtils.forceMkdir(propsFileAsDir);
-    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[0], homeDir);
+    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[0], homeDir, serviceLoaderWrapper);
 
     expectedException.expect(IllegalStateException.class);
     expectedException.expectMessage("Cannot open file " + propsFileAsDir.getAbsolutePath());
@@ -65,7 +77,7 @@ public class AppSettingsLoaderImplTest {
   public void file_is_not_loaded_if_it_does_not_exist() throws Exception {
     File homeDir = temp.newFolder();
 
-    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[0], homeDir);
+    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[0], homeDir, serviceLoaderWrapper);
     AppSettings settings = underTest.load();
 
     // no failure, file is ignored
@@ -76,7 +88,7 @@ public class AppSettingsLoaderImplTest {
   public void command_line_arguments_are_included_to_settings() throws Exception {
     File homeDir = temp.newFolder();
 
-    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[] {"-Dsonar.foo=bar", "-Dhello=world"}, homeDir);
+    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[] {"-Dsonar.foo=bar", "-Dhello=world"}, homeDir, serviceLoaderWrapper);
     AppSettings settings = underTest.load();
 
     assertThat(settings.getProps().rawProperties())
@@ -90,7 +102,7 @@ public class AppSettingsLoaderImplTest {
     File propsFile = new File(homeDir, "conf/sonar.properties");
     FileUtils.write(propsFile, "sonar.foo=file");
 
-    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[]{"-Dsonar.foo=cli"}, homeDir);
+    AppSettingsLoaderImpl underTest = new AppSettingsLoaderImpl(new String[] {"-Dsonar.foo=cli"}, homeDir, serviceLoaderWrapper);
     AppSettings settings = underTest.load();
 
     assertThat(settings.getProps().rawProperties()).contains(entry("sonar.foo", "cli"));
@@ -98,7 +110,7 @@ public class AppSettingsLoaderImplTest {
 
   @Test
   public void detectHomeDir_returns_existing_dir() {
-    assertThat(new AppSettingsLoaderImpl(new String[0]).getHomeDir()).exists().isDirectory();
+    assertThat(new AppSettingsLoaderImpl(new String[0], serviceLoaderWrapper).getHomeDir()).exists().isDirectory();
 
   }
 }
index 0f319b2371a32b0d09449dd8b7e242b6ac7c95f8..ad3fcf195dfd7f1e8c7c33c89209d75cd96502a9 100644 (file)
  */
 package org.sonar.application.config;
 
+import com.google.common.collect.ImmutableSet;
 import java.util.Optional;
 import java.util.Properties;
+import org.sonar.core.extension.ServiceLoaderWrapper;
 import org.sonar.process.ProcessProperties;
 import org.sonar.process.Props;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 /**
  * Simple implementation of {@link AppSettings} that loads
  * the default values defined by {@link ProcessProperties}.
@@ -34,7 +39,9 @@ public class TestAppSettings implements AppSettings {
 
   public TestAppSettings() {
     this.props = new Props(new Properties());
-    ProcessProperties.completeDefaults(this.props);
+    ServiceLoaderWrapper serviceLoaderWrapper = mock(ServiceLoaderWrapper.class);
+    when(serviceLoaderWrapper.load()).thenReturn(ImmutableSet.of());
+    new ProcessProperties(serviceLoaderWrapper).completeDefaults(this.props);
   }
 
   public TestAppSettings set(String key, String value) {
index 8d4a0ab1afc0b7fff8c9e22675928e80f540d97e..b859dfd988130ef94703119ab109ea117210d05e 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.application.es;
 
 import ch.qos.logback.classic.spi.ILoggingEvent;
+import com.google.common.collect.ImmutableSet;
 import java.io.File;
 import java.io.IOException;
 import java.util.Map;
@@ -31,6 +32,7 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.application.logging.ListAppender;
+import org.sonar.core.extension.ServiceLoaderWrapper;
 import org.sonar.process.ProcessProperties;
 import org.sonar.process.ProcessProperties.Property;
 import org.sonar.process.Props;
@@ -331,7 +333,9 @@ public class EsSettingsTest {
   private Props minProps(boolean cluster) throws IOException {
     File homeDir = temp.newFolder();
     Props props = new Props(new Properties());
-    ProcessProperties.completeDefaults(props);
+    ServiceLoaderWrapper serviceLoaderWrapper = mock(ServiceLoaderWrapper.class);
+    when(serviceLoaderWrapper.load()).thenReturn(ImmutableSet.of());
+    new ProcessProperties(serviceLoaderWrapper).completeDefaults(props);
     props.set(PATH_HOME.getKey(), homeDir.getAbsolutePath());
     props.set(Property.CLUSTER_ENABLED.getKey(), Boolean.toString(cluster));
     return props;
index 4f6c00cbf540fdd82e08b6bd48b82083df9eee72..d458ec793f025b944d4cd231f0ba19052cc3714e 100644 (file)
@@ -15,7 +15,8 @@ dependencies {
   compile 'com.hazelcast:hazelcast'
   compile 'org.slf4j:jul-to-slf4j'
   compile 'org.slf4j:slf4j-api'
-
+  compile project(':sonar-core')
+  
   compileOnly 'com.google.code.findbugs:jsr305'
   compileOnly 'com.google.protobuf:protobuf-java'
   compileOnly 'org.nanohttpd:nanohttpd'
index cf9dce23bd555e1a5bfae7bdab4ca7adca5f4d50..fd0a8b7dad2f592a87634eda43cc48b33441ba82 100644 (file)
@@ -22,12 +22,18 @@ package org.sonar.process;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
+import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
+import org.sonar.core.extension.CoreExtension;
+import org.sonar.core.extension.ServiceLoaderWrapper;
+
+import static java.lang.String.format;
 
 /**
  * Constants shared by search, web server and app processes.
@@ -35,6 +41,8 @@ import javax.annotation.Nullable;
  */
 public class ProcessProperties {
 
+  private final ServiceLoaderWrapper serviceLoaderWrapper;
+
   public enum Property {
     JDBC_URL("sonar.jdbc.url"),
     JDBC_USERNAME("sonar.jdbc.username", ""),
@@ -151,11 +159,11 @@ public class ProcessProperties {
     }
   }
 
-  private ProcessProperties() {
-    // only static stuff
+  public ProcessProperties(ServiceLoaderWrapper serviceLoaderWrapper) {
+    this.serviceLoaderWrapper = serviceLoaderWrapper;
   }
 
-  public static void completeDefaults(Props props) {
+  public void completeDefaults(Props props) {
     // init string properties
     for (Map.Entry<Object, Object> entry : defaults().entrySet()) {
       props.setDefault(entry.getKey().toString(), entry.getValue().toString());
@@ -164,14 +172,30 @@ public class ProcessProperties {
     fixPortIfZero(props, Property.SEARCH_HOST.getKey(), Property.SEARCH_PORT.getKey());
   }
 
-  public static Properties defaults() {
+  private Properties defaults() {
     Properties defaults = new Properties();
     defaults.putAll(Arrays.stream(Property.values())
       .filter(Property::hasDefaultValue)
       .collect(Collectors.toMap(Property::getKey, Property::getDefaultValue)));
+    defaults.putAll(loadDefaultsFromExtensions());
     return defaults;
   }
 
+  private Map<String, String> loadDefaultsFromExtensions() {
+    Map<String, String> propertyDefaults = new HashMap<>();
+    Set<CoreExtension> extensions = serviceLoaderWrapper.load();
+    for (CoreExtension ext : extensions) {
+      for (Map.Entry<String, String> property : ext.getExtensionProperties().entrySet()) {
+        if (propertyDefaults.put(property.getKey(), property.getValue()) != null) {
+          throw new IllegalStateException(format("Configuration error: property definition named '%s' found in multiple extensions.",
+            property.getKey()));
+        }
+      }
+    }
+
+    return propertyDefaults;
+  }
+
   private static void fixPortIfZero(Props props, String addressPropertyKey, String portPropertyKey) {
     String port = props.value(portPropertyKey);
     if ("0".equals(port)) {
index 24b0f49c8105574376aaaf966557e8451996120c..7a5d004249cc60e18642348a353bb1b0624f5fb3 100644 (file)
  */
 package org.sonar.process;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import java.net.InetAddress;
+import java.util.Map;
 import java.util.Properties;
+import org.junit.Rule;
 import org.junit.Test;
-import org.sonar.test.TestUtils;
+import org.junit.rules.ExpectedException;
+import org.sonar.core.extension.CoreExtension;
+import org.sonar.core.extension.ServiceLoaderWrapper;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class ProcessPropertiesTest {
 
+  private ServiceLoaderWrapper serviceLoaderWrapper = mock(ServiceLoaderWrapper.class);
+  private ProcessProperties processProperties = new ProcessProperties(serviceLoaderWrapper);
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
   @Test
   public void completeDefaults_adds_default_values() {
     Props props = new Props(new Properties());
-    ProcessProperties.completeDefaults(props);
+
+    processProperties.completeDefaults(props);
 
     assertThat(props.value("sonar.search.javaOpts")).contains("-Xmx");
     assertThat(props.valueAsInt("sonar.jdbc.maxActive")).isEqualTo(60);
@@ -44,21 +59,22 @@ public class ProcessPropertiesTest {
     Properties p = new Properties();
     p.setProperty("sonar.jdbc.username", "angela");
     Props props = new Props(p);
-    ProcessProperties.completeDefaults(props);
+
+    processProperties.completeDefaults(props);
 
     assertThat(props.value("sonar.jdbc.username")).isEqualTo("angela");
   }
 
   @Test
-  public void completeDefaults_set_default_elasticsearch_port_and_bind_address() throws Exception{
+  public void completeDefaults_set_default_elasticsearch_port_and_bind_address() throws Exception {
     Properties p = new Properties();
     Props props = new Props(p);
-    ProcessProperties.completeDefaults(props);
+
+    processProperties.completeDefaults(props);
 
     String address = props.value("sonar.search.host");
     assertThat(address).isNotEmpty();
     assertThat(InetAddress.getByName(address).isLoopbackAddress()).isTrue();
-
     assertThat(props.valueAsInt("sonar.search.port")).isEqualTo(9001);
   }
 
@@ -68,12 +84,93 @@ public class ProcessPropertiesTest {
     p.setProperty("sonar.search.port", "0");
     Props props = new Props(p);
 
-    ProcessProperties.completeDefaults(props);
+    processProperties.completeDefaults(props);
+
     assertThat(props.valueAsInt("sonar.search.port")).isGreaterThan(0);
   }
 
   @Test
-  public void private_constructor() {
-    assertThat(TestUtils.hasOnlyPrivateConstructors(ProcessProperties.class)).isTrue();
+  public void defaults_loads_properties_defaults_from_base_and_extensions() {
+    Props p = new Props(new Properties());
+    when(serviceLoaderWrapper.load()).thenReturn(ImmutableSet.of(new FakeExtension1(), new FakeExtension3()));
+
+    processProperties.completeDefaults(p);
+
+    assertThat(p.value("sonar.some.property")).isEqualTo("1");
+    assertThat(p.value("sonar.some.property2")).isEqualTo("455");
+    assertThat(p.value("sonar.some.property4")).isEqualTo("abc");
+    assertThat(p.value("sonar.some.property5")).isEqualTo("def");
+    assertThat(p.value("sonar.some.property5")).isEqualTo("def");
+    assertThat(p.value("sonar.search.port")).isEqualTo("9001");
+  }
+
+  @Test
+  public void defaults_throws_exception_on_same_property_defined_more_than_once_in_extensions() {
+    Props p = new Props(new Properties());
+    when(serviceLoaderWrapper.load()).thenReturn(ImmutableSet.of(new FakeExtension1(), new FakeExtension2()));
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage("Configuration error: property definition named 'sonar.some.property2' found in multiple extensions.");
+
+    processProperties.completeDefaults(p);
+  }
+
+  private class FakeExtension1 implements CoreExtension {
+
+    @Override
+    public String getName() {
+      return "fakeExt1";
+    }
+
+    @Override
+    public void load(Context context) {
+      // do nothing
+    }
+
+    @Override
+    public Map<String, String> getExtensionProperties() {
+      return ImmutableMap.of(
+        "sonar.some.property", "1",
+        "sonar.some.property2", "455");
+    }
+  }
+
+  private class FakeExtension2 implements CoreExtension {
+
+    @Override
+    public String getName() {
+      return "fakeExt2";
+    }
+
+    @Override
+    public void load(Context context) {
+      // do nothing
+    }
+
+    @Override
+    public Map<String, String> getExtensionProperties() {
+      return ImmutableMap.of(
+        "sonar.some.property2", "5435",
+        "sonar.some.property3", "32131");
+    }
+  }
+
+  private class FakeExtension3 implements CoreExtension {
+
+    @Override
+    public String getName() {
+      return "fakeExt3";
+    }
+
+    @Override
+    public void load(Context context) {
+      // do nothing
+    }
+
+    @Override
+    public Map<String, String> getExtensionProperties() {
+      return ImmutableMap.of(
+        "sonar.some.property4", "abc",
+        "sonar.some.property5", "def");
+    }
   }
 }
index a4500fe305db272a0a34b5aed8c3a31868010b28..6ee5c6c54748c0e973c22247a1a56a0be7f0bf6c 100644 (file)
@@ -1,3 +1,5 @@
+import org.apache.tools.ant.filters.ReplaceTokens
+
 sonarqube {
   properties {
     property 'sonar.projectName', "${projectTitle} :: Application"
@@ -92,6 +94,7 @@ task zip(type: Zip, dependsOn: [configurations.compile]) {
 
   into("${archiveDir}/") {
     from file('src/main/assembly')
+      exclude 'conf/sonar.properties'
       exclude 'elasticsearch-patch'
       // elasticsearch script will be replaced by patched version below
       exclude 'elasticsearch/bin/elasticsearch'
@@ -125,6 +128,20 @@ task zip(type: Zip, dependsOn: [configurations.compile]) {
       exclude 'elasticsearch/modules/tribe/**'
       exclude 'elasticsearch/modules/x-pack-*/**'
   }
+
+  into("${archiveDir}/conf/") {
+    from file('src/main/assembly/conf/sonar.properties')
+      filter(ReplaceTokens, tokens: [
+              'searchDefaultHeapSize': '512MB',
+              'searchJavaOpts'       : '-Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError',
+              'ceDefaultHeapSize'    : '512MB',
+              'ceJavaOpts'           : '-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError',
+              'webDefaultHeapSize'   : '512MB',
+              'webJavaOpts'          : '-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError'
+      ])
+  }
+    
+  
   into("${archiveDir}/elasticsearch/") {
     from file('src/main/assembly/elasticsearch-patch')
       include 'bin/elasticsearch'
index 984274da44ef7ac728f41380db7095fde23cc066..301f1b61438fa1673eeb61117a685ec86f362895 100644 (file)
@@ -84,7 +84,7 @@
 
 #--------------------------------------------------------------------------------------------------
 # WEB SERVER
-# Web server is executed in a dedicated Java process. By default heap size is 512Mb.
+# Web server is executed in a dedicated Java process. By default heap size is @webDefaultHeapSize@.
 # Use the following property to customize JVM options.
 #    Recommendations:
 #
@@ -96,7 +96,7 @@
 #    -Djava.security.egd=file:/dev/./urandom is an option to resolve the problem.
 #    See https://wiki.apache.org/tomcat/HowTo/FasterStartUp#Entropy_Source
 #
-#sonar.web.javaOpts=-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError
+#sonar.web.javaOpts=@webJavaOpts@
 
 # Same as previous property, but allows to not repeat all other settings like -Xmx
 #sonar.web.javaAdditionalOpts=
 #--------------------------------------------------------------------------------------------------
 # COMPUTE ENGINE
 # The Compute Engine is responsible for processing background tasks.
-# Compute Engine is executed in a dedicated Java process. Default heap size is 512Mb.
+# Compute Engine is executed in a dedicated Java process. Default heap size is @ceDefaultHeapSize@.
 # Use the following property to customize JVM options.
 #    Recommendations:
 #
 #    is not enabled by default on your environment:
 #    http://docs.oracle.com/javase/8/docs/technotes/guides/vm/server-class.html
 #
-#sonar.ce.javaOpts=-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError
+#sonar.ce.javaOpts=@ceJavaOpts@
 
 # Same as previous property, but allows to not repeat all other settings like -Xmx
 #sonar.ce.javaAdditionalOpts=
 #--------------------------------------------------------------------------------------------------
 # ELASTICSEARCH
 # Elasticsearch is used to facilitate fast and accurate information retrieval.
-# It is executed in a dedicated Java process. Default heap size is 512Mb.
+# It is executed in a dedicated Java process. Default heap size is @searchDefaultHeapSize@.
 #
 # --------------------------------------------------
 # Word of caution for Linux users on 64bits systems
 #
 
 # JVM options of Elasticsearch process
-#sonar.search.javaOpts=-Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError
+#sonar.search.javaOpts=@searchJavaOpts@
 
 # Same as previous property, but allows to not repeat all other settings like -Xmx
 #sonar.search.javaAdditionalOpts=
index d262fe5df9c4de6141526be6ce5fd142788d667d..af29478eeef87f40ee2f5c72c37fefca9a191886 100644 (file)
@@ -33,6 +33,7 @@ import org.sonar.application.process.ProcessLauncher;
 import org.sonar.application.process.ProcessLauncherImpl;
 import org.sonar.application.process.StopRequestWatcher;
 import org.sonar.application.process.StopRequestWatcherImpl;
+import org.sonar.core.extension.ServiceLoaderWrapper;
 import org.sonar.process.System2;
 import org.sonar.process.SystemExit;
 
@@ -50,7 +51,7 @@ public class App {
   }
 
   public void start(String[] cliArguments) throws IOException {
-    AppSettingsLoader settingsLoader = new AppSettingsLoaderImpl(cliArguments);
+    AppSettingsLoader settingsLoader = new AppSettingsLoaderImpl(cliArguments, new ServiceLoaderWrapper());
     AppSettings settings = settingsLoader.load();
     // order is important - logging must be configured before any other components (AppFileSystem, ...)
     AppLogging logging = new AppLogging(settings);
index 16541f4122db51180b42788df301d47d1491c76f..1be315494cfb421f0020ee4403f750f553de70e6 100644 (file)
@@ -19,7 +19,9 @@
  */
 package org.sonar.core.extension;
 
+import com.google.common.collect.ImmutableMap;
 import java.util.Collection;
+import java.util.Map;
 import org.sonar.api.SonarRuntime;
 import org.sonar.api.config.Configuration;
 
@@ -51,4 +53,12 @@ public interface CoreExtension {
   }
 
   void load(Context context);
+
+  /**
+   * Properties with (optionally) default values defined by the extension.
+   * @return map of property names as keys and property default value as values
+   */
+  default Map<String, String> getExtensionProperties() {
+    return ImmutableMap.of();
+  }
 }
index 41866c16a345fef5339562d46286e9dacd112e6a..c571b349fab1899156c5c5d6fb705f7fa94b916f 100644 (file)
@@ -19,9 +19,7 @@
  */
 package org.sonar.core.extension;
 
-import com.google.common.collect.ImmutableSet;
 import java.util.Map;
-import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.sonar.api.utils.log.Logger;
@@ -70,11 +68,4 @@ public class CoreExtensionsLoader {
       "Multiple core extensions declare the following names: %s",
       duplicatedNames.stream().sorted().collect(Collectors.joining(", ")));
   }
-
-  static class ServiceLoaderWrapper {
-    Set<CoreExtension> load(ClassLoader classLoader) {
-      ServiceLoader<CoreExtension> loader = ServiceLoader.load(CoreExtension.class, classLoader);
-      return ImmutableSet.copyOf(loader.iterator());
-    }
-  }
 }
diff --git a/sonar-core/src/main/java/org/sonar/core/extension/ServiceLoaderWrapper.java b/sonar-core/src/main/java/org/sonar/core/extension/ServiceLoaderWrapper.java
new file mode 100644 (file)
index 0000000..f2770a9
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.core.extension;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.ServiceLoader;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+public class ServiceLoaderWrapper {
+  public Set<CoreExtension> load(@Nullable ClassLoader classLoader) {
+    ServiceLoader<CoreExtension> loader = ServiceLoader.load(CoreExtension.class, classLoader);
+    return ImmutableSet.copyOf(loader.iterator());
+  }
+
+  public Set<CoreExtension> load() {
+    return load(null);
+  }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionTest.java b/sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionTest.java
new file mode 100644 (file)
index 0000000..b3acb76
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.core.extension;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CoreExtensionTest {
+
+  private CoreExtension underTest = new CoreExtension() {
+    @Override
+    public String getName() {
+      return "fake";
+    }
+
+    @Override
+    public void load(Context context) {
+      // nothing to do here
+    }
+  };
+
+  @Test
+  public void getExtensionProperties_by_default_does_not_contain_any_overridden_property_defaults() {
+    assertThat(underTest.getExtensionProperties()).isEmpty();
+  }
+}
index eb727ff824903e86b5736751317e4ea57c7be7d5..0fe863d1cdfc0a61a22b58758fd077d3d652c3b8 100644 (file)
@@ -40,7 +40,7 @@ public class CoreExtensionsLoaderTest {
   public ExpectedException expectedException = ExpectedException.none();
 
   private CoreExtensionRepository coreExtensionRepository = mock(CoreExtensionRepository.class);
-  private CoreExtensionsLoader.ServiceLoaderWrapper serviceLoaderWrapper = mock(CoreExtensionsLoader.ServiceLoaderWrapper.class);
+  private ServiceLoaderWrapper serviceLoaderWrapper = mock(ServiceLoaderWrapper.class);
   private CoreExtensionsLoader underTest = new CoreExtensionsLoader(coreExtensionRepository, serviceLoaderWrapper);
 
   @Test