[platform] migrates JdkVersionDetector service to new Java version parser

This commit is contained in:
Roman Shevchenko
2017-12-21 15:19:20 +01:00
parent ff6c68ec82
commit 9ad2d140d1
4 changed files with 95 additions and 120 deletions
@@ -1,21 +1,10 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package org.jetbrains.jps.model.java;
import com.intellij.openapi.util.Bitness;
import com.intellij.util.lang.JavaVersion;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.service.JpsServiceManager;
@@ -30,18 +19,21 @@ public abstract class JdkVersionDetector {
return JpsServiceManager.getInstance().getService(JdkVersionDetector.class);
}
/**
* Returns java version for JDK located at {@code homePath} in format like<br>
* <tt>java version "1.8.0_40"</tt><br>
* by running '<tt>java -version</tt>' command
* @param homePath path to JDK home directory
* @return version string of {@code null} if version cannot be determined
*/
/** @deprecated use {@link #detectJdkVersionInfo(String)} (to be removed in IDEA 2019) */
@Nullable
public abstract String detectJdkVersion(@NotNull String homePath);
@SuppressWarnings("deprecation")
public String detectJdkVersion(@NotNull String homePath) {
JdkVersionInfo info = detectJdkVersionInfo(homePath);
return info != null ? info.getVersion() : null;
}
/** @deprecated use {@link #detectJdkVersionInfo(String, ActionRunner)} (to be removed in IDEA 2019) */
@Nullable
public abstract String detectJdkVersion(@NotNull String homePath, @NotNull ActionRunner actionRunner);
@SuppressWarnings("deprecation")
public String detectJdkVersion(@NotNull String homePath, @NotNull ActionRunner runner) {
JdkVersionInfo info = detectJdkVersionInfo(homePath, runner);
return info != null ? info.getVersion() : null;
}
@Nullable
public abstract JdkVersionInfo detectJdkVersionInfo(@NotNull String homePath);
@@ -55,22 +47,27 @@ public abstract class JdkVersionDetector {
}
public static final class JdkVersionInfo {
private final String myVersion;
private final Bitness myBitness;
public final JavaVersion version;
public final Bitness bitness;
public JdkVersionInfo(@NotNull String version, @NotNull Bitness bitness) {
myVersion = version;
myBitness = bitness;
public JdkVersionInfo(@NotNull JavaVersion version, @NotNull Bitness bitness) {
this.version = version;
this.bitness = bitness;
}
@NotNull
@Override
public String toString() {
return version + " " + bitness;
}
/** @deprecated use {@link #version} (to be removed in IDEA 2019) */
public String getVersion() {
return myVersion;
return "java version \"" + version + '"';
}
@NotNull
/** @deprecated use {@link #bitness} (to be removed in IDEA 2019) */
public Bitness getBitness() {
return myBitness;
return bitness;
}
}
}
@@ -1,17 +1,5 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package org.jetbrains.jps.model.java.impl;
@@ -21,6 +9,7 @@ import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.util.io.BaseOutputReader;
import com.intellij.util.lang.JavaVersion;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.java.JdkVersionDetector;
@@ -44,67 +33,58 @@ public class JdkVersionDetectorImpl extends JdkVersionDetector {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.impl.SdkVersionUtil");
private static final ActionRunner ACTION_RUNNER = r -> SharedThreadPool.getInstance().executeOnPooledThread(r);
@Override
@Nullable
public String detectJdkVersion(@NotNull String homePath) {
return detectJdkVersion(homePath, ACTION_RUNNER);
}
@Nullable
public String detectJdkVersion(@NotNull String homePath, @NotNull ActionRunner runner) {
File rtFile = new File(homePath, "jre/lib/rt.jar");
if (rtFile.isFile()) {
try (JarFile rtJar = new JarFile(rtFile, false)) {
Manifest manifest = rtJar.getManifest();
if (manifest != null) {
String version = manifest.getMainAttributes().getValue("Implementation-Version");
if (version != null) {
return "java version \"" + version + "\"";
}
}
}
catch (IOException e) {
LOG.info(e);
}
}
JdkVersionInfo info = detectJdkVersionInfo(homePath, runner);
return info != null ? info.getVersion() : null;
}
@Override
public JdkVersionInfo detectJdkVersionInfo(@NotNull String homePath) {
return detectJdkVersionInfo(homePath, ACTION_RUNNER);
}
@Nullable
@Override
public JdkVersionInfo detectJdkVersionInfo(@NotNull String homePath, @NotNull ActionRunner runner) {
// Java 1.7+
File releaseFile = new File(homePath, "release");
if (releaseFile.isFile()) {
try {
Properties p = new Properties();
p.load(new FileInputStream(releaseFile));
String version = p.getProperty("JAVA_FULL_VERSION", p.getProperty("JAVA_VERSION"));
if (version != null) {
version = StringUtil.unquoteString(version);
int i = version.indexOf('+');
if (i > 0) {
version = version.substring(0, i);
}
Properties p = new Properties();
try (FileInputStream stream = new FileInputStream(releaseFile)) {
p.load(stream);
String versionString = p.getProperty("JAVA_FULL_VERSION", p.getProperty("JAVA_VERSION"));
if (versionString != null) {
JavaVersion version = JavaVersion.parse(versionString);
String arch = StringUtil.unquoteString(p.getProperty("OS_ARCH", ""));
boolean x64 = "x86_64".equals(arch) || "amd64".equals(arch);
return new JdkVersionInfo("java version \"" + version + "\"", x64 ? Bitness.x64 : Bitness.x32);
return new JdkVersionInfo(version, x64 ? Bitness.x64 : Bitness.x32);
}
}
catch (IOException e) {
LOG.info(e);
catch (IOException | IllegalArgumentException e) {
LOG.info(releaseFile.getPath(), e);
}
}
String javaExe = homePath + File.separator + "bin" + File.separator + (SystemInfo.isWindows ? "java.exe" : "java");
if (new File(javaExe).canExecute()) {
// Java 1.2 - 1.8
File rtFile = new File(homePath, "jre/lib/rt.jar");
if (rtFile.isFile()) {
try (JarFile rtJar = new JarFile(rtFile, false)) {
Manifest manifest = rtJar.getManifest();
if (manifest != null) {
String versionString = manifest.getMainAttributes().getValue("Implementation-Version");
if (versionString != null) {
JavaVersion version = JavaVersion.parse(versionString);
boolean x64 = new File(rtFile.getParent(), "amd64").isDirectory();
return new JdkVersionInfo(version, x64 ? Bitness.x64 : Bitness.x32);
}
}
}
catch (IOException | IllegalArgumentException e) {
LOG.info(rtFile.getPath(), e);
}
}
// last resort
File javaExe = new File(homePath, "bin/" + (SystemInfo.isWindows ? "java.exe" : "java"));
if (javaExe.canExecute()) {
try {
Process process = new ProcessBuilder(javaExe, "-version").redirectErrorStream(true).start();
Process process = new ProcessBuilder(javaExe.getPath(), "-version").redirectErrorStream(true).start();
VersionOutputReader reader = new VersionOutputReader(process.getInputStream(), runner);
try {
reader.waitFor();
@@ -113,10 +93,17 @@ public class JdkVersionDetectorImpl extends JdkVersionDetector {
LOG.info(e);
process.destroy();
}
return reader.getVersionInfo();
if (!reader.myLines.isEmpty()) {
JavaVersion base = JavaVersion.parse(reader.myLines.get(0));
JavaVersion rt = JavaVersion.tryParse(reader.myLines.size() > 2 ? reader.myLines.get(1) : null);
JavaVersion version = rt != null && rt.feature == base.feature && rt.minor == base.minor ? rt : base;
boolean x64 = reader.myLines.stream().anyMatch(s -> s.contains("64-Bit") || s.contains("x86_64") || s.contains("amd64"));
return new JdkVersionInfo(version, x64 ? Bitness.x64 : Bitness.x32);
}
}
catch (IOException e) {
LOG.info(e);
catch (IOException | IllegalArgumentException e) {
LOG.info(javaExe.getPath(), e);
}
}
@@ -150,27 +137,7 @@ public class JdkVersionDetectorImpl extends JdkVersionDetector {
@Override
protected void onTextAvailable(@NotNull String text) {
myLines.add(text);
LOG.debug("text: " + text);
}
@Nullable
public JdkVersionInfo getVersionInfo() {
String version = null;
Bitness arch = Bitness.x32;
for (String line : myLines) {
if (line.contains("version")) {
if (version == null) {
version = line;
}
}
else if (line.contains("64-Bit") || line.contains("x86_64") || line.contains("amd64")) {
arch = Bitness.x64;
}
}
LOG.debug("Returning " + version + " " + arch);
return version != null ? new JdkVersionInfo(version, arch) : null;
LOG.trace("text: " + text);
}
}
}
@@ -1,4 +1,6 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
/*
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package org.jetbrains.jps.model
import com.intellij.openapi.util.Bitness
@@ -9,15 +11,9 @@ import org.junit.Test
class JdkVersionDetectorTest {
@Test fun detectJdkVersion() {
val jdkHome = System.getProperty("java.home")
val jdkVersion = JdkVersionDetector.getInstance().detectJdkVersion(jdkHome)
assertThat(jdkVersion).contains("\"${SystemInfo.JAVA_VERSION.removeSuffix("-ea")}\"")
}
@Test fun detectJdkVersionInfo() {
val jdkHome = System.getProperty("java.home")
val jdkVersion = JdkVersionDetector.getInstance().detectJdkVersionInfo(jdkHome)
assertThat(jdkVersion?.version).contains("\"${SystemInfo.JAVA_VERSION.removeSuffix("-ea")}\"")
assertThat(jdkVersion?.version.toString()).startsWith(SystemInfo.JAVA_VERSION)
assertThat(jdkVersion?.bitness).isEqualTo(if (SystemInfo.is64Bit) Bitness.x64 else Bitness.x32)
}
}
@@ -5,6 +5,7 @@ package com.intellij.util.lang;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -144,7 +145,7 @@ public final class JavaVersion {
*/
public static @NotNull JavaVersion parse(@NotNull String versionString) throws IllegalArgumentException {
// trimming
String str = versionString;
String str = versionString.trim();
if (str.contains("Runtime Environment")) {
int p = str.indexOf("(build ");
if (p > 0) str = str.substring(p);
@@ -226,4 +227,18 @@ public final class JavaVersion {
private static boolean startsWithWord(String s, String word) {
return s.startsWith(word) && (s.length() == word.length() || !Character.isLetterOrDigit(s.charAt(word.length())));
}
/**
* A safe version of {@link #parse(String)} - returns {@code null} if can't parse a version string.
*/
public static @Nullable JavaVersion tryParse(String versionString) {
if (versionString != null) {
try {
return parse(versionString);
}
catch (IllegalArgumentException ignored) { }
}
return null;
}
}