/** * 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.hadoop.hbase.crosssite; import java.io.ByteArrayInputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.DoNotRetryIOException; import org.apache.hadoop.hbase.crosssite.locator.ClusterLocator; import org.apache.hadoop.hbase.filter.ParseConstants; import org.apache.hadoop.hbase.util.Bytes; /** * The utilities of the cross site table. */ public class CrossSiteUtil { /** * Gets the table name in the cluster. The table name looks like tableName_clusterName. * * @param tableName * @param clusterName * @return */ public static String getClusterTableName(String tableName, String clusterName) { return tableName + "_" + clusterName; } /** * Gets the table name in a given peer cluster. * * @param tableName * @param masterCluster * @param peerCluster * @return table name in a given peer cluster */ // TODO : This does not use the peerCluster? public static String getPeerClusterTableName(String tableName, String masterCluster, String peerCluster) { return getClusterTableName(tableName, masterCluster); } /** * Takes a quoted byte array and converts it into an unquoted byte array For example: given a byte * array representing 'abc', it returns a byte array representing abc * <p> * * @param stringAsByteArray * the quoted byte array * @return */ public static byte[] convertByteArrayToString(byte[] stringAsByteArray) { if (stringAsByteArray != null && stringAsByteArray.length > 0) { int beginWithQuote = 0; int endWithQuote = 0; if (stringAsByteArray[0] == ParseConstants.SINGLE_QUOTE) { beginWithQuote = 1; } if (stringAsByteArray[stringAsByteArray.length - 1] == ParseConstants.SINGLE_QUOTE) { endWithQuote = 1; } int length = stringAsByteArray.length - beginWithQuote - endWithQuote; byte[] targetString = new byte[length]; Bytes.putBytes(targetString, 0, stringAsByteArray, beginWithQuote, length); return targetString; } return stringAsByteArray; } /** * Converts an int expressed in a byte array to an actual int * <p> * This doesn't use Bytes.toInt because that assumes that there will be {@link #SIZEOF_INT} bytes * available. * <p> * * @param numberAsByteArray * the int value expressed as a byte array * @return the int value */ public static int convertByteArrayToInt(byte[] numberAsByteArray) { long tempResult = convertByteArrayToLong(numberAsByteArray); if (tempResult > Integer.MAX_VALUE) { throw new IllegalArgumentException("Integer Argument too large"); } else if (tempResult < Integer.MIN_VALUE) { throw new IllegalArgumentException("Integer Argument too small"); } int result = (int) tempResult; return result; } /** * Converts a long expressed in a byte array to an actual long * <p> * This doesn't use Bytes.toLong because that assumes that there will be {@link #SIZEOF_LONG} * bytes available. * <p> * * @param numberAsByteArray * the long value expressed as a byte array * @return the long value */ public static long convertByteArrayToLong(byte[] numberAsByteArray) { if (numberAsByteArray == null) { throw new IllegalArgumentException("convertByteArrayToLong called with a null array"); } int i = 0; long result = 0; boolean isNegative = false; if (numberAsByteArray[i] == ParseConstants.MINUS_SIGN) { i++; isNegative = true; } while (i != numberAsByteArray.length) { if (numberAsByteArray[i] < ParseConstants.ZERO || numberAsByteArray[i] > ParseConstants.NINE) { throw new IllegalArgumentException("Byte Array should only contain digits"); } result = result * 10 + (numberAsByteArray[i] - ParseConstants.ZERO); if (result < 0) { throw new IllegalArgumentException("Long Argument too large"); } i++; } if (isNegative) { return -result; } else { return result; } } /** * Reads the split keys from the data. * * @param data * @return * @throws IOException */ public static byte[][] readSplitKeys(byte[] data) throws IOException { if (data == null) { return null; } ByteArrayInputStream stream = null; try { stream = new ByteArrayInputStream(data); DataInput in = new DataInputStream(stream); if (in.readBoolean()) { int size = in.readInt(); byte[][] splitKeys = new byte[size][]; for (int i = 0; i < size; i++) { splitKeys[i] = Bytes.readByteArray(in); } return splitKeys; } else { return null; } } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { } } } } /** * Writes the split keys into the DataOutput. * * @param splitkeys * @param out * @throws IOException */ public static void writeSplitKeys(final byte[][] splitkeys, DataOutput out) throws IOException { if (splitkeys != null) { out.writeBoolean(true); int numOfKeys = splitkeys.length; out.writeInt(numOfKeys); // TODO : this would be too heavy on the zk for (int i = 0; i < numOfKeys; i++) { Bytes.writeByteArray(out, splitkeys[i]); } } else { out.writeBoolean(false); } } public static ClusterLocator instantiateClusterLocator(Configuration conf, String clusterLocatorClassName) throws IOException { Class<? extends ClusterLocator> clusterLocatorClass = null; try { clusterLocatorClass = (Class<? extends ClusterLocator>) conf .getClassByName(clusterLocatorClassName); } catch (ClassNotFoundException e) { throw new IOException("Couldn't load clusterlocator class " + clusterLocatorClassName, e); } if (clusterLocatorClass == null) { throw new IOException("Failed loading clusterlocator class " + clusterLocatorClassName); } try { return clusterLocatorClass.asSubclass(ClusterLocator.class).newInstance(); } catch (Exception e) { throw new IOException("Problem loading clusterinf: ", e); } } /** * Gets the name of the cross site table by the HTable name. * * The HTable name is composed according to the rule, crossSiteTableName_clusterName * * @param hTableName * @return */ public static String getCrossSiteTableName(String hTableName) { int index = hTableName.lastIndexOf('_'); if (index >= 1) { return hTableName.substring(0, index); } else { throw new IllegalArgumentException("The table name " + hTableName + " is in an incorrect format"); } } /** * Gets the cluster name by the HTable name. * * The HTable name is composed according to the rule, crossSiteTableName_clusterName * * @param hTableName * @return */ public static String getClusterName(String hTableName) { int index = hTableName.lastIndexOf('_'); if (index >= 1 && index < hTableName.length() - 1) { return hTableName.substring(index + 1); } else { throw new IllegalArgumentException("The table name " + hTableName + " is in an incorrect format"); } } /** * Validates the cluster name. The cluster name should not contain _. * * If the cluster name contains _, the IllegalArgumentException will be thrown. * * @param clusterName */ public static void validateClusterName(String clusterName) { if (clusterName.indexOf('_') >= 0) { throw new IllegalArgumentException("The cluster name should not contain _, but the name is " + clusterName); } } /** * Finds out whether to perform the failover when the exception is encounted. * * Performs the failover when the exception is not a <code>DoNotRetryIOException</code>. * * @param e * @return */ public static boolean isFailoverException(Throwable e) { if (!(e instanceof DoNotRetryIOException)) { return true; } return false; } /** * Gets all the descendant clusters in the hierarchy. * * @param hierarchyMap * @param parent * @return */ public static Set<String> getDescendantClusters(Map<String, Set<String>> hierarchyMap, String parent) { Set<String> results = new TreeSet<String>(); Set<String> children = hierarchyMap.get(parent); if (children != null) { results.addAll(children); for (String child : children) { if (!child.equals(parent)) { results.addAll(getDescendantClusters(hierarchyMap, child)); } } } return results; } /** * Gets all the child clusters in the hierarchy. * * @param hierarchyMap * @param parent * @return */ public static Set<String> getChildClusters(Map<String, Set<String>> hierarchyMap, String parent) { Set<String> results = null; Set<String> children = hierarchyMap.get(parent); if (children != null) { results = children; } else { results = Collections.emptySet(); } return results; } /** * Converts the list of ClusterInfo to a list of String. * * @param clusterInfos * @return */ public static List<String> toStringList(List<ClusterInfo> clusterInfos) { List<String> result = Collections.emptyList(); if (clusterInfos != null && !clusterInfos.isEmpty()) { result = new ArrayList<String>(); for (ClusterInfo clusterInfo : clusterInfos) { result.add(clusterInfo.getName()); } } return result; } }