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 */ 018package org.apache.hadoop.hbase; 019 020import java.io.File; 021import java.io.IOException; 022import java.net.ServerSocket; 023import java.util.Arrays; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Random; 027import java.util.Set; 028import java.util.UUID; 029import java.util.concurrent.ThreadLocalRandom; 030import org.apache.commons.io.FileUtils; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.fs.Path; 033import org.apache.hadoop.hbase.Waiter.Predicate; 034import org.apache.hadoop.hbase.io.compress.Compression; 035import org.apache.yetus.audience.InterfaceAudience; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039/** 040 * Common helpers for testing HBase that do not depend on specific server/etc. things. 041 * @see org.apache.hadoop.hbase.HBaseCommonTestingUtility 042 */ 043@InterfaceAudience.Public 044public class HBaseCommonTestingUtility { 045 protected static final Logger LOG = LoggerFactory.getLogger(HBaseCommonTestingUtility.class); 046 047 /** 048 * Compression algorithms to use in parameterized JUnit 4 tests 049 */ 050 public static final List<Object[]> COMPRESSION_ALGORITHMS_PARAMETERIZED = 051 Arrays.asList(new Object[][] { { Compression.Algorithm.NONE }, { Compression.Algorithm.GZ } }); 052 053 /** 054 * This is for unit tests parameterized with a two booleans. 055 */ 056 public static final List<Object[]> BOOLEAN_PARAMETERIZED = 057 Arrays.asList(new Object[][] { { false }, { true } }); 058 059 /** 060 * Compression algorithms to use in testing 061 */ 062 public static final Compression.Algorithm[] COMPRESSION_ALGORITHMS = 063 { Compression.Algorithm.NONE, Compression.Algorithm.GZ }; 064 065 protected final Configuration conf; 066 067 public HBaseCommonTestingUtility() { 068 this(null); 069 } 070 071 public HBaseCommonTestingUtility(Configuration conf) { 072 this.conf = (conf == null ? HBaseConfiguration.create() : conf); 073 } 074 075 /** 076 * Returns this classes's instance of {@link Configuration}. 077 * @return Instance of Configuration. 078 */ 079 public Configuration getConfiguration() { 080 return this.conf; 081 } 082 083 /** 084 * System property key to get base test directory value 085 */ 086 public static final String BASE_TEST_DIRECTORY_KEY = "test.build.data.basedirectory"; 087 088 /** 089 * Default base directory for test output. 090 */ 091 public static final String DEFAULT_BASE_TEST_DIRECTORY = "target/test-data"; 092 093 /** 094 * Directory where we put the data for this instance of HBaseTestingUtility 095 */ 096 private File dataTestDir = null; 097 098 /** 099 * @return Where to write test data on local filesystem, specific to the test. Useful for tests 100 * that do not use a cluster. Creates it if it does not exist already. 101 */ 102 public Path getDataTestDir() { 103 if (this.dataTestDir == null) { 104 setupDataTestDir(); 105 } 106 return new Path(this.dataTestDir.getAbsolutePath()); 107 } 108 109 /** 110 * @param name the name of a subdirectory or file in the test data directory 111 * @return Path to a subdirectory or file named {code subdirName} under {@link #getDataTestDir()}. 112 * Does *NOT* create the directory or file if it does not exist. 113 */ 114 public Path getDataTestDir(final String name) { 115 return new Path(getDataTestDir(), name); 116 } 117 118 /** 119 * Sets up a directory for a test to use. 120 * @return New directory path, if created. 121 */ 122 protected Path setupDataTestDir() { 123 if (this.dataTestDir != null) { 124 LOG.warn("Data test dir already setup in " + dataTestDir.getAbsolutePath()); 125 return null; 126 } 127 Path testPath = getRandomDir(); 128 this.dataTestDir = new File(testPath.toString()).getAbsoluteFile(); 129 // Set this property so if mapreduce jobs run, they will use this as their home dir. 130 System.setProperty("test.build.dir", this.dataTestDir.toString()); 131 132 if (deleteOnExit()) { 133 this.dataTestDir.deleteOnExit(); 134 } 135 136 createSubDir("hbase.local.dir", testPath, "hbase-local-dir"); 137 138 return testPath; 139 } 140 141 /** 142 * @return A dir with a random (uuid) name under the test dir 143 * @see #getBaseTestDir() 144 */ 145 public Path getRandomDir() { 146 return new Path(getBaseTestDir(), getRandomUUID().toString()); 147 } 148 149 public static UUID getRandomUUID() { 150 return new UUID(ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong()); 151 } 152 153 protected void createSubDir(String propertyName, Path parent, String subDirName) { 154 Path newPath = new Path(parent, subDirName); 155 File newDir = new File(newPath.toString()).getAbsoluteFile(); 156 157 if (deleteOnExit()) { 158 newDir.deleteOnExit(); 159 } 160 161 conf.set(propertyName, newDir.getAbsolutePath()); 162 } 163 164 /** Returns True if we should delete testing dirs on exit. */ 165 boolean deleteOnExit() { 166 String v = System.getProperty("hbase.testing.preserve.testdir"); 167 // Let default be true, to delete on exit. 168 return v == null ? true : !Boolean.parseBoolean(v); 169 } 170 171 /** Returns True if we removed the test dirs */ 172 public boolean cleanupTestDir() { 173 if (deleteDir(this.dataTestDir)) { 174 this.dataTestDir = null; 175 return true; 176 } 177 return false; 178 } 179 180 /** 181 * @param subdir Test subdir name. 182 * @return True if we removed the test dir 183 */ 184 boolean cleanupTestDir(final String subdir) { 185 if (this.dataTestDir == null) { 186 return false; 187 } 188 return deleteDir(new File(this.dataTestDir, subdir)); 189 } 190 191 /** 192 * @return Where to write test data on local filesystem; usually 193 * {@link #DEFAULT_BASE_TEST_DIRECTORY} Should not be used by the unit tests, hence its's 194 * private. Unit test will use a subdirectory of this directory. 195 * @see #setupDataTestDir() 196 */ 197 private Path getBaseTestDir() { 198 String PathName = System.getProperty(BASE_TEST_DIRECTORY_KEY, DEFAULT_BASE_TEST_DIRECTORY); 199 200 return new Path(PathName); 201 } 202 203 /** 204 * @param dir Directory to delete 205 * @return True if we deleted it. 206 */ 207 boolean deleteDir(final File dir) { 208 if (dir == null || !dir.exists()) { 209 return true; 210 } 211 int ntries = 0; 212 do { 213 ntries += 1; 214 try { 215 if (deleteOnExit()) { 216 FileUtils.deleteDirectory(dir); 217 } 218 219 return true; 220 } catch (IOException ex) { 221 LOG.warn("Failed to delete " + dir.getAbsolutePath()); 222 } catch (IllegalArgumentException ex) { 223 LOG.warn("Failed to delete " + dir.getAbsolutePath(), ex); 224 } 225 } while (ntries < 30); 226 227 return false; 228 } 229 230 /** 231 * Wrapper method for {@link Waiter#waitFor(Configuration, long, Predicate)}. 232 */ 233 public <E extends Exception> long waitFor(long timeout, Predicate<E> predicate) throws E { 234 return Waiter.waitFor(this.conf, timeout, predicate); 235 } 236 237 /** 238 * Wrapper method for {@link Waiter#waitFor(Configuration, long, long, Predicate)}. 239 */ 240 public <E extends Exception> long waitFor(long timeout, long interval, Predicate<E> predicate) 241 throws E { 242 return Waiter.waitFor(this.conf, timeout, interval, predicate); 243 } 244 245 /** 246 * Wrapper method for {@link Waiter#waitFor(Configuration, long, long, boolean, Predicate)}. 247 */ 248 public <E extends Exception> long waitFor(long timeout, long interval, boolean failIfTimeout, 249 Predicate<E> predicate) throws E { 250 return Waiter.waitFor(this.conf, timeout, interval, failIfTimeout, predicate); 251 } 252 253 private static final PortAllocator portAllocator = new PortAllocator(); 254 255 public static int randomFreePort() { 256 return portAllocator.randomFreePort(); 257 } 258 259 static class PortAllocator { 260 private static final int MIN_RANDOM_PORT = 0xc000; 261 private static final int MAX_RANDOM_PORT = 0xfffe; 262 263 /** A set of ports that have been claimed using {@link #randomFreePort()}. */ 264 private final Set<Integer> takenRandomPorts = new HashSet<>(); 265 private final Random random; 266 private final AvailablePortChecker portChecker; 267 268 public PortAllocator() { 269 this.random = new Random(); 270 this.portChecker = new AvailablePortChecker() { 271 @Override 272 public boolean available(int port) { 273 try { 274 ServerSocket sock = new ServerSocket(port); 275 sock.close(); 276 return true; 277 } catch (IOException ex) { 278 return false; 279 } 280 } 281 }; 282 } 283 284 public PortAllocator(Random random, AvailablePortChecker portChecker) { 285 this.random = random; 286 this.portChecker = portChecker; 287 } 288 289 public PortAllocator(AvailablePortChecker portChecker) { 290 this(new Random(), portChecker); 291 } 292 293 /** 294 * Returns a random free port and marks that port as taken. Not thread-safe. Expected to be 295 * called from single-threaded test setup code/ 296 */ 297 public int randomFreePort() { 298 int port = 0; 299 do { 300 port = randomPort(); 301 if (takenRandomPorts.contains(port)) { 302 port = 0; 303 continue; 304 } 305 takenRandomPorts.add(port); 306 307 if (!portChecker.available(port)) { 308 port = 0; 309 } 310 } while (port == 0); 311 return port; 312 } 313 314 /** 315 * Returns a random port. These ports cannot be registered with IANA and are intended for 316 * dynamic allocation (see http://bit.ly/dynports). 317 */ 318 private int randomPort() { 319 return MIN_RANDOM_PORT + random.nextInt(MAX_RANDOM_PORT - MIN_RANDOM_PORT); 320 } 321 322 interface AvailablePortChecker { 323 boolean available(int port); 324 } 325 } 326}