blob: 6a3d81ce2c441a75dea79357b12970e39a168d3a [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.ambari.server.utils;
import java.util.ArrayList;
import java.util.List;
import org.apache.ambari.server.bootstrap.BootStrapImpl;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
/**
* Provides various utility functions to be used for version handling.
* The compatibility matrix between server, agent, store can also be maintained
* in this class. Currently, exact match is required between all the three.
*/
public class VersionUtils {
/**
* Compares two versions strings of the form N.N.N.N or even N.N.N.N-###
* (which should ignore everything after the dash). If the user has a custom
* stack, e.g., 2.3.MYNAME or MYNAME.2.3, then any segment that contains
* letters should be ignored.
*
* @param version1
* the first operand. If set to {@value BootStrapImpl#DEV_VERSION}
* then this will always return {@code 0)}
* @param version2
* the second operand.
* @param maxLengthToCompare
* The maximum length to compare - 2 means only Major and Minor 0 to
* compare the whole version strings
* @return 0 if both are equal up to the length compared, -1 if first one is
* lower, +1 otherwise
*/
public static int compareVersions(String version1, String version2, int maxLengthToCompare)
throws IllegalArgumentException {
if (version1 == null){
throw new IllegalArgumentException("version1 cannot be null");
}
if (version2 == null){
throw new IllegalArgumentException("version2 cannot be null");
}
version1 = StringUtils.trim(version1);
version2 = StringUtils.trim(version2);
if (version1.indexOf('-') >=0) {
version1 = version1.substring(0, version1.indexOf('-'));
}
if (version2.indexOf('-') >=0) {
version2 = version2.substring(0, version2.indexOf('-'));
}
if (version1.isEmpty()) {
throw new IllegalArgumentException("version1 cannot be empty");
}
if (version2.isEmpty()) {
throw new IllegalArgumentException("version2 cannot be empty");
}
if (maxLengthToCompare < 0) {
throw new IllegalArgumentException("maxLengthToCompare cannot be less than 0");
}
if(BootStrapImpl.DEV_VERSION.equals(version1.trim())) {
return 0;
}
//String pattern = "^([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+).*";
String pattern = "([0-9]+).([0-9]+).([0-9]+).?([0-9]+)?.*";
String[] version1Parts = version1.replaceAll(pattern, "$1.$2.$3.$4").split("\\.");
String[] version2Parts = version2.replaceAll(pattern, "$1.$2.$3.$4").split("\\.");
int length = Math.max(version1Parts.length, version2Parts.length);
length = maxLengthToCompare == 0 || maxLengthToCompare > length ? length : maxLengthToCompare;
List<Integer> stack1Parts = new ArrayList<>();
List<Integer> stack2Parts = new ArrayList<>();
for (int i = 0; i < length; i++) {
// Robust enough to handle strings in the version
try {
int stack1Part = i < version1Parts.length ?
Integer.parseInt(version1Parts[i]) : 0;
stack1Parts.add(stack1Part);
} catch (NumberFormatException e) {
stack1Parts.add(0);
}
try {
int stack2Part = i < version2Parts.length ?
Integer.parseInt(version2Parts[i]) : 0;
stack2Parts.add(stack2Part);
} catch (NumberFormatException e) {
stack2Parts.add(0);
}
}
length = Math.max(stack1Parts.size(), stack2Parts.size());
for (int i = 0; i < length; i++) {
Integer stack1Part = stack1Parts.get(i);
Integer stack2Part = stack2Parts.get(i);
if (stack1Part < stack2Part) {
return -1;
}
if (stack1Part > stack2Part) {
return 1;
}
}
return 0;
}
/**
* Compares two versions strings of the form N.N.N.N
*
* @param version1
* the first operand. If set to {@value BootStrapImpl#DEV_VERSION}
* then this will always return {@code 0)}
* @param version2
* the second operand.
* @param allowEmptyVersions
* Allow one or both version values to be null or empty string
* @return 0 if both are equal up to the length compared, -1 if first one is
* lower, +1 otherwise
*/
public static int compareVersions(String version1, String version2, boolean allowEmptyVersions) {
if (allowEmptyVersions) {
if (version1 != null && version1.equals(BootStrapImpl.DEV_VERSION)) {
return 0;
}
if (version1 == null && version2 == null) {
return 0;
} else {
if (version1 == null) {
return -1;
}
if (version2 == null) {
return 1;
}
}
if (version1.isEmpty() && version2.isEmpty()) {
return 0;
} else {
if (version1.isEmpty()) {
return -1;
}
if (version2.isEmpty()) {
return 1;
}
}
}
return compareVersions(version1, version2, 0);
}
/**
* Compares two versions strings of the form N.N.N.N
*
* @param version1
* the first operand. If set to {@value BootStrapImpl#DEV_VERSION}
* then this will always return {@code 0)}
* @param version2
* the second operand.
* @return 0 if both are equal, -1 if first one is lower, +1 otherwise
*/
public static int compareVersions(String version1, String version2) {
return compareVersions(version1, version2, 0);
}
/**
* Compares two version for equality, allows empty versions
*
* @param version1
* the first operand. If set to {@value BootStrapImpl#DEV_VERSION}
* then this will always return {@code 0)}
* @param version2
* the second operand.
* @param allowEmptyVersions
* Allow one or both version values to be null or empty string
* @return true if versions are equal; false otherwise
*/
public static boolean areVersionsEqual(String version1, String version2, boolean allowEmptyVersions) {
return 0 == compareVersions(version1, version2, allowEmptyVersions);
}
/**
* Return N.N.N from N.N.N.xyz
* @param version
* @return
*/
public static String getVersionSubstring(String version) {
String[] versionParts = version.split("\\.");
if (versionParts.length < 3) {
throw new IllegalArgumentException("Invalid version number");
}
return versionParts[0] + "." + versionParts[1] + "." + versionParts[2];
}
/**
* Compares versions, using a build number using a dash separator, if one exists.
* This is is useful when comparing repository versions with one another that include
* build number
* @param version1
* the first version
* @param version2
* the second version
* @param places
* the number of decimal-separated places to compare
* @return
*/
public static int compareVersionsWithBuild(String version1, String version2, int places) {
version1 = (null == version1) ? "0" : version1;
version2 = (null == version2) ? "0" : version2;
// check _exact_ equality
if (StringUtils.equals(version1, version2)) {
return 0;
}
int compare = VersionUtils.compareVersions(version1, version2, places);
if (0 != compare) {
return compare;
}
int v1 = 0;
int v2 = 0;
if (version1.indexOf('-') > -1) {
v1 = NumberUtils.toInt(version1.substring(version1.indexOf('-')), 0);
}
if (version2.indexOf('-') > -1) {
v2 = NumberUtils.toInt(version2.substring(version2.indexOf('-')), 0);
}
compare = v2 - v1;
return Integer.compare(compare, 0);
}
}