| /* |
| * 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.ignite.lang; |
| |
| import java.io.Externalizable; |
| import java.io.IOException; |
| import java.io.ObjectInput; |
| import java.io.ObjectOutput; |
| import java.util.Date; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import org.apache.ignite.IgniteCheckedException; |
| import org.apache.ignite.internal.IgniteVersionUtils; |
| import org.apache.ignite.internal.util.typedef.internal.U; |
| import org.jetbrains.annotations.NotNull; |
| |
| /** |
| * Represents node version. |
| * <p> |
| * Node version can be acquired via {@link org.apache.ignite.cluster.ClusterNode#version()} method. |
| * <p> |
| * Two versions are compared in the following order: major number, |
| * minor number, maintenance number, revision timestamp. |
| */ |
| public class IgniteProductVersion implements Comparable<IgniteProductVersion>, Externalizable { |
| /** */ |
| private static final long serialVersionUID = 0L; |
| |
| /** Size of the {@link #revHash }*/ |
| public static final int REV_HASH_SIZE = 20; |
| |
| /** Size in bytes of serialized: 3 bytes (maj, min, maintenance version), 8 bytes - timestamp */ |
| public static final int SIZE_IN_BYTES = 3 + 8 + REV_HASH_SIZE; |
| |
| /** Regexp parse pattern. */ |
| private static final Pattern VER_PATTERN = |
| Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)([-.]([^0123456789][^-]+)(-SNAPSHOT)?)?(-(\\d+))?(-([\\da-f]+))?"); |
| |
| /** Major version number. */ |
| private byte major; |
| |
| /** Minor version number. */ |
| private byte minor; |
| |
| /** Maintenance version number. */ |
| private byte maintenance; |
| |
| /** Stage of development. */ |
| private String stage; |
| |
| /** Revision timestamp. */ |
| private long revTs; |
| |
| /** Revision hash. */ |
| private byte[] revHash; |
| |
| /** |
| * Empty constructor required by {@link Externalizable}. |
| */ |
| public IgniteProductVersion() { |
| // No-op. |
| } |
| |
| /** |
| * @param major Major version number. |
| * @param minor Minor version number. |
| * @param maintenance Maintenance version number. |
| * @param revTs Revision timestamp. |
| * @param revHash Revision hash. |
| */ |
| public IgniteProductVersion(byte major, byte minor, byte maintenance, long revTs, byte[] revHash) { |
| this(major, minor, maintenance, "", revTs, revHash); |
| } |
| |
| /** |
| * @param major Major version number. |
| * @param minor Minor version number. |
| * @param maintenance Maintenance version number. |
| * @param stage Stage of development. |
| * @param revTs Revision timestamp. |
| * @param revHash Revision hash. |
| */ |
| public IgniteProductVersion(byte major, byte minor, byte maintenance, String stage, long revTs, byte[] revHash) { |
| if (revHash != null && revHash.length != REV_HASH_SIZE) { |
| throw new IllegalArgumentException("Invalid length for SHA1 hash (must be " |
| + REV_HASH_SIZE + "): " + revHash.length); |
| } |
| |
| this.major = major; |
| this.minor = minor; |
| this.maintenance = maintenance; |
| this.stage = stage; |
| this.revTs = revTs; |
| this.revHash = revHash != null ? revHash : new byte[REV_HASH_SIZE]; |
| } |
| |
| /** |
| * Gets major version number. |
| * |
| * @return Major version number. |
| */ |
| public byte major() { |
| return major; |
| } |
| |
| /** |
| * Gets minor version number. |
| * |
| * @return Minor version number. |
| */ |
| public byte minor() { |
| return minor; |
| } |
| |
| /** |
| * Gets maintenance version number. |
| * |
| * @return Maintenance version number. |
| */ |
| public byte maintenance() { |
| return maintenance; |
| } |
| |
| /** |
| * @return Stage of development. |
| */ |
| public String stage() { |
| return stage; |
| } |
| |
| /** |
| * Gets revision timestamp. |
| * |
| * @return Revision timestamp. |
| */ |
| public long revisionTimestamp() { |
| return revTs; |
| } |
| |
| /** |
| * Gets revision hash. |
| * |
| * @return Revision hash. |
| */ |
| public byte[] revisionHash() { |
| return revHash; |
| } |
| |
| /** |
| * Gets release date. |
| * |
| * @return Release date. |
| */ |
| public Date releaseDate() { |
| return new Date(revTs * 1000); |
| } |
| |
| /** |
| * @param major Major version number. |
| * @param minor Minor version number. |
| * @param maintenance Maintenance version number. |
| * @return {@code True} if this version is greater or equal than the one passed in. |
| */ |
| public boolean greaterThanEqual(int major, int minor, int maintenance) { |
| // NOTE: Unknown version is less than any other version. |
| if (major == this.major) |
| return minor == this.minor ? this.maintenance >= maintenance : this.minor > minor; |
| else |
| return this.major > major; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int compareTo(@NotNull IgniteProductVersion o) { |
| // NOTE: Unknown version is less than any other version. |
| int res = Integer.compare(major, o.major); |
| |
| if (res != 0) |
| return res; |
| |
| res = Integer.compare(minor, o.minor); |
| |
| if (res != 0) |
| return res; |
| |
| res = Integer.compare(maintenance, o.maintenance); |
| |
| if (res != 0) |
| return res; |
| |
| return Long.compare(revTs, o.revTs); |
| } |
| |
| /** |
| * @param o Other version. |
| * @return Compare result. |
| */ |
| public int compareToIgnoreTimestamp(@NotNull IgniteProductVersion o) { |
| int res = Integer.compare(major, o.major); |
| |
| if (res != 0) |
| return res; |
| |
| res = Integer.compare(minor, o.minor); |
| |
| if (res != 0) |
| return res; |
| |
| return Integer.compare(maintenance, o.maintenance); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean equals(Object o) { |
| if (this == o) |
| return true; |
| |
| if (!(o instanceof IgniteProductVersion)) |
| return false; |
| |
| IgniteProductVersion that = (IgniteProductVersion)o; |
| |
| return revTs == that.revTs && maintenance == that.maintenance && minor == that.minor && major == that.major; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int hashCode() { |
| int res = major; |
| |
| res = 31 * res + minor; |
| res = 31 * res + maintenance; |
| res = 31 * res + (int)(revTs ^ (revTs >>> 32)); |
| |
| return res; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void writeExternal(ObjectOutput out) throws IOException { |
| out.writeByte(major); |
| out.writeByte(minor); |
| out.writeByte(maintenance); |
| out.writeLong(revTs); |
| U.writeByteArray(out, revHash); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { |
| major = in.readByte(); |
| minor = in.readByte(); |
| maintenance = in.readByte(); |
| revTs = in.readLong(); |
| revHash = U.readByteArray(in); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public String toString() { |
| String revTsStr = IgniteVersionUtils.formatBuildTimeStamp(revTs * 1000); |
| |
| String hash = U.byteArray2HexString(revHash).toLowerCase(); |
| |
| hash = hash.length() > 8 ? hash.substring(0, 8) : hash; |
| |
| return major + "." + minor + "." + maintenance + "#" + revTsStr + "-sha1:" + hash; |
| } |
| |
| /** |
| * Tries to parse product version from it's string representation. |
| * |
| * @param verStr String representation of version. |
| * @return Product version. |
| */ |
| public static IgniteProductVersion fromString(String verStr) { |
| assert verStr != null; |
| |
| if (verStr.endsWith("-DEV") || verStr.endsWith("-n/a")) // Development or built from source ZIP. |
| verStr = verStr.substring(0, verStr.length() - 4); |
| |
| Matcher match = VER_PATTERN.matcher(verStr); |
| |
| if (match.matches()) { |
| try { |
| byte major = Byte.parseByte(match.group(1)); |
| byte minor = Byte.parseByte(match.group(2)); |
| byte maintenance = Byte.parseByte(match.group(3)); |
| |
| String stage = ""; |
| |
| if (match.group(4) != null) |
| stage = match.group(4).substring(1); |
| |
| long revTs = 0; |
| |
| if (match.group(7) != null) |
| revTs = Long.parseLong(match.group(8)); |
| |
| byte[] revHash = null; |
| |
| if (match.group(9) != null) |
| revHash = U.decodeHex(match.group(10).toCharArray()); |
| |
| return new IgniteProductVersion(major, minor, maintenance, stage, revTs, revHash); |
| } |
| catch (IllegalStateException | IndexOutOfBoundsException | NumberFormatException | IgniteCheckedException e) { |
| throw new IllegalStateException("Failed to parse version: " + verStr, e); |
| } |
| } |
| else |
| throw new IllegalStateException("Failed to parse version: " + verStr); |
| } |
| } |