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  public static HBaseProtos.VersionInfo getCurrentClientVersionInfo() {
074    return RpcServer.getCurrentCall().map(RpcCallContext::getClientVersionInfo).orElse(null);
075  }
076
077
078  /**
079   * @param version
080   * @return the passed-in <code>version</code> int as a version String
081   *         (e.g. 0x0103004 is 1.3.4)
082   */
083  public static String versionNumberToString(final int version) {
084    return String.format("%d.%d.%d",
085        ((version >> 20) & 0xff),
086        ((version >> 12) & 0xff),
087        (version & 0xfff));
088  }
089
090  /**
091   * Pack the full number version in a int. by shifting each component by 8bit,
092   * except the dot release which has 12bit.
093   * Examples: (1.3.4 is 0x0103004, 2.1.0 is 0x0201000)
094   * @param versionInfo the VersionInfo object to pack
095   * @return the version number as int. (e.g. 0x0103004 is 1.3.4)
096   */
097  public static int getVersionNumber(final HBaseProtos.VersionInfo versionInfo) {
098    if (versionInfo != null) {
099      try {
100        final String[] components = getVersionComponents(versionInfo);
101        int clientMajor = components.length > 0 ? Integer.parseInt(components[0]) : 0;
102        int clientMinor = components.length > 1 ? Integer.parseInt(components[1]) : 0;
103        int clientPatch = components.length > 2 ? Integer.parseInt(components[2]) : 0;
104        return buildVersionNumber(clientMajor, clientMinor, clientPatch);
105      } catch (NumberFormatException e) {
106        int clientMajor = versionInfo.hasVersionMajor() ? versionInfo.getVersionMajor() : 0;
107        int clientMinor = versionInfo.hasVersionMinor() ? versionInfo.getVersionMinor() : 0;
108        return buildVersionNumber(clientMajor, clientMinor, 0);
109      }
110    }
111    return(0); // no version
112  }
113
114  /**
115   * Pack the full number version in a int. by shifting each component by 8bit,
116   * except the dot release which has 12bit.
117   * Examples: (1.3.4 is 0x0103004, 2.1.0 is 0x0201000)
118   * @param major version major number
119   * @param minor version minor number
120   * @param patch version patch number
121   * @return the version number as int. (e.g. 0x0103004 is 1.3.4)
122   */
123  private static int buildVersionNumber(int major, int minor, int patch) {
124    return (major << 20) | (minor << 12) | patch;
125  }
126
127  /**
128   * Returns the version components
129   * Examples: "1.4.3" returns [1, 4, 3], "4.5.6-SNAPSHOT" returns [4, 5, 6, "SNAPSHOT"]
130   * @return the components of the version string
131   */
132  private static String[] getVersionComponents(final HBaseProtos.VersionInfo versionInfo) {
133    return versionInfo.getVersion().split("[\\.-]");
134  }
135}