1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.coprocessor;
22
23 import java.io.IOException;
24 import java.io.InterruptedIOException;
25
26 import org.apache.hadoop.conf.Configuration;
27 import org.apache.hadoop.hbase.*;
28 import org.apache.hadoop.hbase.client.HBaseAdmin;
29 import org.apache.hadoop.hbase.master.HMaster;
30 import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
31 import org.apache.hadoop.hbase.util.Bytes;
32 import org.apache.hadoop.hbase.zookeeper.ZooKeeperNodeTracker;
33 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
34 import org.junit.AfterClass;
35 import org.junit.BeforeClass;
36 import org.junit.Test;
37 import org.junit.experimental.categories.Category;
38
39 import static org.junit.Assert.*;
40
41
42
43
44
45
46
47 @Category(MediumTests.class)
48 public class TestMasterCoprocessorExceptionWithAbort {
49
50 public static class MasterTracker extends ZooKeeperNodeTracker {
51 public boolean masterZKNodeWasDeleted = false;
52
53 public MasterTracker(ZooKeeperWatcher zkw, String masterNode, Abortable abortable) {
54 super(zkw, masterNode, abortable);
55 }
56
57 @Override
58 public synchronized void nodeDeleted(String path) {
59 if (path.equals("/hbase/master")) {
60 masterZKNodeWasDeleted = true;
61 }
62 }
63 }
64
65 public static class CreateTableThread extends Thread {
66 HBaseTestingUtility UTIL;
67 public CreateTableThread(HBaseTestingUtility UTIL) {
68 this.UTIL = UTIL;
69 }
70
71 @Override
72 public void run() {
73
74
75 HTableDescriptor htd = new HTableDescriptor(TEST_TABLE);
76 htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
77 try {
78 HBaseAdmin admin = UTIL.getHBaseAdmin();
79 admin.createTable(htd);
80 fail("BuggyMasterObserver failed to throw an exception.");
81 } catch (IOException e) {
82 assertEquals("HBaseAdmin threw an interrupted IOException as expected.",
83 e.getClass().getName(), "java.io.InterruptedIOException");
84 }
85 }
86 }
87
88 public static class BuggyMasterObserver extends BaseMasterObserver {
89 private boolean preCreateTableCalled;
90 private boolean postCreateTableCalled;
91 private boolean startCalled;
92 private boolean postStartMasterCalled;
93
94 @Override
95 public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> env,
96 HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
97
98
99 Integer i;
100 i = null;
101 i = i++;
102 }
103
104 public boolean wasCreateTableCalled() {
105 return preCreateTableCalled && postCreateTableCalled;
106 }
107
108 @Override
109 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
110 throws IOException {
111 postStartMasterCalled = true;
112 }
113
114 public boolean wasStartMasterCalled() {
115 return postStartMasterCalled;
116 }
117
118 @Override
119 public void start(CoprocessorEnvironment env) throws IOException {
120 startCalled = true;
121 }
122
123 public boolean wasStarted() {
124 return startCalled;
125 }
126 }
127
128 private static HBaseTestingUtility UTIL = new HBaseTestingUtility();
129 private static byte[] TEST_TABLE = Bytes.toBytes("observed_table");
130 private static byte[] TEST_FAMILY = Bytes.toBytes("fam1");
131
132 @BeforeClass
133 public static void setupBeforeClass() throws Exception {
134 Configuration conf = UTIL.getConfiguration();
135 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
136 BuggyMasterObserver.class.getName());
137 conf.set("hbase.coprocessor.abortonerror", "true");
138 UTIL.startMiniCluster();
139 }
140
141 @AfterClass
142 public static void teardownAfterClass() throws Exception {
143 UTIL.shutdownMiniCluster();
144 }
145
146 @Test(timeout=30000)
147 public void testExceptionFromCoprocessorWhenCreatingTable()
148 throws IOException {
149 MiniHBaseCluster cluster = UTIL.getHBaseCluster();
150
151 HMaster master = cluster.getMaster();
152 MasterCoprocessorHost host = master.getCoprocessorHost();
153 BuggyMasterObserver cp = (BuggyMasterObserver)host.findCoprocessor(
154 BuggyMasterObserver.class.getName());
155 assertFalse("No table created yet", cp.wasCreateTableCalled());
156
157
158
159 ZooKeeperWatcher zkw = new ZooKeeperWatcher(UTIL.getConfiguration(),
160 "unittest", new Abortable() {
161 @Override
162 public void abort(String why, Throwable e) {
163 throw new RuntimeException("Fatal ZK error: " + why, e);
164 }
165 @Override
166 public boolean isAborted() {
167 return false;
168 }
169 });
170
171 MasterTracker masterTracker = new MasterTracker(zkw,"/hbase/master",
172 new Abortable() {
173 @Override
174 public void abort(String why, Throwable e) {
175 throw new RuntimeException("Fatal ZK master tracker error, why=", e);
176 }
177 @Override
178 public boolean isAborted() {
179 return false;
180 }
181 });
182
183 masterTracker.start();
184 zkw.registerListener(masterTracker);
185
186
187
188
189 assertTrue(master.getLoadedCoprocessors().
190 equals("[" +
191 TestMasterCoprocessorExceptionWithAbort.BuggyMasterObserver.class.getName() +
192 "]"));
193
194 CreateTableThread createTableThread = new CreateTableThread(UTIL);
195
196
197
198 createTableThread.start();
199
200
201 for (int i = 0; i < 30; i++) {
202 if (masterTracker.masterZKNodeWasDeleted == true) {
203 break;
204 }
205 try {
206 Thread.sleep(1000);
207 } catch (InterruptedException e) {
208 fail("InterruptedException while waiting for master zk node to "
209 + "be deleted.");
210 }
211 }
212
213 assertTrue("Master aborted on coprocessor exception, as expected.",
214 masterTracker.masterZKNodeWasDeleted);
215
216 createTableThread.interrupt();
217 try {
218 createTableThread.join(1000);
219 } catch (InterruptedException e) {
220 assertTrue("Ignoring InterruptedException while waiting for " +
221 " createTableThread.join().", true);
222 }
223 }
224
225
226 @org.junit.Rule
227 public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
228 new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
229 }
230