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.IOException; 021import java.util.concurrent.CompletableFuture; 022import java.util.concurrent.ExecutionException; 023import java.util.function.Supplier; 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.client.AsyncConnection; 026import org.apache.hadoop.hbase.client.Connection; 027import org.apache.hadoop.hbase.client.ConnectionFactory; 028import org.junit.ClassRule; 029import org.junit.Rule; 030import org.junit.rules.ExternalResource; 031import org.junit.rules.TestRule; 032 033/** 034 * A {@link TestRule} that manages an instance of the {@link SingleProcessHBaseCluster}. Can be used 035 * in either the {@link Rule} or {@link ClassRule} positions. Built on top of an instance of 036 * {@link HBaseTestingUtil}, so be weary of intermixing direct use of that class with this Rule. 037 * </p> 038 * Use in combination with {@link ConnectionRule}, for example: 039 * 040 * <pre> 041 * { 042 * @code 043 * public class TestMyClass { 044 * @ClassRule 045 * public static final MiniClusterRule miniClusterRule = MiniClusterRule.newBuilder().build(); 046 * 047 * @Rule 048 * public final ConnectionRule connectionRule = 049 * ConnectionRule.createAsyncConnectionRule(miniClusterRule::createAsyncConnection); 050 * } 051 * } 052 * </pre> 053 */ 054public final class MiniClusterRule extends ExternalResource { 055 056 /** 057 * A builder for fluent composition of a new {@link MiniClusterRule}. 058 */ 059 public static class Builder { 060 061 private StartTestingClusterOption miniClusterOption; 062 private Configuration conf; 063 064 /** 065 * Use the provided {@link StartTestingClusterOption} to construct the 066 * {@link SingleProcessHBaseCluster}. 067 */ 068 public Builder setMiniClusterOption(final StartTestingClusterOption miniClusterOption) { 069 this.miniClusterOption = miniClusterOption; 070 return this; 071 } 072 073 /** 074 * Seed the underlying {@link HBaseTestingUtil} with the provided {@link Configuration}. 075 */ 076 public Builder setConfiguration(final Configuration conf) { 077 this.conf = conf; 078 return this; 079 } 080 081 public Builder setConfiguration(Supplier<Configuration> supplier) { 082 return setConfiguration(supplier.get()); 083 } 084 085 public MiniClusterRule build() { 086 return new MiniClusterRule(conf, 087 miniClusterOption != null 088 ? miniClusterOption 089 : StartTestingClusterOption.builder().build()); 090 } 091 } 092 093 private final HBaseTestingUtil testingUtility; 094 private final StartTestingClusterOption miniClusterOptions; 095 096 private SingleProcessHBaseCluster miniCluster; 097 098 private MiniClusterRule(final Configuration conf, 099 final StartTestingClusterOption miniClusterOptions) { 100 this.testingUtility = new HBaseTestingUtil(conf); 101 this.miniClusterOptions = miniClusterOptions; 102 } 103 104 public static Builder newBuilder() { 105 return new Builder(); 106 } 107 108 /** 109 * Returns the underlying instance of {@link HBaseTestingUtil} 110 */ 111 public HBaseTestingUtil getTestingUtility() { 112 return testingUtility; 113 } 114 115 /** 116 * Create a {@link Connection} to the managed {@link SingleProcessHBaseCluster}. It's up to the 117 * caller to {@link Connection#close() close()} the connection when finished. 118 */ 119 public Connection createConnection() { 120 try { 121 return createAsyncConnection().get().toConnection(); 122 } catch (InterruptedException | ExecutionException e) { 123 throw new RuntimeException(e); 124 } 125 } 126 127 /** 128 * Create a {@link AsyncConnection} to the managed {@link SingleProcessHBaseCluster}. It's up to 129 * the caller to {@link AsyncConnection#close() close()} the connection when finished. 130 */ 131 public CompletableFuture<AsyncConnection> createAsyncConnection() { 132 if (miniCluster == null) { 133 throw new IllegalStateException("test cluster not initialized"); 134 } 135 return ConnectionFactory.createAsyncConnection(miniCluster.getConf()); 136 } 137 138 @Override 139 protected void before() throws Throwable { 140 miniCluster = testingUtility.startMiniCluster(miniClusterOptions); 141 } 142 143 @Override 144 protected void after() { 145 try { 146 testingUtility.shutdownMiniCluster(); 147 } catch (IOException e) { 148 throw new RuntimeException(e); 149 } 150 } 151}