blob: 3efc6028254f6ebb15b2121b09e1babe08290409 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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.
*/
package org.apache.geode.test.version;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.function.BiConsumer;
import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.SystemUtils;
/**
* VersionManager loads the class-paths for all of the releases of Geode configured for
* backward-compatibility testing in the geode-core build.gradle file.
* <p>
* Tests may use these versions in launching VMs to run older clients or servers.
*
* see Host.getVM(String, int)
*/
public class VersionManager {
public static final String CURRENT_VERSION = "10240.0.0";
private static VersionManager instance;
protected String loadFailure = "";
/**
* returns the ordinal of the Version of Geode used in this JVM. Use this
* instead of Version.CURRENT or Version.CURRENT_ORDINAL in test code
*/
public short getCurrentVersionOrdinal() {
return geodeCurrentVersionOrdinal;
}
private short geodeCurrentVersionOrdinal = -1;
protected static void init() {
instance = new VersionManager();
final String fileName = "geodeOldVersionClasspaths.txt";
final String installLocations = "geodeOldVersionInstalls.txt";
instance.findVersions(fileName);
instance.findInstalls(installLocations);
instance.establishGeodeVersionOrdinal();
// System.out
// .println("VersionManager has loaded the following classpaths:\n" + instance.classPaths);
}
public static VersionManager getInstance() {
if (instance == null) {
init();
}
return instance;
}
/**
* for unit testing, this creates a VersionManager with paths loaded from the given file, which
* may or may not exist. The instance is not retained
*/
protected static VersionManager getInstance(String classpathsFileName, String installFileName) {
VersionManager result = new VersionManager();
result.findVersions(classpathsFileName);
return result;
}
/**
* classPaths for old versions loaded from a file generated by Gradle
*/
private Map<String, String> classPaths = new HashMap<>();
private List<String> testVersions = new ArrayList<String>(10);
private Map<String, String> installs = new HashMap();
/**
* Test to see if a version string is known to VersionManager. Versions are either CURRENT_VERSION
* or one of the versions returned by VersionManager#getVersions()
*/
public boolean isValidVersion(String version) {
return version.equals(CURRENT_VERSION) || classPaths.containsKey(version);
}
/**
* Returns true if the version is equal to the CURRENT_VERSION constant
*/
public static boolean isCurrentVersion(String version) {
return version.equals(CURRENT_VERSION);
}
/**
* Returns the classPath for the given version, or null if the version is not valid. Use
* CURRENT_VERSION for the version in development.
*/
public String getClasspath(String version) {
return classPaths.get(version);
}
public String getInstall(String version) {
if (!installs.containsKey(version) || (installs.get(version) == null)) {
throw new IllegalArgumentException("unable to locate installation directory for " + version);
}
return installs.get(version);
}
/**
* Returns a list of older versions available for testing, sorted
* from oldest to newest.<br>
* Note: if you need to compare version strings use TestVersion.compare() or TestVersion.equals()
*/
public List<String> getVersions() {
checkForLoadFailure();
return Collections.unmodifiableList(testVersions);
}
/**
* returns a list of testable versions sorted from oldest to newest<br>
* Note: if you need to compare version strings use TestVersion.compare() or TestVersion.equals()
*/
public List<String> getVersionsWithoutCurrent() {
checkForLoadFailure();
List<String> result = new ArrayList<>(testVersions);
result.remove(CURRENT_VERSION);
return result;
}
private void checkForLoadFailure() {
if (loadFailure.length() > 0) {
throw new InternalError(loadFailure);
}
}
private void findVersions(String fileName) {
// this file is created by the gradle task createClasspathsPropertiesFile
readVersionsFile(fileName, (version, path) -> {
Optional<String> parsedVersion = parseVersion(version);
if (parsedVersion.isPresent()) {
if (parsedVersion.get().equals("1.4.0")
&& SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9)) {
// Serialization filtering was added in 140, but the support for them in java 9+ was added
// in 1.5.0. As a result, 1.4.0 servers and clients will fail categorically when run in
// Java 9+ even with the additional libs (jaxb and activation) in the classpath
System.err.println(
"Geode version 1.4.0 is incompatible with Java 9 and higher. Skipping this version.");
} else {
classPaths.put(parsedVersion.get(), path);
testVersions.add(parsedVersion.get());
}
}
});
Collections.sort(testVersions, (v1, v2) -> TestVersion.compare(v1, v2));
}
private void findInstalls(String fileName) {
readVersionsFile(fileName, (version, install) -> {
Optional<String> parsedVersion = parseVersion(version);
if (parsedVersion.isPresent()) {
installs.put(parsedVersion.get(), install);
}
});
}
private Optional<String> parseVersion(String version) {
String parsedVersion = null;
if (version.length() > 0 && Character.isDigit(version.charAt(0))
&& version.length() >= "1.2.3".length()) {
parsedVersion = version;
}
return Optional.ofNullable(parsedVersion);
}
private void readVersionsFile(String fileName, BiConsumer<String, String> consumer) {
Properties props = readPropertiesFile(fileName);
props.forEach((k, v) -> {
consumer.accept(k.toString(), v.toString());
});
}
public Properties readPropertiesFile(String fileName) {
// this file is created by the gradle task createClasspathsPropertiesFile
Properties props = new Properties();
URL url = VersionManager.class.getResource("/" + fileName);
if (url == null) {
loadFailure = "VersionManager: unable to locate " + fileName + " in class-path";
return props;
}
try (InputStream in = VersionManager.class.getResource("/" + fileName).openStream()) {
props.load(in);
} catch (IOException e) {
loadFailure = "VersionManager: unable to read resource " + fileName;
return props;
}
return props;
}
public void establishGeodeVersionOrdinal() {
Class versionClass;
Field currentOrdinalField;
// GEODE's Version class was repackaged when serialization was modularized
try {
versionClass = Class.forName("org.apache.geode.internal.Version");
} catch (ClassNotFoundException e) {
try {
versionClass = Class.forName("org.apache.geode.internal.serialization.Version");
} catch (ClassNotFoundException e2) {
System.out.println("classpath is " + System.getProperty("java.class.path"));
throw new IllegalStateException(
"Unable to locate Version.java in order to establish the product's serialization version",
e2);
}
}
try {
currentOrdinalField = versionClass.getDeclaredField("CURRENT_ORDINAL");
} catch (NoSuchFieldException e) {
throw new IllegalStateException(
"Unable to locate Version.java's CURRENT_ORDINAL field in order to establish the product's serialization version",
e);
}
currentOrdinalField.setAccessible(true);
try {
geodeCurrentVersionOrdinal = currentOrdinalField.getShort(null);
} catch (IllegalAccessException e) {
throw new IllegalStateException(
"Unable to retrieve Version.java's CURRENT_ORDINAL field in order to establlish the product's serialization version",
e);
}
}
}