From dfcb1c01a430cf3317d12ff4448fc700b9979d4f Mon Sep 17 00:00:00 2001 From: Michal Duda Date: Thu, 16 May 2019 12:36:17 +0200 Subject: [PATCH] SONAR-11720 Set different memory defaults for EE+ --- .../ComputeEngineContainerImplTest.java | 11 +- .../config/AppSettingsLoaderImpl.java | 14 ++- .../command/CommandFactoryImplTest.java | 7 +- .../config/AppSettingsLoaderImplTest.java | 24 +++- .../application/config/TestAppSettings.java | 9 +- .../sonar/application/es/EsSettingsTest.java | 6 +- server/sonar-process/build.gradle | 3 +- .../org/sonar/process/ProcessProperties.java | 32 ++++- .../sonar/process/ProcessPropertiesTest.java | 115 ++++++++++++++++-- sonar-application/build.gradle | 17 +++ .../src/main/assembly/conf/sonar.properties | 12 +- .../main/java/org/sonar/application/App.java | 3 +- .../sonar/core/extension/CoreExtension.java | 10 ++ .../core/extension/CoreExtensionsLoader.java | 9 -- .../core/extension/ServiceLoaderWrapper.java | 36 ++++++ .../core/extension/CoreExtensionTest.java | 44 +++++++ .../extension/CoreExtensionsLoaderTest.java | 2 +- 17 files changed, 307 insertions(+), 47 deletions(-) create mode 100644 sonar-core/src/main/java/org/sonar/core/extension/ServiceLoaderWrapper.java create mode 100644 sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionTest.java diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index 3494bc34fc6..62ea3f86c44 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -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(); diff --git a/server/sonar-main/src/main/java/org/sonar/application/config/AppSettingsLoaderImpl.java b/server/sonar-main/src/main/java/org/sonar/application/config/AppSettingsLoaderImpl.java index 92811eb78da..64f5a32c380 100644 --- a/server/sonar-main/src/main/java/org/sonar/application/config/AppSettingsLoaderImpl.java +++ b/server/sonar-main/src/main/java/org/sonar/application/config/AppSettingsLoaderImpl.java @@ -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[] 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... consumers) { + AppSettingsLoaderImpl(String[] cliArguments, File homeDir, ServiceLoaderWrapper serviceLoaderWrapper, Consumer... 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); diff --git a/server/sonar-main/src/test/java/org/sonar/application/command/CommandFactoryImplTest.java b/server/sonar-main/src/test/java/org/sonar/application/command/CommandFactoryImplTest.java index 1f3a059a34c..babc1e2204b 100644 --- a/server/sonar-main/src/test/java/org/sonar/application/command/CommandFactoryImplTest.java +++ b/server/sonar-main/src/test/java/org/sonar/application/command/CommandFactoryImplTest.java @@ -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); } diff --git a/server/sonar-main/src/test/java/org/sonar/application/config/AppSettingsLoaderImplTest.java b/server/sonar-main/src/test/java/org/sonar/application/config/AppSettingsLoaderImplTest.java index 482040aef5d..2aaa3eb458b 100644 --- a/server/sonar-main/src/test/java/org/sonar/application/config/AppSettingsLoaderImplTest.java +++ b/server/sonar-main/src/test/java/org/sonar/application/config/AppSettingsLoaderImplTest.java @@ -19,15 +19,20 @@ */ 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(); } } diff --git a/server/sonar-main/src/test/java/org/sonar/application/config/TestAppSettings.java b/server/sonar-main/src/test/java/org/sonar/application/config/TestAppSettings.java index 0f319b2371a..ad3fcf195df 100644 --- a/server/sonar-main/src/test/java/org/sonar/application/config/TestAppSettings.java +++ b/server/sonar-main/src/test/java/org/sonar/application/config/TestAppSettings.java @@ -19,11 +19,16 @@ */ 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) { diff --git a/server/sonar-main/src/test/java/org/sonar/application/es/EsSettingsTest.java b/server/sonar-main/src/test/java/org/sonar/application/es/EsSettingsTest.java index 8d4a0ab1afc..b859dfd9881 100644 --- a/server/sonar-main/src/test/java/org/sonar/application/es/EsSettingsTest.java +++ b/server/sonar-main/src/test/java/org/sonar/application/es/EsSettingsTest.java @@ -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; diff --git a/server/sonar-process/build.gradle b/server/sonar-process/build.gradle index 4f6c00cbf54..d458ec793f0 100644 --- a/server/sonar-process/build.gradle +++ b/server/sonar-process/build.gradle @@ -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' diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java index cf9dce23bd5..fd0a8b7dad2 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java +++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java @@ -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 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 loadDefaultsFromExtensions() { + Map propertyDefaults = new HashMap<>(); + Set extensions = serviceLoaderWrapper.load(); + for (CoreExtension ext : extensions) { + for (Map.Entry 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)) { diff --git a/server/sonar-process/src/test/java/org/sonar/process/ProcessPropertiesTest.java b/server/sonar-process/src/test/java/org/sonar/process/ProcessPropertiesTest.java index 24b0f49c810..7a5d004249c 100644 --- a/server/sonar-process/src/test/java/org/sonar/process/ProcessPropertiesTest.java +++ b/server/sonar-process/src/test/java/org/sonar/process/ProcessPropertiesTest.java @@ -19,19 +19,34 @@ */ 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 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 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 getExtensionProperties() { + return ImmutableMap.of( + "sonar.some.property4", "abc", + "sonar.some.property5", "def"); + } } } diff --git a/sonar-application/build.gradle b/sonar-application/build.gradle index a4500fe305d..6ee5c6c5474 100644 --- a/sonar-application/build.gradle +++ b/sonar-application/build.gradle @@ -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' diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties index 984274da44e..301f1b61438 100644 --- a/sonar-application/src/main/assembly/conf/sonar.properties +++ b/sonar-application/src/main/assembly/conf/sonar.properties @@ -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= @@ -238,7 +238,7 @@ #-------------------------------------------------------------------------------------------------- # 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: # @@ -246,7 +246,7 @@ # 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= @@ -255,7 +255,7 @@ #-------------------------------------------------------------------------------------------------- # 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 @@ -269,7 +269,7 @@ # # 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= diff --git a/sonar-application/src/main/java/org/sonar/application/App.java b/sonar-application/src/main/java/org/sonar/application/App.java index d262fe5df9c..af29478eeef 100644 --- a/sonar-application/src/main/java/org/sonar/application/App.java +++ b/sonar-application/src/main/java/org/sonar/application/App.java @@ -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); diff --git a/sonar-core/src/main/java/org/sonar/core/extension/CoreExtension.java b/sonar-core/src/main/java/org/sonar/core/extension/CoreExtension.java index 16541f4122d..1be315494cf 100644 --- a/sonar-core/src/main/java/org/sonar/core/extension/CoreExtension.java +++ b/sonar-core/src/main/java/org/sonar/core/extension/CoreExtension.java @@ -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 getExtensionProperties() { + return ImmutableMap.of(); + } } diff --git a/sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsLoader.java b/sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsLoader.java index 41866c16a34..c571b349fab 100644 --- a/sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsLoader.java +++ b/sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsLoader.java @@ -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 load(ClassLoader classLoader) { - ServiceLoader 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 index 00000000000..f2770a9d68c --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/extension/ServiceLoaderWrapper.java @@ -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 load(@Nullable ClassLoader classLoader) { + ServiceLoader loader = ServiceLoader.load(CoreExtension.class, classLoader); + return ImmutableSet.copyOf(loader.iterator()); + } + + public Set 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 index 00000000000..b3acb7608da --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionTest.java @@ -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(); + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionsLoaderTest.java b/sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionsLoaderTest.java index eb727ff8249..0fe863d1cdf 100644 --- a/sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionsLoaderTest.java +++ b/sonar-core/src/test/java/org/sonar/core/extension/CoreExtensionsLoaderTest.java @@ -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 -- 2.39.5