001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.hbase.client;
020
021import org.apache.yetus.audience.InterfaceAudience;
022import org.apache.hadoop.hbase.ipc.RpcCallContext;
023import org.apache.hadoop.hbase.ipc.RpcServer;
024import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
025
026
027/**
028 * Class to help with parsing the version info.
029 */
030@InterfaceAudience.Private
031public final class VersionInfoUtil {
032
033  private VersionInfoUtil() {
034    /* UTIL CLASS ONLY */
035  }
036
037  public static boolean currentClientHasMinimumVersion(int major, int minor) {
038    return hasMinimumVersion(getCurrentClientVersionInfo(), major, minor);
039  }
040
041  public static boolean hasMinimumVersion(HBaseProtos.VersionInfo versionInfo,
042                                          int major,
043                                          int minor) {
044    if (versionInfo != null) {
045      if (versionInfo.hasVersionMajor() && versionInfo.hasVersionMinor()) {
046        int clientMajor = versionInfo.getVersionMajor();
047        if (clientMajor != major) {
048          return clientMajor > major;
049        }
050        int clientMinor = versionInfo.getVersionMinor();
051        return clientMinor >= minor;
052      }
053      try {
054        final String[] components = getVersionComponents(versionInfo);
055
056        int clientMajor = components.length > 0 ? Integer.parseInt(components[0]) : 0;
057        if (clientMajor != major) {
058          return clientMajor > major;
059        }
060
061        int clientMinor = components.length > 1 ? Integer.parseInt(components[1]) : 0;
062        return clientMinor >= minor;
063      } catch (NumberFormatException e) {
064        return false;
065      }
066    }
067    return false;
068  }
069
070  /**
071   * @return the versionInfo extracted from the current RpcCallContext
072   */
073  private static HBaseProtos.VersionInfo getCurrentClientVersionInfo() {
074    return RpcServer.getCurrentCall().map(RpcCallContext::getClientVersionInfo).orElse(null);
075  }
076
077  /**
078   * @return the version number extracted from the current RpcCallContext as int.
079   *         (e.g. 0x0103004 is 1.3.4)
080   */
081  public static int getCurrentClientVersionNumber() {
082    return getVersionNumber(getCurrentClientVersionInfo());
083  }
084
085
086  /**
087   * @param version
088   * @return the passed-in <code>version</code> int as a version String
089   *         (e.g. 0x0103004 is 1.3.4)
090   */
091  public static String versionNumberToString(final int version) {
092    return String.format("%d.%d.%d",
093        ((version >> 20) & 0xff),
094        ((version >> 12) & 0xff),
095        (version & 0xfff));
096  }
097
098  /**
099   * Pack the full number version in a int. by shifting each component by 8bit,
100   * except the dot release which has 12bit.
101   * Examples: (1.3.4 is 0x0103004, 2.1.0 is 0x0201000)
102   * @param versionInfo the VersionInfo object to pack
103   * @return the version number as int. (e.g. 0x0103004 is 1.3.4)
104   */
105  public static int getVersionNumber(final HBaseProtos.VersionInfo versionInfo) {
106    if (versionInfo != null) {
107      try {
108        final String[] components = getVersionComponents(versionInfo);
109        int clientMajor = components.length > 0 ? Integer.parseInt(components[0]) : 0;
110        int clientMinor = components.length > 1 ? Integer.parseInt(components[1]) : 0;
111        int clientPatch = components.length > 2 ? Integer.parseInt(components[2]) : 0;
112        return buildVersionNumber(clientMajor, clientMinor, clientPatch);
113      } catch (NumberFormatException e) {
114        int clientMajor = versionInfo.hasVersionMajor() ? versionInfo.getVersionMajor() : 0;
115        int clientMinor = versionInfo.hasVersionMinor() ? versionInfo.getVersionMinor() : 0;
116        return buildVersionNumber(clientMajor, clientMinor, 0);
117      }
118    }
119    return(0); // no version
120  }
121
122  /**
123   * Pack the full number version in a int. by shifting each component by 8bit,
124   * except the dot release which has 12bit.
125   * Examples: (1.3.4 is 0x0103004, 2.1.0 is 0x0201000)
126   * @param major version major number
127   * @param minor version minor number
128   * @param patch version patch number
129   * @return the version number as int. (e.g. 0x0103004 is 1.3.4)
130   */
131  private static int buildVersionNumber(int major, int minor, int patch) {
132    return (major << 20) | (minor << 12) | patch;
133  }
134
135  /**
136   * Returns the version components
137   * Examples: "1.4.3" returns [1, 4, 3], "4.5.6-SNAPSHOT" returns [4, 5, 6, "SNAPSHOT"]
138   * @return the components of the version string
139   */
140  private static String[] getVersionComponents(final HBaseProtos.VersionInfo versionInfo) {
141    return versionInfo.getVersion().split("[\\.-]");
142  }
143}