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;
}
}
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)
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);
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);
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
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;
}
}
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");
+ }
}
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 {
@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;
@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)
@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)
@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();
@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();
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) {
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");
+ }
+
}
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;
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();
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);
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 {
*/
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;
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;
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);
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;
/**
* 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
/**
* Shortcut for {@code System{@link #setProperty(String, String)}}
+ *
* @since 6.4
*/
public System2 setProperty(String key, String value) {
/**
* 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}.
*/
}
/**
- * @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);
/**
* Closes the object and throws an {@link java.lang.IllegalStateException} on error.
+ *
* @since 5.1
*/
public void close(AutoCloseable closeable) {
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;
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