From 9ad2d140d1a64414b125c1dce8d8ee02c83fddc5 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 21 Dec 2017 15:16:55 +0100 Subject: [PATCH] [platform] migrates JdkVersionDetector service to new Java version parser --- .../jps/model/java/JdkVersionDetector.java | 59 ++++---- .../java/impl/JdkVersionDetectorImpl.java | 127 +++++++----------- .../jps/model/JdkVersionDetectorTest.kt | 12 +- .../com/intellij/util/lang/JavaVersion.java | 17 ++- 4 files changed, 95 insertions(+), 120 deletions(-) diff --git a/jps/model-api/src/org/jetbrains/jps/model/java/JdkVersionDetector.java b/jps/model-api/src/org/jetbrains/jps/model/java/JdkVersionDetector.java index 64f60bd390df..8fa6d7c869b4 100644 --- a/jps/model-api/src/org/jetbrains/jps/model/java/JdkVersionDetector.java +++ b/jps/model-api/src/org/jetbrains/jps/model/java/JdkVersionDetector.java @@ -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
- * java version "1.8.0_40"
- * by running 'java -version' 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; } } } \ No newline at end of file diff --git a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JdkVersionDetectorImpl.java b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JdkVersionDetectorImpl.java index ce9ccde97c06..ce0da9bc9a65 100644 --- a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JdkVersionDetectorImpl.java +++ b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JdkVersionDetectorImpl.java @@ -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); } } } \ No newline at end of file diff --git a/jps/model-impl/testSrc/org/jetbrains/jps/model/JdkVersionDetectorTest.kt b/jps/model-impl/testSrc/org/jetbrains/jps/model/JdkVersionDetectorTest.kt index a1e6bca8420b..614dacab18e3 100644 --- a/jps/model-impl/testSrc/org/jetbrains/jps/model/JdkVersionDetectorTest.kt +++ b/jps/model-impl/testSrc/org/jetbrains/jps/model/JdkVersionDetectorTest.kt @@ -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) } } \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/lang/JavaVersion.java b/platform/util/src/com/intellij/util/lang/JavaVersion.java index 05e5bc568048..76d70e2ba3c4 100644 --- a/platform/util/src/com/intellij/util/lang/JavaVersion.java +++ b/platform/util/src/com/intellij/util/lang/JavaVersion.java @@ -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; + } } \ No newline at end of file