| /** |
| * |
| * 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; |
| |
| import java.io.IOException; |
| import java.util.NavigableMap; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FileSystem; |
| import org.apache.hadoop.fs.Path; |
| import org.apache.hadoop.hbase.client.Durability; |
| import org.apache.hadoop.hbase.client.Get; |
| import org.apache.hadoop.hbase.client.Put; |
| import org.apache.hadoop.hbase.client.Result; |
| import org.apache.hadoop.hbase.client.Table; |
| import org.apache.hadoop.hbase.regionserver.HRegion; |
| import org.apache.hadoop.hbase.regionserver.Region; |
| import org.apache.hadoop.hbase.regionserver.RegionAsTable; |
| import org.apache.hadoop.hbase.util.Bytes; |
| import org.apache.hadoop.hbase.util.FSTableDescriptors; |
| import org.apache.hadoop.hbase.util.FSUtils; |
| import org.apache.hadoop.hdfs.MiniDFSCluster; |
| |
| import junit.framework.AssertionFailedError; |
| import junit.framework.TestCase; |
| |
| /** |
| * Abstract HBase test class. Initializes a few things that can come in handly |
| * like an HBaseConfiguration and filesystem. |
| * @deprecated Write junit4 unit tests using {@link HBaseTestingUtility} |
| */ |
| @Deprecated |
| public abstract class HBaseTestCase extends TestCase { |
| private static final Log LOG = LogFactory.getLog(HBaseTestCase.class); |
| |
| protected final static byte [] fam1 = Bytes.toBytes("colfamily11"); |
| protected final static byte [] fam2 = Bytes.toBytes("colfamily21"); |
| protected final static byte [] fam3 = Bytes.toBytes("colfamily31"); |
| |
| protected static final byte [][] COLUMNS = {fam1, fam2, fam3}; |
| |
| private boolean localfs = false; |
| protected static Path testDir = null; |
| protected FileSystem fs = null; |
| protected HRegion meta = null; |
| protected static final char FIRST_CHAR = 'a'; |
| protected static final char LAST_CHAR = 'z'; |
| protected static final String PUNCTUATION = "~`@#$%^&*()-_+=:;',.<>/?[]{}|"; |
| protected static final byte [] START_KEY_BYTES = {FIRST_CHAR, FIRST_CHAR, FIRST_CHAR}; |
| protected String START_KEY = new String(START_KEY_BYTES, HConstants.UTF8_CHARSET); |
| protected static final int MAXVERSIONS = 3; |
| |
| protected final HBaseTestingUtility testUtil = new HBaseTestingUtility(); |
| |
| public volatile Configuration conf = HBaseConfiguration.create(); |
| public final FSTableDescriptors fsTableDescriptors; |
| { |
| try { |
| fsTableDescriptors = new FSTableDescriptors(conf); |
| } catch (IOException e) { |
| throw new RuntimeException("Failed to init descriptors", e); |
| } |
| } |
| |
| /** constructor */ |
| public HBaseTestCase() { |
| super(); |
| } |
| |
| /** |
| * @param name |
| */ |
| public HBaseTestCase(String name) { |
| super(name); |
| } |
| |
| /** |
| * Note that this method must be called after the mini hdfs cluster has |
| * started or we end up with a local file system. |
| */ |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| localfs = |
| (conf.get("fs.defaultFS", "file:///").compareTo("file:///") == 0); |
| |
| if (fs == null) { |
| this.fs = FileSystem.get(conf); |
| } |
| try { |
| if (localfs) { |
| testDir = getUnitTestdir(getName()); |
| if (fs.exists(testDir)) { |
| fs.delete(testDir, true); |
| } |
| } else { |
| testDir = FSUtils.getRootDir(conf); |
| } |
| } catch (Exception e) { |
| LOG.fatal("error during setup", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| try { |
| if (localfs) { |
| if (this.fs.exists(testDir)) { |
| this.fs.delete(testDir, true); |
| } |
| } |
| } catch (Exception e) { |
| LOG.fatal("error during tear down", e); |
| } |
| super.tearDown(); |
| } |
| |
| /** |
| * @see HBaseTestingUtility#getBaseTestDir |
| * @param testName |
| * @return directory to use for this test |
| */ |
| protected Path getUnitTestdir(String testName) { |
| return testUtil.getDataTestDir(testName); |
| } |
| |
| /** |
| * You must call close on the returned region and then close on the log file it created. Do |
| * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to close both the region and the WAL. |
| * @param desc |
| * @param startKey |
| * @param endKey |
| * @return An {@link HRegion} |
| * @throws IOException |
| */ |
| public HRegion createNewHRegion(HTableDescriptor desc, byte [] startKey, |
| byte [] endKey) |
| throws IOException { |
| return createNewHRegion(desc, startKey, endKey, this.conf); |
| } |
| |
| public HRegion createNewHRegion(HTableDescriptor desc, byte [] startKey, |
| byte [] endKey, Configuration conf) |
| throws IOException { |
| HRegionInfo hri = new HRegionInfo(desc.getTableName(), startKey, endKey); |
| return HBaseTestingUtility.createRegionAndWAL(hri, testDir, conf, desc); |
| } |
| |
| protected HRegion openClosedRegion(final HRegion closedRegion) |
| throws IOException { |
| return HRegion.openHRegion(closedRegion, null); |
| } |
| |
| /** |
| * Create a table of name <code>name</code> with {@link COLUMNS} for |
| * families. |
| * @param name Name to give table. |
| * @return Column descriptor. |
| */ |
| protected HTableDescriptor createTableDescriptor(final String name) { |
| return createTableDescriptor(name, MAXVERSIONS); |
| } |
| |
| /** |
| * Create a table of name <code>name</code> with {@link COLUMNS} for |
| * families. |
| * @param name Name to give table. |
| * @param versions How many versions to allow per column. |
| * @return Column descriptor. |
| */ |
| protected HTableDescriptor createTableDescriptor(final String name, |
| final int versions) { |
| return createTableDescriptor(name, HColumnDescriptor.DEFAULT_MIN_VERSIONS, |
| versions, HConstants.FOREVER, HColumnDescriptor.DEFAULT_KEEP_DELETED); |
| } |
| |
| /** |
| * Create a table of name <code>name</code> with {@link COLUMNS} for |
| * families. |
| * @param name Name to give table. |
| * @param versions How many versions to allow per column. |
| * @return Column descriptor. |
| */ |
| protected HTableDescriptor createTableDescriptor(final String name, |
| final int minVersions, final int versions, final int ttl, KeepDeletedCells keepDeleted) { |
| HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name)); |
| for (byte[] cfName : new byte[][]{ fam1, fam2, fam3 }) { |
| htd.addFamily(new HColumnDescriptor(cfName) |
| .setMinVersions(minVersions) |
| .setMaxVersions(versions) |
| .setKeepDeletedCells(keepDeleted) |
| .setBlockCacheEnabled(false) |
| .setTimeToLive(ttl) |
| ); |
| } |
| return htd; |
| } |
| |
| /** |
| * Add content to region <code>r</code> on the passed column |
| * <code>column</code>. |
| * Adds data of the from 'aaa', 'aab', etc where key and value are the same. |
| * @param r |
| * @param columnFamily |
| * @param column |
| * @throws IOException |
| * @return count of what we added. |
| */ |
| public static long addContent(final Region r, final byte [] columnFamily, final byte[] column) |
| throws IOException { |
| byte [] startKey = r.getRegionInfo().getStartKey(); |
| byte [] endKey = r.getRegionInfo().getEndKey(); |
| byte [] startKeyBytes = startKey; |
| if (startKeyBytes == null || startKeyBytes.length == 0) { |
| startKeyBytes = START_KEY_BYTES; |
| } |
| return addContent(new RegionAsTable(r), Bytes.toString(columnFamily), Bytes.toString(column), |
| startKeyBytes, endKey, -1); |
| } |
| |
| public static long addContent(final Region r, final byte [] columnFamily) throws IOException { |
| return addContent(r, columnFamily, null); |
| } |
| |
| /** |
| * Add content to region <code>r</code> on the passed column |
| * <code>column</code>. |
| * Adds data of the from 'aaa', 'aab', etc where key and value are the same. |
| * @throws IOException |
| * @return count of what we added. |
| */ |
| public static long addContent(final Table updater, |
| final String columnFamily) throws IOException { |
| return addContent(updater, columnFamily, START_KEY_BYTES, null); |
| } |
| |
| public static long addContent(final Table updater, final String family, |
| final String column) throws IOException { |
| return addContent(updater, family, column, START_KEY_BYTES, null); |
| } |
| |
| /** |
| * Add content to region <code>r</code> on the passed column |
| * <code>column</code>. |
| * Adds data of the from 'aaa', 'aab', etc where key and value are the same. |
| * @return count of what we added. |
| * @throws IOException |
| */ |
| public static long addContent(final Table updater, final String columnFamily, |
| final byte [] startKeyBytes, final byte [] endKey) |
| throws IOException { |
| return addContent(updater, columnFamily, null, startKeyBytes, endKey, -1); |
| } |
| |
| public static long addContent(final Table updater, final String family, String column, |
| final byte [] startKeyBytes, final byte [] endKey) throws IOException { |
| return addContent(updater, family, column, startKeyBytes, endKey, -1); |
| } |
| |
| /** |
| * Add content to region <code>r</code> on the passed column |
| * <code>column</code>. |
| * Adds data of the from 'aaa', 'aab', etc where key and value are the same. |
| * @return count of what we added. |
| * @throws IOException |
| */ |
| public static long addContent(final Table updater, |
| final String columnFamily, |
| final String column, |
| final byte [] startKeyBytes, final byte [] endKey, final long ts) |
| throws IOException { |
| long count = 0; |
| // Add rows of three characters. The first character starts with the |
| // 'a' character and runs up to 'z'. Per first character, we run the |
| // second character over same range. And same for the third so rows |
| // (and values) look like this: 'aaa', 'aab', 'aac', etc. |
| char secondCharStart = (char)startKeyBytes[1]; |
| char thirdCharStart = (char)startKeyBytes[2]; |
| EXIT: for (char c = (char)startKeyBytes[0]; c <= LAST_CHAR; c++) { |
| for (char d = secondCharStart; d <= LAST_CHAR; d++) { |
| for (char e = thirdCharStart; e <= LAST_CHAR; e++) { |
| byte [] t = new byte [] {(byte)c, (byte)d, (byte)e}; |
| if (endKey != null && endKey.length > 0 |
| && Bytes.compareTo(endKey, t) <= 0) { |
| break EXIT; |
| } |
| try { |
| Put put; |
| if(ts != -1) { |
| put = new Put(t, ts); |
| } else { |
| put = new Put(t); |
| } |
| try { |
| StringBuilder sb = new StringBuilder(); |
| if (column != null && column.contains(":")) { |
| sb.append(column); |
| } else { |
| if (columnFamily != null) { |
| sb.append(columnFamily); |
| if (!columnFamily.endsWith(":")) { |
| sb.append(":"); |
| } |
| if (column != null) { |
| sb.append(column); |
| } |
| } |
| } |
| byte[][] split = |
| KeyValue.parseColumn(Bytes.toBytes(sb.toString())); |
| if(split.length == 1) { |
| byte[] qualifier = new byte[0]; |
| put.addColumn(split[0], qualifier, t); |
| } else { |
| put.addColumn(split[0], split[1], t); |
| } |
| put.setDurability(Durability.SKIP_WAL); |
| updater.put(put); |
| count++; |
| } catch (RuntimeException ex) { |
| ex.printStackTrace(); |
| throw ex; |
| } catch (IOException ex) { |
| ex.printStackTrace(); |
| throw ex; |
| } |
| } catch (RuntimeException ex) { |
| ex.printStackTrace(); |
| throw ex; |
| } catch (IOException ex) { |
| ex.printStackTrace(); |
| throw ex; |
| } |
| } |
| // Set start character back to FIRST_CHAR after we've done first loop. |
| thirdCharStart = FIRST_CHAR; |
| } |
| secondCharStart = FIRST_CHAR; |
| } |
| return count; |
| } |
| |
| protected void assertResultEquals(final HRegion region, final byte [] row, |
| final byte [] family, final byte [] qualifier, final long timestamp, |
| final byte [] value) |
| throws IOException { |
| Get get = new Get(row); |
| get.setTimeStamp(timestamp); |
| Result res = region.get(get); |
| NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> map = |
| res.getMap(); |
| byte [] res_value = map.get(family).get(qualifier).get(timestamp); |
| |
| if (value == null) { |
| assertEquals(Bytes.toString(family) + " " + Bytes.toString(qualifier) + |
| " at timestamp " + timestamp, null, res_value); |
| } else { |
| if (res_value == null) { |
| fail(Bytes.toString(family) + " " + Bytes.toString(qualifier) + |
| " at timestamp " + timestamp + "\" was expected to be \"" + |
| Bytes.toStringBinary(value) + " but was null"); |
| } |
| if (res_value != null) { |
| assertEquals(Bytes.toString(family) + " " + Bytes.toString(qualifier) + |
| " at timestamp " + |
| timestamp, value, new String(res_value)); |
| } |
| } |
| } |
| |
| /** |
| * Common method to close down a MiniDFSCluster and the associated file system |
| * |
| * @param cluster |
| */ |
| public static void shutdownDfs(MiniDFSCluster cluster) { |
| if (cluster != null) { |
| LOG.info("Shutting down Mini DFS "); |
| try { |
| cluster.shutdown(); |
| } catch (Exception e) { |
| /// Can get a java.lang.reflect.UndeclaredThrowableException thrown |
| // here because of an InterruptedException. Don't let exceptions in |
| // here be cause of test failure. |
| } |
| try { |
| FileSystem fs = cluster.getFileSystem(); |
| if (fs != null) { |
| LOG.info("Shutting down FileSystem"); |
| fs.close(); |
| } |
| FileSystem.closeAll(); |
| } catch (IOException e) { |
| LOG.error("error closing file system", e); |
| } |
| } |
| } |
| |
| /** |
| * You must call {@link #closeRootAndMeta()} when done after calling this |
| * method. It does cleanup. |
| * @throws IOException |
| */ |
| protected void createMetaRegion() throws IOException { |
| FSTableDescriptors fsTableDescriptors = new FSTableDescriptors(conf); |
| meta = HBaseTestingUtility.createRegionAndWAL(HRegionInfo.FIRST_META_REGIONINFO, testDir, |
| conf, fsTableDescriptors.get(TableName.META_TABLE_NAME)); |
| } |
| |
| protected void closeRootAndMeta() throws IOException { |
| HBaseTestingUtility.closeRegionAndWAL(meta); |
| } |
| |
| public static void assertByteEquals(byte[] expected, |
| byte[] actual) { |
| if (Bytes.compareTo(expected, actual) != 0) { |
| throw new AssertionFailedError("expected:<" + |
| Bytes.toString(expected) + "> but was:<" + |
| Bytes.toString(actual) + ">"); |
| } |
| } |
| |
| public static void assertEquals(byte[] expected, |
| byte[] actual) { |
| if (Bytes.compareTo(expected, actual) != 0) { |
| throw new AssertionFailedError("expected:<" + |
| Bytes.toStringBinary(expected) + "> but was:<" + |
| Bytes.toStringBinary(actual) + ">"); |
| } |
| } |
| } |