View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.client;
20  
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertTrue;
25  import static org.junit.Assert.fail;
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.Set;
34  import java.util.concurrent.atomic.AtomicBoolean;
35  import java.util.concurrent.atomic.AtomicInteger;
36  import java.util.concurrent.atomic.AtomicReference;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.hadoop.hbase.HBaseTestingUtility;
41  import org.apache.hadoop.hbase.HColumnDescriptor;
42  import org.apache.hadoop.hbase.HConstants;
43  import org.apache.hadoop.hbase.HRegionInfo;
44  import org.apache.hadoop.hbase.HTableDescriptor;
45  import org.apache.hadoop.hbase.InvalidFamilyOperationException;
46  import org.apache.hadoop.hbase.testclassification.LargeTests;
47  import org.apache.hadoop.hbase.ServerName;
48  import org.apache.hadoop.hbase.TableName;
49  import org.apache.hadoop.hbase.TableNotDisabledException;
50  import org.apache.hadoop.hbase.TableNotFoundException;
51  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
52  import org.apache.hadoop.hbase.catalog.CatalogTracker;
53  import org.apache.hadoop.hbase.executor.EventHandler;
54  import org.apache.hadoop.hbase.util.Bytes;
55  import org.apache.hadoop.hbase.zookeeper.ZKTableReadOnly;
56  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
57  import org.apache.zookeeper.KeeperException;
58  import org.junit.After;
59  import org.junit.AfterClass;
60  import org.junit.Before;
61  import org.junit.BeforeClass;
62  import org.junit.Test;
63  import org.junit.experimental.categories.Category;
64  
65  /**
66   * Class to test HBaseAdmin.
67   * Spins up the minicluster once at test start and then takes it down afterward.
68   * Add any testing of HBaseAdmin functionality here.
69   */
70  @Category(LargeTests.class)
71  public class TestAdmin1 {
72    final Log LOG = LogFactory.getLog(getClass());
73    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
74    private HBaseAdmin admin;
75  
76    @BeforeClass
77    public static void setUpBeforeClass() throws Exception {
78      TEST_UTIL.getConfiguration().setBoolean("hbase.online.schema.update.enable", true);
79      TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
80      TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250);
81      TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 6);
82      TEST_UTIL.getConfiguration().setBoolean(
83          "hbase.master.enabletable.roundrobin", true);
84      TEST_UTIL.startMiniCluster(3);
85    }
86  
87    @AfterClass
88    public static void tearDownAfterClass() throws Exception {
89      TEST_UTIL.shutdownMiniCluster();
90    }
91  
92    @Before
93    public void setUp() throws Exception {
94      this.admin = TEST_UTIL.getHBaseAdmin();
95    }
96  
97    @After
98    public void tearDown() throws Exception {
99      for (HTableDescriptor htd : this.admin.listTables()) {
100       TEST_UTIL.deleteTable(htd.getName());
101     }
102   }
103 
104   @Test (timeout=300000)
105   public void testFailedCatalogTrackerGetCleansUpProperly()
106   throws ZooKeeperConnectionException, IOException {
107     // An HBaseAdmin that we can make fail when it goes to get catalogtracker.
108     final AtomicBoolean fail = new AtomicBoolean(false);
109     final AtomicReference<CatalogTracker> internalCt = new AtomicReference<CatalogTracker>();
110     HBaseAdmin doctoredAdmin = new HBaseAdmin(this.admin.getConfiguration()) {
111       @Override
112       protected CatalogTracker startCatalogTracker(CatalogTracker ct)
113       throws IOException, InterruptedException {
114         internalCt.set(ct);
115         super.startCatalogTracker(ct);
116         if (fail.get()) {
117           throw new IOException("Intentional test fail",
118             new KeeperException.ConnectionLossException());
119         }
120         return ct;
121       }
122     };
123     try {
124       CatalogTracker ct = doctoredAdmin.getCatalogTracker();
125       assertFalse(ct.isStopped());
126       doctoredAdmin.cleanupCatalogTracker(ct);
127       assertTrue(ct.isStopped());
128       // Now have mess with our doctored admin and make the start of catalog tracker 'fail'.
129       fail.set(true);
130       boolean expectedException = false;
131       try {
132         doctoredAdmin.getCatalogTracker();
133       } catch (IOException ioe) {
134         assertTrue(ioe.getCause() instanceof KeeperException.ConnectionLossException);
135         expectedException = true;
136       }
137       if (!expectedException) fail("Didn't get expected exception!");
138       // Assert that the internally made ct was properly shutdown.
139       assertTrue("Internal CatalogTracker not closed down", internalCt.get().isStopped());
140     } finally {
141       doctoredAdmin.close();
142     }
143   }
144 
145   @Test (timeout=300000)
146   public void testSplitFlushCompactUnknownTable() throws InterruptedException {
147     final String unknowntable = "fubar";
148     Exception exception = null;
149     try {
150       this.admin.compact(unknowntable);
151     } catch (IOException e) {
152       exception = e;
153     }
154     assertTrue(exception instanceof TableNotFoundException);
155 
156     exception = null;
157     try {
158       this.admin.flush(unknowntable);
159     } catch (IOException e) {
160       exception = e;
161     }
162     assertTrue(exception instanceof TableNotFoundException);
163 
164     exception = null;
165     try {
166       this.admin.split(unknowntable);
167     } catch (IOException e) {
168       exception = e;
169     }
170     assertTrue(exception instanceof TableNotFoundException);
171   }
172 
173   @Test (timeout=300000)
174   public void testDeleteEditUnknownColumnFamilyAndOrTable() throws IOException {
175     // Test we get exception if we try to
176     final String nonexistent = "nonexistent";
177     HColumnDescriptor nonexistentHcd = new HColumnDescriptor(nonexistent);
178     Exception exception = null;
179     try {
180       this.admin.addColumn(nonexistent, nonexistentHcd);
181     } catch (IOException e) {
182       exception = e;
183     }
184     assertTrue(exception instanceof TableNotFoundException);
185 
186     exception = null;
187     try {
188       this.admin.deleteTable(nonexistent);
189     } catch (IOException e) {
190       exception = e;
191     }
192     assertTrue(exception instanceof TableNotFoundException);
193 
194     exception = null;
195     try {
196       this.admin.deleteColumn(nonexistent, nonexistent);
197     } catch (IOException e) {
198       exception = e;
199     }
200     assertTrue(exception instanceof TableNotFoundException);
201 
202     exception = null;
203     try {
204       this.admin.disableTable(nonexistent);
205     } catch (IOException e) {
206       exception = e;
207     }
208     assertTrue(exception instanceof TableNotFoundException);
209 
210     exception = null;
211     try {
212       this.admin.enableTable(nonexistent);
213     } catch (IOException e) {
214       exception = e;
215     }
216     assertTrue(exception instanceof TableNotFoundException);
217 
218     exception = null;
219     try {
220       this.admin.modifyColumn(nonexistent, nonexistentHcd);
221     } catch (IOException e) {
222       exception = e;
223     }
224     assertTrue(exception instanceof TableNotFoundException);
225 
226     exception = null;
227     try {
228       HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(nonexistent));
229       this.admin.modifyTable(htd.getTableName(), htd);
230     } catch (IOException e) {
231       exception = e;
232     }
233     assertTrue(exception instanceof TableNotFoundException);
234 
235     // Now make it so at least the table exists and then do tests against a
236     // nonexistent column family -- see if we get right exceptions.
237     final String tableName =
238         "testDeleteEditUnknownColumnFamilyAndOrTable" + System.currentTimeMillis();
239     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
240     htd.addFamily(new HColumnDescriptor("cf"));
241     this.admin.createTable(htd);
242     try {
243       exception = null;
244       try {
245         this.admin.deleteColumn(htd.getTableName(), nonexistentHcd.getName());
246       } catch (IOException e) {
247         exception = e;
248       }
249       assertTrue("found=" + exception.getClass().getName(),
250           exception instanceof InvalidFamilyOperationException);
251 
252       exception = null;
253       try {
254         this.admin.modifyColumn(htd.getTableName(), nonexistentHcd);
255       } catch (IOException e) {
256         exception = e;
257       }
258       assertTrue("found=" + exception.getClass().getName(),
259           exception instanceof InvalidFamilyOperationException);
260     } finally {
261       this.admin.disableTable(tableName);
262       this.admin.deleteTable(tableName);
263     }
264   }
265 
266   @Test (timeout=300000)
267   public void testDisableAndEnableTable() throws IOException {
268     final byte [] row = Bytes.toBytes("row");
269     final byte [] qualifier = Bytes.toBytes("qualifier");
270     final byte [] value = Bytes.toBytes("value");
271     final byte [] table = Bytes.toBytes("testDisableAndEnableTable");
272     HTable ht = TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
273     Put put = new Put(row);
274     put.add(HConstants.CATALOG_FAMILY, qualifier, value);
275     ht.put(put);
276     Get get = new Get(row);
277     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
278     ht.get(get);
279 
280     this.admin.disableTable(ht.getName());
281     assertTrue("Table must be disabled.", TEST_UTIL.getHBaseCluster()
282         .getMaster().getAssignmentManager().getZKTable().isDisabledTable(
283             ht.getName()));
284 
285     // Test that table is disabled
286     get = new Get(row);
287     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
288     boolean ok = false;
289     try {
290       ht.get(get);
291     } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
292       ok = true;
293     }
294     assertTrue(ok);
295     this.admin.enableTable(table);
296     assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster()
297         .getMaster().getAssignmentManager().getZKTable().isEnabledTable(
298             ht.getName()));
299 
300     // Test that table is enabled
301     try {
302       ht.get(get);
303     } catch (RetriesExhaustedException e) {
304       ok = false;
305     }
306     assertTrue(ok);
307     ht.close();
308   }
309 
310   @Test (timeout=300000)
311   public void testDisableAndEnableTables() throws IOException {
312     final byte [] row = Bytes.toBytes("row");
313     final byte [] qualifier = Bytes.toBytes("qualifier");
314     final byte [] value = Bytes.toBytes("value");
315     final byte [] table1 = Bytes.toBytes("testDisableAndEnableTable1");
316     final byte [] table2 = Bytes.toBytes("testDisableAndEnableTable2");
317     HTable ht1 = TEST_UTIL.createTable(table1, HConstants.CATALOG_FAMILY);
318     HTable ht2 = TEST_UTIL.createTable(table2, HConstants.CATALOG_FAMILY);
319     Put put = new Put(row);
320     put.add(HConstants.CATALOG_FAMILY, qualifier, value);
321     ht1.put(put);
322     ht2.put(put);
323     Get get = new Get(row);
324     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
325     ht1.get(get);
326     ht2.get(get);
327 
328     this.admin.disableTables("testDisableAndEnableTable.*");
329 
330     // Test that tables are disabled
331     get = new Get(row);
332     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
333     boolean ok = false;
334     try {
335       ht1.get(get);
336       ht2.get(get);
337     } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
338       ok = true;
339     }
340 
341     assertTrue(ok);
342     this.admin.enableTables("testDisableAndEnableTable.*");
343 
344     // Test that tables are enabled
345     try {
346       ht1.get(get);
347     } catch (IOException e) {
348       ok = false;
349     }
350     try {
351       ht2.get(get);
352     } catch (IOException e) {
353       ok = false;
354     }
355     assertTrue(ok);
356 
357     ht1.close();
358     ht2.close();
359   }
360 
361   @Test (timeout=300000)
362   public void testCreateTable() throws IOException {
363     HTableDescriptor [] tables = admin.listTables();
364     int numTables = tables.length;
365     TEST_UTIL.createTable(Bytes.toBytes("testCreateTable"),
366       HConstants.CATALOG_FAMILY).close();
367     tables = this.admin.listTables();
368     assertEquals(numTables + 1, tables.length);
369     assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster()
370         .getMaster().getAssignmentManager().getZKTable().isEnabledTable(
371             TableName.valueOf("testCreateTable")));
372   }
373 
374   @Test (timeout=300000)
375   public void testTruncateTable() throws IOException {
376     testTruncateTable(TableName.valueOf("testTruncateTable"), false);
377   }
378 
379   @Test (timeout=300000)
380   public void testTruncateTablePreservingSplits() throws IOException {
381     testTruncateTable(TableName.valueOf("testTruncateTablePreservingSplits"), true);
382   }
383 
384   private void testTruncateTable(final TableName tableName, boolean preserveSplits)
385       throws IOException {
386     byte[][] splitKeys = new byte[2][];
387     splitKeys[0] = Bytes.toBytes(4);
388     splitKeys[1] = Bytes.toBytes(8);
389 
390     // Create & Fill the table
391     HTable table = TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY, splitKeys);
392     try {
393       TEST_UTIL.loadNumericRows(table, HConstants.CATALOG_FAMILY, 0, 10);
394       assertEquals(10, TEST_UTIL.countRows(table));
395     } finally {
396       table.close();
397     }
398     assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
399 
400     // Truncate & Verify
401     this.admin.disableTable(tableName);
402     this.admin.truncateTable(tableName, preserveSplits);
403     table = new HTable(TEST_UTIL.getConfiguration(), tableName);
404     try {
405       assertEquals(0, TEST_UTIL.countRows(table));
406     } finally {
407       table.close();
408     }
409     if (preserveSplits) {
410       assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
411     } else {
412       assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
413     }
414   }
415 
416   @Test (timeout=300000)
417   public void testGetTableDescriptor() throws IOException {
418     HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
419     HColumnDescriptor fam2 = new HColumnDescriptor("fam2");
420     HColumnDescriptor fam3 = new HColumnDescriptor("fam3");
421     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("myTestTable"));
422     htd.addFamily(fam1);
423     htd.addFamily(fam2);
424     htd.addFamily(fam3);
425     this.admin.createTable(htd);
426     HTable table = new HTable(TEST_UTIL.getConfiguration(), "myTestTable");
427     HTableDescriptor confirmedHtd = table.getTableDescriptor();
428     assertEquals(htd.compareTo(confirmedHtd), 0);
429     table.close();
430   }
431 
432   @Test (timeout=300000)
433   public void testHColumnValidName() {
434        boolean exceptionThrown;
435        try {
436          new HColumnDescriptor("\\test\\abc");
437        } catch(IllegalArgumentException iae) {
438            exceptionThrown = true;
439            assertTrue(exceptionThrown);
440        }
441    }
442 
443   /**
444    * Verify schema modification takes.
445    * @throws IOException
446    * @throws InterruptedException
447    */
448   @Test (timeout=300000)
449   public void testOnlineChangeTableSchema() throws IOException, InterruptedException {
450     final TableName tableName =
451         TableName.valueOf("changeTableSchemaOnline");
452     TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean(
453         "hbase.online.schema.update.enable", true);
454     HTableDescriptor [] tables = admin.listTables();
455     int numTables = tables.length;
456     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
457     tables = this.admin.listTables();
458     assertEquals(numTables + 1, tables.length);
459 
460     // FIRST, do htabledescriptor changes.
461     HTableDescriptor htd = this.admin.getTableDescriptor(tableName);
462     // Make a copy and assert copy is good.
463     HTableDescriptor copy = new HTableDescriptor(htd);
464     assertTrue(htd.equals(copy));
465     // Now amend the copy. Introduce differences.
466     long newFlushSize = htd.getMemStoreFlushSize() / 2;
467     if (newFlushSize <=0) {
468       newFlushSize = HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE / 2;
469     }
470     copy.setMemStoreFlushSize(newFlushSize);
471     final String key = "anyoldkey";
472     assertTrue(htd.getValue(key) == null);
473     copy.setValue(key, key);
474     boolean expectedException = false;
475     try {
476       admin.modifyTable(tableName, copy);
477     } catch (TableNotDisabledException re) {
478       expectedException = true;
479     }
480     assertFalse(expectedException);
481     HTableDescriptor modifiedHtd = this.admin.getTableDescriptor(tableName);
482     assertFalse(htd.equals(modifiedHtd));
483     assertTrue(copy.equals(modifiedHtd));
484     assertEquals(newFlushSize, modifiedHtd.getMemStoreFlushSize());
485     assertEquals(key, modifiedHtd.getValue(key));
486 
487     // Now work on column family changes.
488     int countOfFamilies = modifiedHtd.getFamilies().size();
489     assertTrue(countOfFamilies > 0);
490     HColumnDescriptor hcd = modifiedHtd.getFamilies().iterator().next();
491     int maxversions = hcd.getMaxVersions();
492     final int newMaxVersions = maxversions + 1;
493     hcd.setMaxVersions(newMaxVersions);
494     final byte [] hcdName = hcd.getName();
495     expectedException = false;
496     try {
497       this.admin.modifyColumn(tableName, hcd);
498     } catch (TableNotDisabledException re) {
499       expectedException = true;
500     }
501     assertFalse(expectedException);
502     modifiedHtd = this.admin.getTableDescriptor(tableName);
503     HColumnDescriptor modifiedHcd = modifiedHtd.getFamily(hcdName);
504     assertEquals(newMaxVersions, modifiedHcd.getMaxVersions());
505 
506     // Try adding a column
507     assertFalse(this.admin.isTableDisabled(tableName));
508     final String xtracolName = "xtracol";
509     HColumnDescriptor xtracol = new HColumnDescriptor(xtracolName);
510     xtracol.setValue(xtracolName, xtracolName);
511     expectedException = false;
512     try {
513       this.admin.addColumn(tableName, xtracol);
514     } catch (TableNotDisabledException re) {
515       expectedException = true;
516     }
517     // Add column should work even if the table is enabled
518     assertFalse(expectedException);
519     modifiedHtd = this.admin.getTableDescriptor(tableName);
520     hcd = modifiedHtd.getFamily(xtracol.getName());
521     assertTrue(hcd != null);
522     assertTrue(hcd.getValue(xtracolName).equals(xtracolName));
523 
524     // Delete the just-added column.
525     this.admin.deleteColumn(tableName, xtracol.getName());
526     modifiedHtd = this.admin.getTableDescriptor(tableName);
527     hcd = modifiedHtd.getFamily(xtracol.getName());
528     assertTrue(hcd == null);
529 
530     // Delete the table
531     this.admin.disableTable(tableName);
532     this.admin.deleteTable(tableName);
533     this.admin.listTables();
534     assertFalse(this.admin.tableExists(tableName));
535   }
536 
537   @Test (timeout=300000)
538   public void testShouldFailOnlineSchemaUpdateIfOnlineSchemaIsNotEnabled()
539       throws Exception {
540     final byte[] tableName = Bytes.toBytes("changeTableSchemaOnlineFailure");
541     TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean(
542         "hbase.online.schema.update.enable", false);
543     HTableDescriptor[] tables = admin.listTables();
544     int numTables = tables.length;
545     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
546     tables = this.admin.listTables();
547     assertEquals(numTables + 1, tables.length);
548 
549     // FIRST, do htabledescriptor changes.
550     HTableDescriptor htd = this.admin.getTableDescriptor(tableName);
551     // Make a copy and assert copy is good.
552     HTableDescriptor copy = new HTableDescriptor(htd);
553     assertTrue(htd.equals(copy));
554     // Now amend the copy. Introduce differences.
555     long newFlushSize = htd.getMemStoreFlushSize() / 2;
556     if (newFlushSize <=0) {
557       newFlushSize = HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE / 2;
558     }
559     copy.setMemStoreFlushSize(newFlushSize);
560     final String key = "anyoldkey";
561     assertTrue(htd.getValue(key) == null);
562     copy.setValue(key, key);
563     boolean expectedException = false;
564     try {
565       admin.modifyTable(tableName, copy);
566     } catch (TableNotDisabledException re) {
567       expectedException = true;
568     }
569     assertTrue("Online schema update should not happen.", expectedException);
570 
571     // Reset the value for the other tests
572     TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean(
573         "hbase.online.schema.update.enable", true);
574   }
575 
576   /**
577    * Listens for when an event is done in Master.
578    */
579   static class DoneListener implements EventHandler.EventHandlerListener {
580     private final AtomicBoolean done;
581 
582     DoneListener(final AtomicBoolean done) {
583       super();
584       this.done = done;
585     }
586 
587     @Override
588     public void afterProcess(EventHandler event) {
589       this.done.set(true);
590       synchronized (this.done) {
591         // Wake anyone waiting on this value to change.
592         this.done.notifyAll();
593       }
594     }
595 
596     @Override
597     public void beforeProcess(EventHandler event) {
598       // continue
599     }
600   }
601 
602   @SuppressWarnings("deprecation")
603   protected void verifyRoundRobinDistribution(HTable ht, int expectedRegions) throws IOException {
604     int numRS = ht.getConnection().getCurrentNrHRS();
605     Map<HRegionInfo, ServerName> regions = ht.getRegionLocations();
606     Map<ServerName, List<HRegionInfo>> server2Regions = new HashMap<ServerName, List<HRegionInfo>>();
607     for (Map.Entry<HRegionInfo, ServerName> entry : regions.entrySet()) {
608       ServerName server = entry.getValue();
609       List<HRegionInfo> regs = server2Regions.get(server);
610       if (regs == null) {
611         regs = new ArrayList<HRegionInfo>();
612         server2Regions.put(server, regs);
613       }
614       regs.add(entry.getKey());
615     }
616     float average = (float) expectedRegions/numRS;
617     int min = (int)Math.floor(average);
618     int max = (int)Math.ceil(average);
619     for (List<HRegionInfo> regionList : server2Regions.values()) {
620       assertTrue(regionList.size() == min || regionList.size() == max);
621     }
622   }
623 
624   @Test (timeout=300000)
625   public void testCreateTableNumberOfRegions() throws IOException, InterruptedException {
626     byte[] tableName = Bytes.toBytes("testCreateTableNumberOfRegions");
627     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
628     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
629     admin.createTable(desc);
630     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
631     Map<HRegionInfo, ServerName> regions = ht.getRegionLocations();
632     assertEquals("Table should have only 1 region", 1, regions.size());
633     ht.close();
634 
635     byte [] TABLE_2 = Bytes.add(tableName, Bytes.toBytes("_2"));
636     desc = new HTableDescriptor(TableName.valueOf(TABLE_2));
637     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
638     admin.createTable(desc, new byte[][]{new byte[]{42}});
639     HTable ht2 = new HTable(TEST_UTIL.getConfiguration(), TABLE_2);
640     regions = ht2.getRegionLocations();
641     assertEquals("Table should have only 2 region", 2, regions.size());
642     ht2.close();
643 
644     byte [] TABLE_3 = Bytes.add(tableName, Bytes.toBytes("_3"));
645     desc = new HTableDescriptor(TableName.valueOf(TABLE_3));
646     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
647     admin.createTable(desc, "a".getBytes(), "z".getBytes(), 3);
648     HTable ht3 = new HTable(TEST_UTIL.getConfiguration(), TABLE_3);
649     regions = ht3.getRegionLocations();
650     assertEquals("Table should have only 3 region", 3, regions.size());
651     ht3.close();
652 
653     byte [] TABLE_4 = Bytes.add(tableName, Bytes.toBytes("_4"));
654     desc = new HTableDescriptor(TableName.valueOf(TABLE_4));
655     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
656     try {
657       admin.createTable(desc, "a".getBytes(), "z".getBytes(), 2);
658       fail("Should not be able to create a table with only 2 regions using this API.");
659     } catch (IllegalArgumentException eae) {
660     // Expected
661     }
662 
663     byte [] TABLE_5 = Bytes.add(tableName, Bytes.toBytes("_5"));
664     desc = new HTableDescriptor(TableName.valueOf(TABLE_5));
665     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
666     admin.createTable(desc, new byte[] {1}, new byte[] {127}, 16);
667     HTable ht5 = new HTable(TEST_UTIL.getConfiguration(), TABLE_5);
668     regions = ht5.getRegionLocations();
669     assertEquals("Table should have 16 region", 16, regions.size());
670     ht5.close();
671   }
672 
673   @Test (timeout=300000)
674   public void testCreateTableWithRegions() throws IOException, InterruptedException {
675 
676     byte[] tableName = Bytes.toBytes("testCreateTableWithRegions");
677 
678     byte [][] splitKeys = {
679         new byte [] { 1, 1, 1 },
680         new byte [] { 2, 2, 2 },
681         new byte [] { 3, 3, 3 },
682         new byte [] { 4, 4, 4 },
683         new byte [] { 5, 5, 5 },
684         new byte [] { 6, 6, 6 },
685         new byte [] { 7, 7, 7 },
686         new byte [] { 8, 8, 8 },
687         new byte [] { 9, 9, 9 },
688     };
689     int expectedRegions = splitKeys.length + 1;
690 
691     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
692     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
693     admin.createTable(desc, splitKeys);
694 
695     boolean tableAvailable = admin.isTableAvailable(Bytes.toString(tableName), splitKeys);
696     assertTrue("Table should be created with splitKyes + 1 rows in META", tableAvailable);
697 
698     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
699     Map<HRegionInfo, ServerName> regions = ht.getRegionLocations();
700     assertEquals("Tried to create " + expectedRegions + " regions " +
701         "but only found " + regions.size(),
702         expectedRegions, regions.size());
703     System.err.println("Found " + regions.size() + " regions");
704 
705     Iterator<HRegionInfo> hris = regions.keySet().iterator();
706     HRegionInfo hri = hris.next();
707     assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
708     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0]));
709     hri = hris.next();
710     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0]));
711     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1]));
712     hri = hris.next();
713     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1]));
714     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2]));
715     hri = hris.next();
716     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2]));
717     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3]));
718     hri = hris.next();
719     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3]));
720     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4]));
721     hri = hris.next();
722     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4]));
723     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5]));
724     hri = hris.next();
725     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5]));
726     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6]));
727     hri = hris.next();
728     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6]));
729     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7]));
730     hri = hris.next();
731     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7]));
732     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8]));
733     hri = hris.next();
734     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8]));
735     assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
736 
737     verifyRoundRobinDistribution(ht, expectedRegions);
738     ht.close();
739 
740     // Now test using start/end with a number of regions
741 
742     // Use 80 bit numbers to make sure we aren't limited
743     byte [] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
744     byte [] endKey =   { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
745 
746     // Splitting into 10 regions, we expect (null,1) ... (9, null)
747     // with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle
748 
749     expectedRegions = 10;
750 
751     byte [] TABLE_2 = Bytes.add(tableName, Bytes.toBytes("_2"));
752 
753     desc = new HTableDescriptor(TableName.valueOf(TABLE_2));
754     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
755     admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
756     admin.createTable(desc, startKey, endKey, expectedRegions);
757 
758     HTable ht2 = new HTable(TEST_UTIL.getConfiguration(), TABLE_2);
759     regions = ht2.getRegionLocations();
760     assertEquals("Tried to create " + expectedRegions + " regions " +
761         "but only found " + regions.size(),
762         expectedRegions, regions.size());
763     System.err.println("Found " + regions.size() + " regions");
764 
765     hris = regions.keySet().iterator();
766     hri = hris.next();
767     assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
768     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {1,1,1,1,1,1,1,1,1,1}));
769     hri = hris.next();
770     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {1,1,1,1,1,1,1,1,1,1}));
771     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {2,2,2,2,2,2,2,2,2,2}));
772     hri = hris.next();
773     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {2,2,2,2,2,2,2,2,2,2}));
774     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {3,3,3,3,3,3,3,3,3,3}));
775     hri = hris.next();
776     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {3,3,3,3,3,3,3,3,3,3}));
777     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {4,4,4,4,4,4,4,4,4,4}));
778     hri = hris.next();
779     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {4,4,4,4,4,4,4,4,4,4}));
780     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {5,5,5,5,5,5,5,5,5,5}));
781     hri = hris.next();
782     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {5,5,5,5,5,5,5,5,5,5}));
783     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {6,6,6,6,6,6,6,6,6,6}));
784     hri = hris.next();
785     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {6,6,6,6,6,6,6,6,6,6}));
786     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {7,7,7,7,7,7,7,7,7,7}));
787     hri = hris.next();
788     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {7,7,7,7,7,7,7,7,7,7}));
789     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {8,8,8,8,8,8,8,8,8,8}));
790     hri = hris.next();
791     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {8,8,8,8,8,8,8,8,8,8}));
792     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {9,9,9,9,9,9,9,9,9,9}));
793     hri = hris.next();
794     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {9,9,9,9,9,9,9,9,9,9}));
795     assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
796 
797     verifyRoundRobinDistribution(ht2, expectedRegions);
798     ht2.close();
799 
800     // Try once more with something that divides into something infinite
801 
802     startKey = new byte [] { 0, 0, 0, 0, 0, 0 };
803     endKey = new byte [] { 1, 0, 0, 0, 0, 0 };
804 
805     expectedRegions = 5;
806 
807     byte [] TABLE_3 = Bytes.add(tableName, Bytes.toBytes("_3"));
808 
809     desc = new HTableDescriptor(TableName.valueOf(TABLE_3));
810     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
811     admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
812     admin.createTable(desc, startKey, endKey, expectedRegions);
813 
814 
815     HTable ht3 = new HTable(TEST_UTIL.getConfiguration(), TABLE_3);
816     regions = ht3.getRegionLocations();
817     assertEquals("Tried to create " + expectedRegions + " regions " +
818         "but only found " + regions.size(),
819         expectedRegions, regions.size());
820     System.err.println("Found " + regions.size() + " regions");
821 
822     verifyRoundRobinDistribution(ht3, expectedRegions);
823     ht3.close();
824 
825 
826     // Try an invalid case where there are duplicate split keys
827     splitKeys = new byte [][] {
828         new byte [] { 1, 1, 1 },
829         new byte [] { 2, 2, 2 },
830         new byte [] { 3, 3, 3 },
831         new byte [] { 2, 2, 2 }
832     };
833 
834     byte [] TABLE_4 = Bytes.add(tableName, Bytes.toBytes("_4"));
835     desc = new HTableDescriptor(TableName.valueOf(TABLE_4));
836     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
837     HBaseAdmin ladmin = new HBaseAdmin(TEST_UTIL.getConfiguration());
838     try {
839       ladmin.createTable(desc, splitKeys);
840       assertTrue("Should not be able to create this table because of " +
841           "duplicate split keys", false);
842     } catch(IllegalArgumentException iae) {
843       // Expected
844     }
845     ladmin.close();
846   }
847 
848   @Test (timeout=300000)
849   public void testTableAvailableWithRandomSplitKeys() throws Exception {
850     byte[] tableName = Bytes.toBytes("testTableAvailableWithRandomSplitKeys");
851     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
852     desc.addFamily(new HColumnDescriptor("col"));
853     byte[][] splitKeys = new byte[1][];
854     splitKeys = new byte [][] {
855         new byte [] { 1, 1, 1 },
856         new byte [] { 2, 2, 2 }
857     };
858     admin.createTable(desc);
859     boolean tableAvailable = admin.isTableAvailable(Bytes.toString(tableName), splitKeys);
860     assertFalse("Table should be created with 1 row in META", tableAvailable);
861   }
862 
863   @Test (timeout=300000)
864   public void testCreateTableWithOnlyEmptyStartRow() throws IOException {
865     byte[] tableName = Bytes.toBytes("testCreateTableWithOnlyEmptyStartRow");
866     byte[][] splitKeys = new byte[1][];
867     splitKeys[0] = HConstants.EMPTY_BYTE_ARRAY;
868     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
869     desc.addFamily(new HColumnDescriptor("col"));
870     try {
871       admin.createTable(desc, splitKeys);
872       fail("Test case should fail as empty split key is passed.");
873     } catch (IllegalArgumentException e) {
874     }
875   }
876 
877   @Test (timeout=300000)
878   public void testCreateTableWithEmptyRowInTheSplitKeys() throws IOException{
879     byte[] tableName = Bytes.toBytes("testCreateTableWithEmptyRowInTheSplitKeys");
880     byte[][] splitKeys = new byte[3][];
881     splitKeys[0] = "region1".getBytes();
882     splitKeys[1] = HConstants.EMPTY_BYTE_ARRAY;
883     splitKeys[2] = "region2".getBytes();
884     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
885     desc.addFamily(new HColumnDescriptor("col"));
886     try {
887       admin.createTable(desc, splitKeys);
888       fail("Test case should fail as empty split key is passed.");
889     } catch (IllegalArgumentException e) {
890       LOG.info("Expected ", e);
891     }
892   }
893 
894   @Test (timeout=120000)
895   public void testTableExist() throws IOException {
896     final byte [] table = Bytes.toBytes("testTableExist");
897     boolean exist;
898     exist = this.admin.tableExists(table);
899     assertEquals(false, exist);
900     TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
901     exist = this.admin.tableExists(table);
902     assertEquals(true, exist);
903   }
904 
905   /**
906    * Tests forcing split from client and having scanners successfully ride over split.
907    * @throws Exception
908    * @throws IOException
909    */
910   @Test (timeout=300000)
911   public void testForceSplit() throws Exception {
912     byte[][] familyNames = new byte[][] { Bytes.toBytes("cf") };
913     int[] rowCounts = new int[] { 6000 };
914     int numVersions = HColumnDescriptor.DEFAULT_VERSIONS;
915     int blockSize = 256;
916     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
917 
918     byte[] splitKey = Bytes.toBytes(3500);
919     splitTest(splitKey, familyNames, rowCounts, numVersions, blockSize);
920   }
921 
922   /**
923    * Test retain assignment on enableTable.
924    *
925    * @throws IOException
926    */
927   @Test (timeout=300000)
928   public void testEnableTableRetainAssignment() throws IOException {
929     byte[] tableName = Bytes.toBytes("testEnableTableAssignment");
930     byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 },
931         new byte[] { 3, 3, 3 }, new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 },
932         new byte[] { 6, 6, 6 }, new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 },
933         new byte[] { 9, 9, 9 } };
934     int expectedRegions = splitKeys.length + 1;
935     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
936     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
937     admin.createTable(desc, splitKeys);
938     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
939     Map<HRegionInfo, ServerName> regions = ht.getRegionLocations();
940     ht.close();
941     assertEquals("Tried to create " + expectedRegions + " regions "
942         + "but only found " + regions.size(), expectedRegions, regions.size());
943     // Disable table.
944     admin.disableTable(tableName);
945     // Enable table, use retain assignment to assign regions.
946     admin.enableTable(tableName);
947     Map<HRegionInfo, ServerName> regions2 = ht.getRegionLocations();
948 
949     // Check the assignment.
950     assertEquals(regions.size(), regions2.size());
951     for (Map.Entry<HRegionInfo, ServerName> entry : regions.entrySet()) {
952       assertEquals(regions2.get(entry.getKey()), entry.getValue());
953     }
954   }
955 
956   /**
957    * Multi-family scenario. Tests forcing split from client and
958    * having scanners successfully ride over split.
959    * @throws Exception
960    * @throws IOException
961    */
962   @Test (timeout=300000)
963   public void testForceSplitMultiFamily() throws Exception {
964     int numVersions = HColumnDescriptor.DEFAULT_VERSIONS;
965 
966     // use small HFile block size so that we can have lots of blocks in HFile
967     // Otherwise, if there is only one block,
968     // HFileBlockIndex.midKey()'s value == startKey
969     int blockSize = 256;
970     byte[][] familyNames = new byte[][] { Bytes.toBytes("cf1"),
971       Bytes.toBytes("cf2") };
972 
973     // one of the column families isn't splittable
974     int[] rowCounts = new int[] { 6000, 1 };
975     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
976 
977     rowCounts = new int[] { 1, 6000 };
978     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
979 
980     // one column family has much smaller data than the other
981     // the split key should be based on the largest column family
982     rowCounts = new int[] { 6000, 300 };
983     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
984 
985     rowCounts = new int[] { 300, 6000 };
986     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
987 
988   }
989 
990   void splitTest(byte[] splitPoint, byte[][] familyNames, int[] rowCounts,
991     int numVersions, int blockSize) throws Exception {
992     TableName tableName = TableName.valueOf("testForceSplit");
993     StringBuilder sb = new StringBuilder();
994     // Add tail to String so can see better in logs where a test is running.
995     for (int i = 0; i < rowCounts.length; i++) {
996       sb.append("_").append(Integer.toString(rowCounts[i]));
997     }
998     assertFalse(admin.tableExists(tableName));
999     final HTable table = TEST_UTIL.createTable(tableName, familyNames,
1000       numVersions, blockSize);
1001 
1002     int rowCount = 0;
1003     byte[] q = new byte[0];
1004 
1005     // insert rows into column families. The number of rows that have values
1006     // in a specific column family is decided by rowCounts[familyIndex]
1007     for (int index = 0; index < familyNames.length; index++) {
1008       ArrayList<Put> puts = new ArrayList<Put>(rowCounts[index]);
1009       for (int i = 0; i < rowCounts[index]; i++) {
1010         byte[] k = Bytes.toBytes(i);
1011         Put put = new Put(k);
1012         put.add(familyNames[index], q, k);
1013         puts.add(put);
1014       }
1015       table.put(puts);
1016 
1017       if ( rowCount < rowCounts[index] ) {
1018         rowCount = rowCounts[index];
1019       }
1020     }
1021 
1022     // get the initial layout (should just be one region)
1023     Map<HRegionInfo, ServerName> m = table.getRegionLocations();
1024     LOG.info("Initial regions (" + m.size() + "): " + m);
1025     assertTrue(m.size() == 1);
1026 
1027     // Verify row count
1028     Scan scan = new Scan();
1029     ResultScanner scanner = table.getScanner(scan);
1030     int rows = 0;
1031     for(@SuppressWarnings("unused") Result result : scanner) {
1032       rows++;
1033     }
1034     scanner.close();
1035     assertEquals(rowCount, rows);
1036 
1037     // Have an outstanding scan going on to make sure we can scan over splits.
1038     scan = new Scan();
1039     scanner = table.getScanner(scan);
1040     // Scan first row so we are into first region before split happens.
1041     scanner.next();
1042 
1043     // Split the table
1044     this.admin.split(tableName.getName(), splitPoint);
1045 
1046     final AtomicInteger count = new AtomicInteger(0);
1047     Thread t = new Thread("CheckForSplit") {
1048       public void run() {
1049         for (int i = 0; i < 20; i++) {
1050           try {
1051             sleep(1000);
1052           } catch (InterruptedException e) {
1053             continue;
1054           }
1055           // check again    table = new HTable(conf, tableName);
1056           Map<HRegionInfo, ServerName> regions = null;
1057           try {
1058             regions = table.getRegionLocations();
1059           } catch (IOException e) {
1060             e.printStackTrace();
1061           }
1062           if (regions == null) continue;
1063           count.set(regions.size());
1064           if (count.get() >= 2) {
1065             LOG.info("Found: " + regions);
1066             break;
1067           }
1068           LOG.debug("Cycle waiting on split");
1069         }
1070         LOG.debug("CheckForSplit thread exited, current region count: " + count.get());
1071       }
1072     };
1073     t.setPriority(Thread.NORM_PRIORITY - 2);
1074     t.start();
1075     t.join();
1076 
1077     // Verify row count
1078     rows = 1; // We counted one row above.
1079     for (@SuppressWarnings("unused") Result result : scanner) {
1080       rows++;
1081       if (rows > rowCount) {
1082         scanner.close();
1083         assertTrue("Scanned more than expected (" + rowCount + ")", false);
1084       }
1085     }
1086     scanner.close();
1087     assertEquals(rowCount, rows);
1088 
1089     Map<HRegionInfo, ServerName> regions = null;
1090     try {
1091       regions = table.getRegionLocations();
1092     } catch (IOException e) {
1093       e.printStackTrace();
1094     }
1095     assertEquals(2, regions.size());
1096     Set<HRegionInfo> hRegionInfos = regions.keySet();
1097     HRegionInfo[] r = hRegionInfos.toArray(new HRegionInfo[hRegionInfos.size()]);
1098     if (splitPoint != null) {
1099       // make sure the split point matches our explicit configuration
1100       assertEquals(Bytes.toString(splitPoint),
1101           Bytes.toString(r[0].getEndKey()));
1102       assertEquals(Bytes.toString(splitPoint),
1103           Bytes.toString(r[1].getStartKey()));
1104       LOG.debug("Properly split on " + Bytes.toString(splitPoint));
1105     } else {
1106       if (familyNames.length > 1) {
1107         int splitKey = Bytes.toInt(r[0].getEndKey());
1108         // check if splitKey is based on the largest column family
1109         // in terms of it store size
1110         int deltaForLargestFamily = Math.abs(rowCount/2 - splitKey);
1111         LOG.debug("SplitKey=" + splitKey + "&deltaForLargestFamily=" + deltaForLargestFamily +
1112           ", r=" + r[0]);
1113         for (int index = 0; index < familyNames.length; index++) {
1114           int delta = Math.abs(rowCounts[index]/2 - splitKey);
1115           if (delta < deltaForLargestFamily) {
1116             assertTrue("Delta " + delta + " for family " + index
1117               + " should be at least deltaForLargestFamily " + deltaForLargestFamily,
1118               false);
1119           }
1120         }
1121       }
1122     }
1123     TEST_UTIL.deleteTable(tableName);
1124     table.close();
1125   }
1126 
1127   /**
1128    * HADOOP-2156
1129    * @throws IOException
1130    */
1131   @SuppressWarnings("deprecation")
1132   @Test (expected=IllegalArgumentException.class, timeout=300000)
1133   public void testEmptyHTableDescriptor() throws IOException {
1134     this.admin.createTable(new HTableDescriptor());
1135   }
1136 
1137   @Test (expected=IllegalArgumentException.class, timeout=300000)
1138   public void testInvalidHColumnDescriptor() throws IOException {
1139      new HColumnDescriptor("/cfamily/name");
1140   }
1141 
1142   @Test (timeout=300000)
1143   public void testEnableDisableAddColumnDeleteColumn() throws Exception {
1144     ZooKeeperWatcher zkw = HBaseTestingUtility.getZooKeeperWatcher(TEST_UTIL);
1145     TableName tableName = TableName.valueOf("testMasterAdmin");
1146     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
1147     while (!ZKTableReadOnly.isEnabledTable(zkw,
1148         TableName.valueOf("testMasterAdmin"))) {
1149       Thread.sleep(10);
1150     }
1151     this.admin.disableTable(tableName);
1152     try {
1153       new HTable(TEST_UTIL.getConfiguration(), tableName);
1154     } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
1155       //expected
1156     }
1157 
1158     this.admin.addColumn(tableName, new HColumnDescriptor("col2"));
1159     this.admin.enableTable(tableName);
1160     try {
1161       this.admin.deleteColumn(tableName, Bytes.toBytes("col2"));
1162     } catch (TableNotDisabledException e) {
1163       LOG.info(e);
1164     }
1165     this.admin.disableTable(tableName);
1166     this.admin.deleteTable(tableName);
1167   }
1168 }