package com.sforce.ws; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaVersion implements Comparable { private static final String JAVA_VERSION_REGEX = "^(?[1-9][0-9]*)(?:\\.(?[0-9]*))?(?:\\.(?\\d+))?(?:\\.(?[0-9]*))?(?:_(?[0-9]*))?(.*)"; private static final Pattern JAVA_VERSION_PATTERN = Pattern.compile("^(?[1-9][0-9]*)(?:\\.(?[0-9]*))?(?:\\.(?\\d+))?(?:\\.(?[0-9]*))?(?:_(?[0-9]*))?(.*)"); protected static final String VERSION_PARSE_ERROR = "Cannot parse provided version string"; protected static final String INVALID_FEATURE_VALUE_ERROR = "Invalid feature value in the provided version string"; public static final String JAVA_VERSION_PROPERTY = "java.version"; public static final String JDK_UPDATE_MESSAGE = "Please update the client to a JDK version that includes a fix for JDK-8209178. See https://trailblazer.salesforce.com/issues_view?id=a1p4V000002JfxSQAS for more details."; private static final JavaVersion JAVA_VERSION_14_0_0_0 = Builder.newBuilder().setFeature(Integer.valueOf(14)).build(); private static final JavaVersion JAVA_VERSION_13_0_2 = Builder.newBuilder().setFeature(Integer.valueOf(13)).setInterim(0).setUpdate(2).build(); private static final JavaVersion JAVA_VERSION_11_0_6 = Builder.newBuilder().setFeature(Integer.valueOf(11)).setInterim(0).setUpdate(6).build(); private static final JavaVersion JAVA_VERSION_1_8_0_0_321 = Builder.newBuilder().setFeature(Integer.valueOf(1)).setInterim(8).setUpdate(0).setPatch(0).setBuild(321).build(); private final int feature; private final int interim; private final int update; private final int patch; private final int build; private JavaVersion(Integer feature, int interim, int update, int patch, int build) { if (feature == null || feature.equals(Integer.valueOf(0))) throw new IllegalArgumentException("Invalid feature value in the provided version string"); this.feature = feature.intValue(); this.interim = interim; this.update = update; this.patch = patch; this.build = build; } public static JavaVersion parse(String version) { if (version == null) throw new NullPointerException("Cannot parse provided version string"); try { Matcher matcher = JAVA_VERSION_PATTERN.matcher(version); if (matcher.matches()) return buildVersionFromNewPattern(matcher); } catch (NumberFormatException numberFormatException) {} throw new IllegalArgumentException("Cannot parse provided version string"); } private static JavaVersion buildVersionFromNewPattern(Matcher matcher) { Integer feature = getValue(matcher, "FEATURE", null); int interim = getValue(matcher, "INTERIM", Integer.valueOf(0)).intValue(); int update = getValue(matcher, "UPDATE", Integer.valueOf(0)).intValue(); int patch = getValue(matcher, "PATCH", Integer.valueOf(0)).intValue(); int build = getValue(matcher, "BUILD", Integer.valueOf(0)).intValue(); return (new Builder()) .setFeature(feature) .setInterim(interim) .setUpdate(update) .setPatch(patch) .setBuild(build) .build(); } private static Integer getValue(Matcher matcher, String group, Integer defaultValue) { String value = matcher.group(group); if (value == null) return defaultValue; return Integer.valueOf(Integer.parseInt(value)); } public static boolean javaVersionHasABug(String runtimeVersion) { try { JavaVersion javaVersion = parse(runtimeVersion); if (javaVersion.compareTo(JAVA_VERSION_14_0_0_0) >= 0) return false; if (javaVersion.getFeature().intValue() == 13 && javaVersion.compareTo(JAVA_VERSION_13_0_2) >= 0) return false; if (javaVersion.getFeature().intValue() == 11 && javaVersion.compareTo(JAVA_VERSION_11_0_6) >= 0) return false; if (javaVersion.getFeature().intValue() == 1 && javaVersion.getInterim() == 8 && javaVersion.compareTo(JAVA_VERSION_1_8_0_0_321) >= 0) return false; return true; } catch (Exception e) { return false; } } public int compareTo(JavaVersion that) { if (that == null) throw new NullPointerException(); if (!Objects.equals(getFeature(), that.getFeature())) return Integer.compare(getFeature().intValue(), that.getFeature().intValue()); if (!Objects.equals(Integer.valueOf(getInterim()), Integer.valueOf(that.getInterim()))) return Integer.compare(getInterim(), that.getInterim()); if (!Objects.equals(Integer.valueOf(getUpdate()), Integer.valueOf(that.getUpdate()))) return Integer.compare(getUpdate(), that.getUpdate()); if (!Objects.equals(Integer.valueOf(getPatch()), Integer.valueOf(that.getPatch()))) return Integer.compare(getPatch(), that.getPatch()); if (!Objects.equals(Integer.valueOf(getBuild()), Integer.valueOf(that.getBuild()))) return Integer.compare(getBuild(), that.getBuild()); return 0; } public Integer getFeature() { return Integer.valueOf(this.feature); } public int getInterim() { return this.interim; } public int getUpdate() { return this.update; } public int getPatch() { return this.patch; } public int getBuild() { return this.build; } public String toString() { return "Version{feature=" + this.feature + ", interim=" + this.interim + ", update=" + this.update + ", patch=" + this.patch + ", build=" + this.build + '}'; } public static class Builder { private Integer feature; private int interim; private int update; private int patch; private int build; public static Builder newBuilder() { return new Builder(); } public Builder setFeature(Integer feature) { this.feature = feature; return this; } public Builder setInterim(int interim) { this.interim = interim; return this; } public Builder setUpdate(int update) { this.update = update; return this; } public Builder setPatch(int patch) { this.patch = patch; return this; } public Builder setBuild(int build) { this.build = build; return this; } public JavaVersion build() { return new JavaVersion(this.feature, this.interim, this.update, this.patch, this.build, null); } } }