Browse Source

SONAR-12108 Support Java 11 RTE

tags/7.8
Duarte Meneses 5 years ago
parent
commit
74cbdd7c04

+ 9
- 3
server/sonar-main/src/main/java/org/sonar/application/command/CeJvmOptions.java View File

@@ -24,15 +24,21 @@ import java.util.LinkedHashMap;
import java.util.Map;

public class CeJvmOptions extends JvmOptions<CeJvmOptions> {
public CeJvmOptions(File tmpDir) {
super(mandatoryOptions(tmpDir));

public CeJvmOptions(File tmpDir, JavaVersion javaVersion) {
super(mandatoryOptions(tmpDir, javaVersion));
}

private static Map<String, String> mandatoryOptions(File tmpDir) {
private static Map<String, String> mandatoryOptions(File tmpDir, JavaVersion javaVersion) {
Map<String, String> res = new LinkedHashMap<>(3);
res.put("-Djava.awt.headless=", "true");
res.put("-Dfile.encoding=", "UTF-8");
res.put("-Djava.io.tmpdir=", tmpDir.getAbsolutePath());

if (javaVersion.isAtLeastJava11()) {
// avoid illegal reflective access operations done by MyBatis
res.put("--add-opens=java.base/java.util=ALL-UNNAMED", "");
}
return res;
}
}

+ 5
- 3
server/sonar-main/src/main/java/org/sonar/application/command/CommandFactoryImpl.java View File

@@ -68,11 +68,13 @@ public class CommandFactoryImpl implements CommandFactory {
private final Props props;
private final File tempDir;
private final System2 system2;
private final JavaVersion javaVersion;

public CommandFactoryImpl(Props props, File tempDir, System2 system2) {
public CommandFactoryImpl(Props props, File tempDir, System2 system2, JavaVersion javaVersion) {
this.props = props;
this.tempDir = tempDir;
this.system2 = system2;
this.javaVersion = javaVersion;
String javaToolOptions = system2.getenv(ENV_VAR_JAVA_TOOL_OPTIONS);
if (javaToolOptions != null && !javaToolOptions.trim().isEmpty()) {
LoggerFactory.getLogger(CommandFactoryImpl.class)
@@ -149,7 +151,7 @@ public class CommandFactoryImpl implements CommandFactory {
public JavaCommand createWebCommand(boolean leader) {
File homeDir = props.nonNullValueAsFile(PATH_HOME.getKey());

WebJvmOptions jvmOptions = new WebJvmOptions(tempDir)
WebJvmOptions jvmOptions = new WebJvmOptions(tempDir, javaVersion)
.addFromMandatoryProperty(props, WEB_JAVA_OPTS.getKey())
.addFromMandatoryProperty(props, WEB_JAVA_ADDITIONAL_OPTS.getKey());
addProxyJvmOptions(jvmOptions);
@@ -175,7 +177,7 @@ public class CommandFactoryImpl implements CommandFactory {
public JavaCommand createCeCommand() {
File homeDir = props.nonNullValueAsFile(PATH_HOME.getKey());

CeJvmOptions jvmOptions = new CeJvmOptions(tempDir)
CeJvmOptions jvmOptions = new CeJvmOptions(tempDir, javaVersion)
.addFromMandatoryProperty(props, CE_JAVA_OPTS.getKey())
.addFromMandatoryProperty(props, CE_JAVA_ADDITIONAL_OPTS.getKey());
addProxyJvmOptions(jvmOptions);

+ 33
- 0
server/sonar-main/src/main/java/org/sonar/application/command/JavaVersion.java View File

@@ -0,0 +1,33 @@
/*
* 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.application.command;

public class JavaVersion {
public static final JavaVersion INSTANCE = new JavaVersion();

public boolean isAtLeastJava11() {
try {
String.class.getMethod("isBlank");
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
}

+ 13
- 3
server/sonar-main/src/main/java/org/sonar/application/command/WebJvmOptions.java View File

@@ -24,15 +24,25 @@ import java.util.LinkedHashMap;
import java.util.Map;

public class WebJvmOptions extends JvmOptions<WebJvmOptions> {
public WebJvmOptions(File tmpDir) {
super(mandatoryOptions(tmpDir));
public WebJvmOptions(File tmpDir, JavaVersion javaVersion) {
super(mandatoryOptions(tmpDir, javaVersion));
}

private static Map<String, String> mandatoryOptions(File tmpDir) {
private static Map<String, String> mandatoryOptions(File tmpDir, JavaVersion javaVersion) {
Map<String, String> res = new LinkedHashMap<>(3);
res.put("-Djava.awt.headless=", "true");
res.put("-Dfile.encoding=", "UTF-8");
res.put("-Djava.io.tmpdir=", tmpDir.getAbsolutePath());

if (javaVersion.isAtLeastJava11()) {
// avoid illegal reflective access operations done by MyBatis
res.put("--add-opens=java.base/java.util=ALL-UNNAMED", "");

// avoid illegal reflective access operations done by Tomcat
res.put("--add-opens=java.base/java.lang=ALL-UNNAMED", "");
res.put("--add-opens=java.base/java.io=ALL-UNNAMED", "");
res.put("--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED", "");
}
return res;
}
}

+ 24
- 4
server/sonar-main/src/test/java/org/sonar/application/command/CeJvmOptionsTest.java View File

@@ -21,22 +21,42 @@ package org.sonar.application.command;

import java.io.File;
import java.io.IOException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class CeJvmOptionsTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();

@Test
public void constructor_sets_mandatory_JVM_options() throws IOException {
File tmpDir = temporaryFolder.newFolder();
CeJvmOptions underTest = new CeJvmOptions(tmpDir);
private File tmpDir;
private JavaVersion javaVersion = mock(JavaVersion.class);
private CeJvmOptions underTest;

@Before
public void setUp() throws IOException {
tmpDir = temporaryFolder.newFolder();
}

@Test
public void constructor_sets_mandatory_JVM_options_before_java11() {
when(javaVersion.isAtLeastJava11()).thenReturn(false);
underTest = new CeJvmOptions(tmpDir, javaVersion);
assertThat(underTest.getAll()).containsExactly(
"-Djava.awt.headless=true", "-Dfile.encoding=UTF-8", "-Djava.io.tmpdir=" + tmpDir.getAbsolutePath());
}

@Test
public void constructor_sets_mandatory_JVM_options_for_java11() {
when(javaVersion.isAtLeastJava11()).thenReturn(true);
underTest = new CeJvmOptions(tmpDir, javaVersion);
assertThat(underTest.getAll()).containsExactly(
"-Djava.awt.headless=true", "-Dfile.encoding=UTF-8", "-Djava.io.tmpdir=" + tmpDir.getAbsolutePath(),
"--add-opens=java.base/java.util=ALL-UNNAMED");
}
}

+ 7
- 12
server/sonar-main/src/test/java/org/sonar/application/command/CommandFactoryImplTest.java View File

@@ -32,15 +32,14 @@ import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.sonar.application.es.EsInstallation;
import org.sonar.application.logging.ListAppender;
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.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

public class CommandFactoryImplTest {
@@ -50,6 +49,8 @@ public class CommandFactoryImplTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();

private System2 system2 = Mockito.mock(System2.class);
private JavaVersion javaVersion = Mockito.mock(JavaVersion.class);
private File homeDir;
private File tempDir;
private File logsDir;
@@ -71,22 +72,19 @@ public class CommandFactoryImplTest {

@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);
new CommandFactoryImpl(new Props(new Properties()), tempDir, system2, javaVersion);

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);
new CommandFactoryImpl(new Props(new Properties()), tempDir, system2, javaVersion);

assertThat(listAppender.getLogs())
.extracting(ILoggingEvent::getMessage)
@@ -97,11 +95,10 @@ public class CommandFactoryImplTest {

@Test
public void constructor_logs_warning_if_env_variable_ES_JAVA_OPTS_is_set() {
System2 system2 = Mockito.mock(System2.class);
when(system2.getenv("ES_JAVA_OPTS")).thenReturn("xyz");
attachMemoryAppenderToLoggerOf(CommandFactoryImpl.class);

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

assertThat(listAppender.getLogs())
.extracting(ILoggingEvent::getMessage)
@@ -120,7 +117,6 @@ public class CommandFactoryImplTest {

@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();

@@ -157,7 +153,6 @@ public class CommandFactoryImplTest {

@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();

@@ -326,7 +321,7 @@ public class CommandFactoryImplTest {

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

private <T> void attachMemoryAppenderToLoggerOf(Class<T> loggerClass) {

+ 27
- 4
server/sonar-main/src/test/java/org/sonar/application/command/WebJvmOptionsTest.java View File

@@ -21,23 +21,46 @@ package org.sonar.application.command;

import java.io.File;
import java.io.IOException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class WebJvmOptionsTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();

@Test
public void constructor_sets_mandatory_JVM_options() throws IOException {
File tmpDir = temporaryFolder.newFolder();
WebJvmOptions underTest = new WebJvmOptions(tmpDir);
private File tmpDir;
private JavaVersion javaVersion = mock(JavaVersion.class);
private WebJvmOptions underTest;

@Before
public void setUp() throws IOException {
tmpDir = temporaryFolder.newFolder();
}

@Test
public void constructor_sets_mandatory_JVM_options_before_java11() {
when(javaVersion.isAtLeastJava11()).thenReturn(false);
underTest = new WebJvmOptions(tmpDir, javaVersion);
assertThat(underTest.getAll()).containsExactly(
"-Djava.awt.headless=true", "-Dfile.encoding=UTF-8", "-Djava.io.tmpdir=" + tmpDir.getAbsolutePath());
}

@Test
public void constructor_sets_mandatory_JVM_options_for_java11() {
when(javaVersion.isAtLeastJava11()).thenReturn(true);
underTest = new WebJvmOptions(tmpDir, javaVersion);
assertThat(underTest.getAll()).containsExactly(
"-Djava.awt.headless=true", "-Dfile.encoding=UTF-8", "-Djava.io.tmpdir=" + tmpDir.getAbsolutePath(),
"--add-opens=java.base/java.util=ALL-UNNAMED",
"--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.io=ALL-UNNAMED",
"--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED");
}

}

+ 22
- 2
sonar-application/src/main/java/org/sonar/application/App.java View File

@@ -20,8 +20,12 @@
package org.sonar.application;

import java.io.IOException;
import org.slf4j.LoggerFactory;
import org.sonar.api.SonarEdition;
import org.sonar.api.internal.MetadataLoader;
import org.sonar.application.command.CommandFactory;
import org.sonar.application.command.CommandFactoryImpl;
import org.sonar.application.command.JavaVersion;
import org.sonar.application.config.AppSettings;
import org.sonar.application.config.AppSettingsLoader;
import org.sonar.application.config.AppSettingsLoaderImpl;
@@ -38,8 +42,13 @@ import static org.sonar.process.ProcessProperties.Property.CLUSTER_NAME;
public class App {

private final SystemExit systemExit = new SystemExit();
private final JavaVersion javaVersion;
private StopRequestWatcher stopRequestWatcher;

public App(JavaVersion javaVersion) {
this.javaVersion = javaVersion;
}

public void start(String[] cliArguments) throws IOException {
AppSettingsLoader settingsLoader = new AppSettingsLoaderImpl(cliArguments);
AppSettings settings = settingsLoader.load();
@@ -47,13 +56,14 @@ public class App {
AppLogging logging = new AppLogging(settings);
logging.configure();
AppFileSystem fileSystem = new AppFileSystem(settings);
checkJavaVersion();

try (AppState appState = new AppStateFactory(settings).create()) {
appState.registerSonarQubeVersion(getSonarqubeVersion());
appState.registerClusterName(settings.getProps().nonNullValue(CLUSTER_NAME.getKey()));
AppReloader appReloader = new AppReloaderImpl(settingsLoader, fileSystem, appState, logging);
fileSystem.reset();
CommandFactory commandFactory = new CommandFactoryImpl(settings.getProps(), fileSystem.getTempDir(), System2.INSTANCE);
CommandFactory commandFactory = new CommandFactoryImpl(settings.getProps(), fileSystem.getTempDir(), System2.INSTANCE, JavaVersion.INSTANCE);

try (ProcessLauncher processLauncher = new ProcessLauncherImpl(fileSystem.getTempDir())) {
Scheduler scheduler = new SchedulerImpl(settings, appReloader, commandFactory, processLauncher, appState);
@@ -74,8 +84,18 @@ public class App {
systemExit.exit(0);
}

private void checkJavaVersion() {
if (MetadataLoader.loadEdition(org.sonar.api.utils.System2.INSTANCE) == SonarEdition.SONARCLOUD) {
return;
}

if (!javaVersion.isAtLeastJava11()) {
LoggerFactory.getLogger(this.getClass()).warn("SonarQube will require Java 11+ starting on next version");
}
}

public static void main(String... args) throws IOException {
new App().start(args);
new App(JavaVersion.INSTANCE).start(args);
}

private class ShutdownHook extends Thread {

+ 51
- 7
sonar-duplications/src/test/java/org/sonar/duplications/java/JavaStatementBuilderTest.java View File

@@ -19,13 +19,6 @@
*/
package org.sonar.duplications.java;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.sonar.duplications.DuplicationsTestUtil;
import org.sonar.duplications.statement.Statement;
import org.sonar.duplications.statement.StatementChunker;
import org.sonar.duplications.token.TokenChunker;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -33,6 +26,12 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.sonar.duplications.DuplicationsTestUtil;
import org.sonar.duplications.statement.Statement;
import org.sonar.duplications.statement.StatementChunker;
import org.sonar.duplications.token.TokenChunker;

import static org.assertj.core.api.Assertions.assertThat;

@@ -318,6 +317,51 @@ public class JavaStatementBuilderTest {
assertThat(statements.get(1).getValue()).isEqualTo("something()");
}

/**
* Java 8.
*/
@Test
public void shouldHandleLambda() {
List<Statement> statements;
statements = chunk("List<String> result = lines.stream().filter(line -> !\"mkyong\".equals(line)).collect(Collectors.toList());");
assertThat(statements.size()).isEqualTo(1);
assertThat(statements).extracting(Statement::getValue).containsExactly("List<String>result=lines.stream().filter(line->!$CHARS.equals(line)).collect(Collectors.toList())");

statements = chunk("items.forEach((k,v)->{System.out.println(\"Item : \" + k + \" Count : \" + v); if(\"E\".equals(k)) { System.out.println(\"Hello E\");}});");
assertThat(statements.size()).isEqualTo(5);
assertThat(statements).extracting(Statement::getValue)
.containsExactly("items.forEach((k,v)->",
"System.out.println($CHARS+k+$CHARS+v)",
"if($CHARS.equals(k))",
"System.out.println($CHARS)",
")");
}

/**
* Java 9.
*/
@Test
public void shouldHandleModuleInfo() {
List<Statement> statements;
statements = chunk("module com.application.infra { requires com.application.domain; exports com.application.infra.api; }");
assertThat(statements.size()).isEqualTo(3);
assertThat(statements).extracting(Statement::getValue)
.containsExactly("modulecom.application.infra",
"requirescom.application.domain",
"exportscom.application.infra.api");
}

/**
* Java 11.
*/
@Test
public void shouldHandleVar() {
List<Statement> statements;
statements = chunk("IFunc f = (@NonNull var x, final var y) -> Foo.foo(x, y);");
assertThat(statements.size()).isEqualTo(1);
assertThat(statements).extracting(Statement::getValue).containsExactly("IFuncf=(@NonNullvarx,finalvary)->Foo.foo(x,y)");
}

@Test
public void realExamples() {
assertThat(chunk(DuplicationsTestUtil.findFile("/java/MessageResources.java")).size()).isGreaterThan(0);

+ 6
- 13
sonar-plugin-api/src/main/java/org/sonar/api/utils/System2.java View File

@@ -21,14 +21,13 @@ package org.sonar.api.utils;

import java.net.URL;
import java.time.Clock;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
import javax.annotation.CheckForNull;
import org.apache.commons.lang.SystemUtils;
import org.sonar.api.scanner.ScannerSide;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.scanner.ScannerSide;
import org.sonar.api.server.ServerSide;

/**
@@ -61,7 +60,6 @@ import org.sonar.api.server.ServerSide;
* Note that the name System2 was chosen to not conflict with {@link java.lang.System}.
* <br>
* An instance is available in IoC container since 4.3.
*
* Since 6.4 you can also inject {@link Clock} instead of {@link System2} if you are only interested by date/time operations
*
* @since 4.2
@@ -98,6 +96,7 @@ public class System2 {

/**
* Shortcut for {@code System{@link #setProperty(String, String)}}
*
* @since 6.4
*/
public System2 setProperty(String key, String value) {
@@ -129,6 +128,7 @@ public class System2 {

/**
* True if Java 7 or Java 8 runtime environment
*
* @since 4.3
* @deprecated in 6.4. Java 8+ is required, so this method always returns {@code true}.
*/
@@ -142,24 +142,16 @@ public class System2 {
}

/**
* @deprecated in 5.2. Please use {@link #now()}
*/
@Deprecated
public Date newDate() {
return new Date();
}

/**
* @since 5.1
* @return the JVM's default time zone
* @since 5.1
*/
public TimeZone getDefaultTimeZone() {
return TimeZone.getDefault();
}

/**
* @since 5.5
* @see Class#getResource(String)
* @since 5.5
*/
public URL getResource(String name) {
return getClass().getResource(name);
@@ -167,6 +159,7 @@ public class System2 {

/**
* Closes the object and throws an {@link java.lang.IllegalStateException} on error.
*
* @since 5.1
*/
public void close(AutoCloseable closeable) {

+ 12
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java View File

@@ -22,10 +22,10 @@ package org.sonar.scanner.bootstrap;
import java.time.Clock;
import java.util.List;
import java.util.Map;
import org.sonar.api.SonarEdition;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
import org.sonar.api.Plugin;
import org.sonar.api.SonarEdition;
import org.sonar.api.SonarQubeSide;
import org.sonar.api.SonarQubeVersion;
import org.sonar.api.internal.MetadataLoader;
@@ -78,9 +78,20 @@ public class GlobalContainer extends ComponentContainer {
addBootstrapComponents();
}

private static void checkJavaVersion() {
try {
String.class.getMethod("isBlank");
} catch (NoSuchMethodException e) {
LOG.warn("SonarQube scanners will require Java 11+ starting on next version");
}
}

private void addBootstrapComponents() {
Version apiVersion = MetadataLoader.loadVersion(System2.INSTANCE);
SonarEdition edition = MetadataLoader.loadEdition(System2.INSTANCE);
if (edition != SonarEdition.SONARCLOUD) {
checkJavaVersion();
}
LOG.debug("{} {}", edition.getLabel(), apiVersion);
add(
// plugins

Loading…
Cancel
Save