1   /**
2    * Copyright 2009 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.client;
21  
22  
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertTrue;
26  
27  import java.io.IOException;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.concurrent.atomic.AtomicBoolean;
34  import java.util.concurrent.atomic.AtomicInteger;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.hadoop.hbase.HBaseTestingUtility;
39  import org.apache.hadoop.hbase.HColumnDescriptor;
40  import org.apache.hadoop.hbase.HConstants;
41  import org.apache.hadoop.hbase.HRegionInfo;
42  import org.apache.hadoop.hbase.HServerAddress;
43  import org.apache.hadoop.hbase.HTableDescriptor;
44  import org.apache.hadoop.hbase.NotServingRegionException;
45  import org.apache.hadoop.hbase.TableExistsException;
46  import org.apache.hadoop.hbase.TableNotDisabledException;
47  import org.apache.hadoop.hbase.TableNotFoundException;
48  import org.apache.hadoop.hbase.executor.EventHandler;
49  import org.apache.hadoop.hbase.executor.EventHandler.EventType;
50  import org.apache.hadoop.hbase.executor.ExecutorService;
51  import org.apache.hadoop.hbase.master.MasterServices;
52  import org.apache.hadoop.hbase.util.Bytes;
53  import org.junit.AfterClass;
54  import org.junit.Before;
55  import org.junit.BeforeClass;
56  import org.junit.Test;
57  
58  
59  /**
60   * Class to test HBaseAdmin.
61   * Spins up the minicluster once at test start and then takes it down afterward.
62   * Add any testing of HBaseAdmin functionality here.
63   */
64  public class TestAdmin {
65    final Log LOG = LogFactory.getLog(getClass());
66    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
67    private HBaseAdmin admin;
68  
69    @BeforeClass
70    public static void setUpBeforeClass() throws Exception {
71      TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
72      TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250);
73      TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 6);
74      TEST_UTIL.startMiniCluster(3);
75    }
76  
77    @AfterClass
78    public static void tearDownAfterClass() throws Exception {
79      TEST_UTIL.shutdownMiniCluster();
80    }
81  
82    @Before
83    public void setUp() throws Exception {
84      this.admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
85    }
86  
87    @Test
88    public void testDisableAndEnableTable() throws IOException {
89      final byte [] row = Bytes.toBytes("row");
90      final byte [] qualifier = Bytes.toBytes("qualifier");
91      final byte [] value = Bytes.toBytes("value");
92      final byte [] table = Bytes.toBytes("testDisableAndEnableTable");
93      HTable ht = TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
94      Put put = new Put(row);
95      put.add(HConstants.CATALOG_FAMILY, qualifier, value);
96      ht.put(put);
97      Get get = new Get(row);
98      get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
99      ht.get(get);
100 
101     this.admin.disableTable(table);
102 
103     // Test that table is disabled
104     get = new Get(row);
105     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
106     boolean ok = false;
107     try {
108       ht.get(get);
109     } catch (NotServingRegionException e) {
110       ok = true;
111     } catch (RetriesExhaustedException e) {
112       ok = true;
113     }
114     assertTrue(ok);
115     this.admin.enableTable(table);
116 
117     // Test that table is enabled
118     try {
119       ht.get(get);
120     } catch (RetriesExhaustedException e) {
121       ok = false;
122     }
123     assertTrue(ok);
124   }
125 
126   @Test
127   public void testCreateTable() throws IOException {
128     HTableDescriptor [] tables = admin.listTables();
129     int numTables = tables.length;
130     TEST_UTIL.createTable(Bytes.toBytes("testCreateTable"),
131       HConstants.CATALOG_FAMILY);
132     tables = this.admin.listTables();
133     assertEquals(numTables + 1, tables.length);
134   }
135 
136   @Test
137   public void testGetTableDescriptor() throws IOException {
138     HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
139     HColumnDescriptor fam2 = new HColumnDescriptor("fam2");
140     HColumnDescriptor fam3 = new HColumnDescriptor("fam3");
141     HTableDescriptor htd = new HTableDescriptor("myTestTable");
142     htd.addFamily(fam1);
143     htd.addFamily(fam2);
144     htd.addFamily(fam3);
145     this.admin.createTable(htd);
146     HTable table = new HTable(TEST_UTIL.getConfiguration(), "myTestTable");
147     HTableDescriptor confirmedHtd = table.getTableDescriptor();
148     assertEquals(htd.compareTo(confirmedHtd), 0);
149   }
150 
151   /**
152    * Verify schema modification takes.
153    * @throws IOException
154    */
155   @Test public void testChangeTableSchema() throws IOException {
156     final byte [] tableName = Bytes.toBytes("changeTableSchema");
157     HTableDescriptor [] tables = admin.listTables();
158     int numTables = tables.length;
159     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY);
160     tables = this.admin.listTables();
161     assertEquals(numTables + 1, tables.length);
162 
163     // FIRST, do htabledescriptor changes.
164     HTableDescriptor htd = this.admin.getTableDescriptor(tableName);
165     // Make a copy and assert copy is good.
166     HTableDescriptor copy = new HTableDescriptor(htd);
167     assertTrue(htd.equals(copy));
168     // Now amend the copy. Introduce differences.
169     long newFlushSize = htd.getMemStoreFlushSize() / 2;
170     copy.setMemStoreFlushSize(newFlushSize);
171     final String key = "anyoldkey";
172     assertTrue(htd.getValue(key) == null);
173     copy.setValue(key, key);
174     boolean expectedException = false;
175     try {
176       this.admin.modifyTable(tableName, copy);
177     } catch (TableNotDisabledException re) {
178       expectedException = true;
179     }
180     assertTrue(expectedException);
181     this.admin.disableTable(tableName);
182     assertTrue(this.admin.isTableDisabled(tableName));
183     modifyTable(tableName, copy);
184     HTableDescriptor modifiedHtd = this.admin.getTableDescriptor(tableName);
185     // Assert returned modifiedhcd is same as the copy.
186     assertFalse(htd.equals(modifiedHtd));
187     assertTrue(copy.equals(modifiedHtd));
188     assertEquals(newFlushSize, modifiedHtd.getMemStoreFlushSize());
189     assertEquals(key, modifiedHtd.getValue(key));
190 
191     // Reenable table to test it fails if not disabled.
192     this.admin.enableTable(tableName);
193     assertFalse(this.admin.isTableDisabled(tableName));
194 
195     // Now work on column family changes.
196     int countOfFamilies = modifiedHtd.getFamilies().size();
197     assertTrue(countOfFamilies > 0);
198     HColumnDescriptor hcd = modifiedHtd.getFamilies().iterator().next();
199     int maxversions = hcd.getMaxVersions();
200     final int newMaxVersions = maxversions + 1;
201     hcd.setMaxVersions(newMaxVersions);
202     final byte [] hcdName = hcd.getName();
203     expectedException = false;
204     try {
205       this.admin.modifyColumn(tableName, hcd);
206     } catch (TableNotDisabledException re) {
207       expectedException = true;
208     }
209     assertTrue(expectedException);
210     this.admin.disableTable(tableName);
211     assertTrue(this.admin.isTableDisabled(tableName));
212     // Modify Column is synchronous
213     this.admin.modifyColumn(tableName, hcd);
214     modifiedHtd = this.admin.getTableDescriptor(tableName);
215     HColumnDescriptor modifiedHcd = modifiedHtd.getFamily(hcdName);
216     assertEquals(newMaxVersions, modifiedHcd.getMaxVersions());
217 
218     // Try adding a column
219     // Reenable table to test it fails if not disabled.
220     this.admin.enableTable(tableName);
221     assertFalse(this.admin.isTableDisabled(tableName));
222     final String xtracolName = "xtracol";
223     HColumnDescriptor xtracol = new HColumnDescriptor(xtracolName);
224     xtracol.setValue(xtracolName, xtracolName);
225     try {
226       this.admin.addColumn(tableName, xtracol);
227     } catch (TableNotDisabledException re) {
228       expectedException = true;
229     }
230     assertTrue(expectedException);
231     this.admin.disableTable(tableName);
232     assertTrue(this.admin.isTableDisabled(tableName));
233     this.admin.addColumn(tableName, xtracol);
234     modifiedHtd = this.admin.getTableDescriptor(tableName);
235     hcd = modifiedHtd.getFamily(xtracol.getName());
236     assertTrue(hcd != null);
237     assertTrue(hcd.getValue(xtracolName).equals(xtracolName));
238 
239     // Delete the just-added column.
240     this.admin.deleteColumn(tableName, xtracol.getName());
241     modifiedHtd = this.admin.getTableDescriptor(tableName);
242     hcd = modifiedHtd.getFamily(xtracol.getName());
243     assertTrue(hcd == null);
244 
245     // Delete the table
246     this.admin.deleteTable(tableName);
247     this.admin.listTables();
248     assertFalse(this.admin.tableExists(tableName));
249   }
250 
251   /**
252    * Modify table is async so wait on completion of the table operation in master.
253    * @param tableName
254    * @param htd
255    * @throws IOException
256    */
257   private void modifyTable(final byte [] tableName, final HTableDescriptor htd)
258   throws IOException {
259     MasterServices services = TEST_UTIL.getMiniHBaseCluster().getMaster();
260     ExecutorService executor = services.getExecutorService();
261     AtomicBoolean done = new AtomicBoolean(false);
262     executor.registerListener(EventType.C_M_MODIFY_TABLE, new DoneListener(done));
263     this.admin.modifyTable(tableName, htd);
264     while (!done.get()) {
265       synchronized (done) {
266         try {
267           done.wait(1000);
268         } catch (InterruptedException e) {
269           e.printStackTrace();
270         }
271       }
272     }
273     executor.unregisterListener(EventType.C_M_MODIFY_TABLE);
274   }
275 
276   /**
277    * Listens for when an event is done in Master.
278    */
279   static class DoneListener implements EventHandler.EventHandlerListener {
280     private final AtomicBoolean done;
281 
282     DoneListener(final AtomicBoolean done) {
283       super();
284       this.done = done;
285     }
286 
287     @Override
288     public void afterProcess(EventHandler event) {
289       this.done.set(true);
290       synchronized (this.done) {
291         // Wake anyone waiting on this value to change.
292         this.done.notifyAll();
293       }
294     }
295 
296     @Override
297     public void beforeProcess(EventHandler event) {
298       // continue
299     }
300   }
301 
302   protected void verifyRoundRobinDistribution(HTable ht, int expectedRegions) throws IOException {
303     int numRS = ht.getCurrentNrHRS();
304     Map<HRegionInfo,HServerAddress> regions = ht.getRegionsInfo();
305     Map<HServerAddress, List<HRegionInfo>> server2Regions = new HashMap<HServerAddress, List<HRegionInfo>>();
306     for (Map.Entry<HRegionInfo,HServerAddress> entry : regions.entrySet()) {
307       HServerAddress server = entry.getValue();
308       List<HRegionInfo> regs = server2Regions.get(server);
309       if (regs == null) {
310         regs = new ArrayList<HRegionInfo>();
311         server2Regions.put(server, regs);
312       }
313       regs.add(entry.getKey());
314     }
315     float average = (float) expectedRegions/numRS;
316     int min = (int)Math.floor(average);
317     int max = (int)Math.ceil(average);
318     for (List<HRegionInfo> regionList : server2Regions.values()) {
319       assertTrue(regionList.size() == min || regionList.size() == max);
320     }
321   }
322 
323   @Test
324   public void testCreateTableWithRegions() throws IOException, InterruptedException {
325 
326     byte[] tableName = Bytes.toBytes("testCreateTableWithRegions");
327 
328     byte [][] splitKeys = {
329         new byte [] { 1, 1, 1 },
330         new byte [] { 2, 2, 2 },
331         new byte [] { 3, 3, 3 },
332         new byte [] { 4, 4, 4 },
333         new byte [] { 5, 5, 5 },
334         new byte [] { 6, 6, 6 },
335         new byte [] { 7, 7, 7 },
336         new byte [] { 8, 8, 8 },
337         new byte [] { 9, 9, 9 },
338     };
339     int expectedRegions = splitKeys.length + 1;
340 
341     HTableDescriptor desc = new HTableDescriptor(tableName);
342     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
343     admin.createTable(desc, splitKeys);
344 
345     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
346     Map<HRegionInfo,HServerAddress> regions = ht.getRegionsInfo();
347     assertEquals("Tried to create " + expectedRegions + " regions " +
348         "but only found " + regions.size(),
349         expectedRegions, regions.size());
350     System.err.println("Found " + regions.size() + " regions");
351 
352     Iterator<HRegionInfo> hris = regions.keySet().iterator();
353     HRegionInfo hri = hris.next();
354     assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
355     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0]));
356     hri = hris.next();
357     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0]));
358     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1]));
359     hri = hris.next();
360     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1]));
361     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2]));
362     hri = hris.next();
363     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2]));
364     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3]));
365     hri = hris.next();
366     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3]));
367     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4]));
368     hri = hris.next();
369     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4]));
370     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5]));
371     hri = hris.next();
372     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5]));
373     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6]));
374     hri = hris.next();
375     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6]));
376     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7]));
377     hri = hris.next();
378     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7]));
379     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8]));
380     hri = hris.next();
381     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8]));
382     assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
383 
384     verifyRoundRobinDistribution(ht, expectedRegions);
385 
386     // Now test using start/end with a number of regions
387 
388     // Use 80 bit numbers to make sure we aren't limited
389     byte [] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
390     byte [] endKey =   { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
391 
392     // Splitting into 10 regions, we expect (null,1) ... (9, null)
393     // with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle
394 
395     expectedRegions = 10;
396 
397     byte [] TABLE_2 = Bytes.add(tableName, Bytes.toBytes("_2"));
398 
399     desc = new HTableDescriptor(TABLE_2);
400     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
401     admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
402     admin.createTable(desc, startKey, endKey, expectedRegions);
403 
404     ht = new HTable(TEST_UTIL.getConfiguration(), TABLE_2);
405     regions = ht.getRegionsInfo();
406     assertEquals("Tried to create " + expectedRegions + " regions " +
407         "but only found " + regions.size(),
408         expectedRegions, regions.size());
409     System.err.println("Found " + regions.size() + " regions");
410 
411     hris = regions.keySet().iterator();
412     hri = hris.next();
413     assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
414     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {1,1,1,1,1,1,1,1,1,1}));
415     hri = hris.next();
416     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {1,1,1,1,1,1,1,1,1,1}));
417     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {2,2,2,2,2,2,2,2,2,2}));
418     hri = hris.next();
419     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {2,2,2,2,2,2,2,2,2,2}));
420     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {3,3,3,3,3,3,3,3,3,3}));
421     hri = hris.next();
422     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {3,3,3,3,3,3,3,3,3,3}));
423     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {4,4,4,4,4,4,4,4,4,4}));
424     hri = hris.next();
425     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {4,4,4,4,4,4,4,4,4,4}));
426     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {5,5,5,5,5,5,5,5,5,5}));
427     hri = hris.next();
428     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {5,5,5,5,5,5,5,5,5,5}));
429     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {6,6,6,6,6,6,6,6,6,6}));
430     hri = hris.next();
431     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {6,6,6,6,6,6,6,6,6,6}));
432     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {7,7,7,7,7,7,7,7,7,7}));
433     hri = hris.next();
434     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {7,7,7,7,7,7,7,7,7,7}));
435     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {8,8,8,8,8,8,8,8,8,8}));
436     hri = hris.next();
437     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {8,8,8,8,8,8,8,8,8,8}));
438     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {9,9,9,9,9,9,9,9,9,9}));
439     hri = hris.next();
440     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {9,9,9,9,9,9,9,9,9,9}));
441     assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
442 
443     verifyRoundRobinDistribution(ht, expectedRegions);
444 
445     // Try once more with something that divides into something infinite
446 
447     startKey = new byte [] { 0, 0, 0, 0, 0, 0 };
448     endKey = new byte [] { 1, 0, 0, 0, 0, 0 };
449 
450     expectedRegions = 5;
451 
452     byte [] TABLE_3 = Bytes.add(tableName, Bytes.toBytes("_3"));
453 
454     desc = new HTableDescriptor(TABLE_3);
455     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
456     admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
457     admin.createTable(desc, startKey, endKey, expectedRegions);
458 
459     ht = new HTable(TEST_UTIL.getConfiguration(), TABLE_3);
460     regions = ht.getRegionsInfo();
461     assertEquals("Tried to create " + expectedRegions + " regions " +
462         "but only found " + regions.size(),
463         expectedRegions, regions.size());
464     System.err.println("Found " + regions.size() + " regions");
465 
466     verifyRoundRobinDistribution(ht, expectedRegions);
467 
468     // Try an invalid case where there are duplicate split keys
469     splitKeys = new byte [][] {
470         new byte [] { 1, 1, 1 },
471         new byte [] { 2, 2, 2 },
472         new byte [] { 3, 3, 3 },
473         new byte [] { 2, 2, 2 }
474     };
475 
476     byte [] TABLE_4 = Bytes.add(tableName, Bytes.toBytes("_4"));
477     desc = new HTableDescriptor(TABLE_4);
478     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
479     admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
480     try {
481       admin.createTable(desc, splitKeys);
482       assertTrue("Should not be able to create this table because of " +
483           "duplicate split keys", false);
484     } catch(IllegalArgumentException iae) {
485       // Expected
486     }
487   }
488 
489   @Test
490   public void testTableExist() throws IOException {
491     final byte [] table = Bytes.toBytes("testTableExist");
492     boolean exist = false;
493     exist = this.admin.tableExists(table);
494     assertEquals(false, exist);
495     TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
496     exist = this.admin.tableExists(table);
497     assertEquals(true, exist);
498   }
499 
500   /**
501    * Tests forcing split from client and having scanners successfully ride over split.
502    * @throws Exception
503    * @throws IOException
504    */
505   @Test
506   public void testForceSplit() throws Exception {
507     byte [] familyName = HConstants.CATALOG_FAMILY;
508     byte [] tableName = Bytes.toBytes("testForceSplit");
509     final HTable table = TEST_UTIL.createTable(tableName, familyName);
510     byte[] k = new byte[3];
511     int rowCount = 0;
512     for (byte b1 = 'a'; b1 < 'z'; b1++) {
513       for (byte b2 = 'a'; b2 < 'z'; b2++) {
514         for (byte b3 = 'a'; b3 < 'z'; b3++) {
515           k[0] = b1;
516           k[1] = b2;
517           k[2] = b3;
518           Put put = new Put(k);
519           put.add(familyName, new byte[0], k);
520           table.put(put);
521           rowCount++;
522         }
523       }
524     }
525 
526     // get the initial layout (should just be one region)
527     Map<HRegionInfo,HServerAddress> m = table.getRegionsInfo();
528     System.out.println("Initial regions (" + m.size() + "): " + m);
529     assertTrue(m.size() == 1);
530 
531     // Verify row count
532     Scan scan = new Scan();
533     ResultScanner scanner = table.getScanner(scan);
534     int rows = 0;
535     for(@SuppressWarnings("unused") Result result : scanner) {
536       rows++;
537     }
538     scanner.close();
539     assertEquals(rowCount, rows);
540 
541     // Have an outstanding scan going on to make sure we can scan over splits.
542     scan = new Scan();
543     scanner = table.getScanner(scan);
544     // Scan first row so we are into first region before split happens.
545     scanner.next();
546 
547     final AtomicInteger count = new AtomicInteger(0);
548     Thread t = new Thread("CheckForSplit") {
549       public void run() {
550         for (int i = 0; i < 20; i++) {
551           try {
552             sleep(1000);
553           } catch (InterruptedException e) {
554             continue;
555           }
556           // check again    table = new HTable(conf, tableName);
557           Map<HRegionInfo, HServerAddress> regions = null;
558           try {
559             regions = table.getRegionsInfo();
560           } catch (IOException e) {
561             e.printStackTrace();
562           }
563           if (regions == null) continue;
564           count.set(regions.size());
565           if (count.get() >= 2) break;
566           LOG.debug("Cycle waiting on split");
567         }
568       }
569     };
570     t.start();
571     // Split the table
572     this.admin.split(Bytes.toString(tableName));
573     t.join();
574 
575     // Verify row count
576     rows = 1; // We counted one row above.
577     for (@SuppressWarnings("unused") Result result : scanner) {
578       rows++;
579       if (rows > rowCount) {
580         scanner.close();
581         assertTrue("Scanned more than expected (" + rowCount + ")", false);
582       }
583     }
584     scanner.close();
585     assertEquals(rowCount, rows);
586   }
587 
588   /**
589    * HADOOP-2156
590    * @throws IOException
591    */
592   @Test (expected=IllegalArgumentException.class)
593   public void testEmptyHHTableDescriptor() throws IOException {
594     this.admin.createTable(new HTableDescriptor());
595   }
596 
597   @Test
598   public void testEnableDisableAddColumnDeleteColumn() throws Exception {
599     byte [] tableName = Bytes.toBytes("testMasterAdmin");
600     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY);
601     this.admin.disableTable(tableName);
602     try {
603       new HTable(TEST_UTIL.getConfiguration(), tableName);
604     } catch (org.apache.hadoop.hbase.client.RegionOfflineException e) {
605       // Expected
606     }
607     this.admin.addColumn(tableName, new HColumnDescriptor("col2"));
608     this.admin.enableTable(tableName);
609     try {
610       this.admin.deleteColumn(tableName, Bytes.toBytes("col2"));
611     } catch(TableNotDisabledException e) {
612       // Expected
613     }
614     this.admin.disableTable(tableName);
615     this.admin.deleteColumn(tableName, Bytes.toBytes("col2"));
616     this.admin.deleteTable(tableName);
617   }
618 
619   @Test
620   public void testCreateBadTables() throws IOException {
621     String msg = null;
622     try {
623       this.admin.createTable(HTableDescriptor.ROOT_TABLEDESC);
624     } catch (IllegalArgumentException e) {
625       msg = e.toString();
626     }
627     assertTrue("Unexcepted exception message " + msg, msg != null &&
628       msg.startsWith(IllegalArgumentException.class.getName()) &&
629       msg.contains(HTableDescriptor.ROOT_TABLEDESC.getNameAsString()));
630     msg = null;
631     try {
632       this.admin.createTable(HTableDescriptor.META_TABLEDESC);
633     } catch(IllegalArgumentException e) {
634       msg = e.toString();
635     }
636     assertTrue("Unexcepted exception message " + msg, msg != null &&
637       msg.startsWith(IllegalArgumentException.class.getName()) &&
638       msg.contains(HTableDescriptor.META_TABLEDESC.getNameAsString()));
639 
640     // Now try and do concurrent creation with a bunch of threads.
641     final HTableDescriptor threadDesc =
642       new HTableDescriptor("threaded_testCreateBadTables");
643     threadDesc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
644     int count = 10;
645     Thread [] threads = new Thread [count];
646     final AtomicInteger successes = new AtomicInteger(0);
647     final AtomicInteger failures = new AtomicInteger(0);
648     final HBaseAdmin localAdmin = this.admin;
649     for (int i = 0; i < count; i++) {
650       threads[i] = new Thread(Integer.toString(i)) {
651         @Override
652         public void run() {
653           try {
654             localAdmin.createTable(threadDesc);
655             successes.incrementAndGet();
656           } catch (TableExistsException e) {
657             failures.incrementAndGet();
658           } catch (IOException e) {
659             throw new RuntimeException("Failed threaded create" + getName(), e);
660           }
661         }
662       };
663     }
664     for (int i = 0; i < count; i++) {
665       threads[i].start();
666     }
667     for (int i = 0; i < count; i++) {
668       while(threads[i].isAlive()) {
669         try {
670           Thread.sleep(1000);
671         } catch (InterruptedException e) {
672           // continue
673         }
674       }
675     }
676     // All threads are now dead.  Count up how many tables were created and
677     // how many failed w/ appropriate exception.
678     assertEquals(1, successes.get());
679     assertEquals(count - 1, failures.get());
680   }
681 
682   /**
683    * Test for hadoop-1581 'HBASE: Unopenable tablename bug'.
684    * @throws Exception
685    */
686   @Test
687   public void testTableNameClash() throws Exception {
688     String name = "testTableNameClash";
689     admin.createTable(new HTableDescriptor(name + "SOMEUPPERCASE"));
690     admin.createTable(new HTableDescriptor(name));
691     // Before fix, below would fail throwing a NoServerForRegionException.
692     new HTable(TEST_UTIL.getConfiguration(), name);
693   }
694 
695   /**
696    * Test read only tables
697    * @throws Exception
698    */
699   @Test
700   public void testReadOnlyTable() throws Exception {
701     byte [] name = Bytes.toBytes("testReadOnlyTable");
702     HTable table = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
703     byte[] value = Bytes.toBytes("somedata");
704     // This used to use an empty row... That must have been a bug
705     Put put = new Put(value);
706     put.add(HConstants.CATALOG_FAMILY, HConstants.CATALOG_FAMILY, value);
707     table.put(put);
708   }
709 
710   /**
711    * Test that user table names can contain '-' and '.' so long as they do not
712    * start with same. HBASE-771
713    * @throws IOException
714    */
715   @Test
716   public void testTableNames() throws IOException {
717     byte[][] illegalNames = new byte[][] {
718         Bytes.toBytes("-bad"),
719         Bytes.toBytes(".bad"),
720         HConstants.ROOT_TABLE_NAME,
721         HConstants.META_TABLE_NAME
722     };
723     for (int i = 0; i < illegalNames.length; i++) {
724       try {
725         new HTableDescriptor(illegalNames[i]);
726         throw new IOException("Did not detect '" +
727           Bytes.toString(illegalNames[i]) + "' as an illegal user table name");
728       } catch (IllegalArgumentException e) {
729         // expected
730       }
731     }
732     byte[] legalName = Bytes.toBytes("g-oo.d");
733     try {
734       new HTableDescriptor(legalName);
735     } catch (IllegalArgumentException e) {
736       throw new IOException("Legal user table name: '" +
737         Bytes.toString(legalName) + "' caused IllegalArgumentException: " +
738         e.getMessage());
739     }
740   }
741 
742   /**
743    * For HADOOP-2579
744    * @throws IOException
745    */
746   @Test (expected=TableExistsException.class)
747   public void testTableNotFoundExceptionWithATable() throws IOException {
748     final byte [] name = Bytes.toBytes("testTableNotFoundExceptionWithATable");
749     TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
750     TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
751   }
752 
753   /**
754    * For HADOOP-2579
755    * @throws IOException
756    */
757   @Test (expected=TableNotFoundException.class)
758   public void testTableNotFoundExceptionWithoutAnyTables() throws IOException {
759     new HTable(TEST_UTIL.getConfiguration(),
760         "testTableNotFoundExceptionWithoutAnyTables");
761   }
762 
763   @Test
764   public void testHundredsOfTable() throws IOException{
765     final int times = 100;
766     HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
767     HColumnDescriptor fam2 = new HColumnDescriptor("fam2");
768     HColumnDescriptor fam3 = new HColumnDescriptor("fam3");
769 
770     for(int i = 0; i < times; i++) {
771       HTableDescriptor htd = new HTableDescriptor("table"+i);
772       htd.addFamily(fam1);
773       htd.addFamily(fam2);
774       htd.addFamily(fam3);
775       this.admin.createTable(htd);
776     }
777 
778     for(int i = 0; i < times; i++) {
779       String tableName = "table"+i;
780       this.admin.disableTable(tableName);
781       byte [] tableNameBytes = Bytes.toBytes(tableName);
782       assertTrue(this.admin.isTableDisabled(tableNameBytes));
783       this.admin.enableTable(tableName);
784       assertFalse(this.admin.isTableDisabled(tableNameBytes));
785       this.admin.disableTable(tableName);
786       assertTrue(this.admin.isTableDisabled(tableNameBytes));
787       this.admin.deleteTable(tableName);
788     }
789   }
790 }