1   /**
2    * Copyright 2011 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  import static org.junit.Assert.fail;
27  
28  import java.io.IOException;
29  import java.util.ArrayList;
30  import java.util.Collections;
31  import java.util.Comparator;
32  import java.util.HashMap;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.concurrent.atomic.AtomicBoolean;
37  import java.util.concurrent.atomic.AtomicInteger;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.hadoop.conf.Configuration;
42  import org.apache.hadoop.hbase.*;
43  import org.apache.hadoop.hbase.catalog.CatalogTracker;
44  import org.apache.hadoop.hbase.executor.EventHandler;
45  import org.apache.hadoop.hbase.executor.EventHandler.EventType;
46  import org.apache.hadoop.hbase.executor.ExecutorService;
47  import org.apache.hadoop.hbase.master.MasterServices;
48  import org.apache.hadoop.hbase.regionserver.HRegion;
49  import org.apache.hadoop.hbase.regionserver.HRegionServer;
50  import org.apache.hadoop.hbase.regionserver.wal.HLogUtilsForTests;
51  import org.apache.hadoop.hbase.InvalidFamilyOperationException;
52  import org.apache.hadoop.hbase.util.Bytes;
53  import org.apache.hadoop.hbase.util.Pair;
54  import org.apache.hadoop.hbase.zookeeper.ZKTableReadOnly;
55  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
56  import org.junit.*;
57  import org.junit.experimental.categories.Category;
58  
59  
60  /**
61   * Class to test HBaseAdmin.
62   * Spins up the minicluster once at test start and then takes it down afterward.
63   * Add any testing of HBaseAdmin functionality here.
64   */
65  @Category(LargeTests.class)
66  public class TestAdmin {
67    final Log LOG = LogFactory.getLog(getClass());
68    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
69    private HBaseAdmin admin;
70  
71    @BeforeClass
72    public static void setUpBeforeClass() throws Exception {
73      TEST_UTIL.getConfiguration().setBoolean("hbase.online.schema.update.enable", true);
74      TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
75      TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250);
76      TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 6);
77      TEST_UTIL.getConfiguration().setBoolean(
78          "hbase.master.enabletable.roundrobin", true);
79      TEST_UTIL.startMiniCluster(3);
80    }
81  
82    @AfterClass
83    public static void tearDownAfterClass() throws Exception {
84      TEST_UTIL.shutdownMiniCluster();
85    }
86  
87    @Before
88    public void setUp() throws Exception {
89      this.admin = TEST_UTIL.getHBaseAdmin();
90    }
91  
92    @After
93    public void tearDown() throws Exception {
94    }
95  
96    @Test
97    public void testSplitFlushCompactUnknownTable() throws InterruptedException {
98      final String unknowntable = "fubar";
99      Exception exception = null;
100     try {
101       this.admin.compact(unknowntable);
102     } catch (IOException e) {
103       exception = e;
104     }
105     assertTrue(exception instanceof TableNotFoundException);
106 
107     exception = null;
108     try {
109       this.admin.flush(unknowntable);
110     } catch (IOException e) {
111       exception = e;
112     }
113     assertTrue(exception instanceof TableNotFoundException);
114 
115     exception = null;
116     try {
117       this.admin.split(unknowntable);
118     } catch (IOException e) {
119       exception = e;
120     }
121     assertTrue(exception instanceof TableNotFoundException);
122   }
123 
124   @Test
125   public void testDeleteEditUnknownColumnFamilyAndOrTable() throws IOException {
126     // Test we get exception if we try to
127     final String nonexistent = "nonexistent";
128     HColumnDescriptor nonexistentHcd = new HColumnDescriptor(nonexistent);
129     Exception exception = null;
130     try {
131       this.admin.addColumn(nonexistent, nonexistentHcd);
132     } catch (IOException e) {
133       exception = e;
134     }
135     assertTrue(exception instanceof TableNotFoundException);
136 
137     exception = null;
138     try {
139       this.admin.deleteTable(nonexistent);
140     } catch (IOException e) {
141       exception = e;
142     }
143     assertTrue(exception instanceof TableNotFoundException);
144 
145     exception = null;
146     try {
147       this.admin.deleteColumn(nonexistent, nonexistent);
148     } catch (IOException e) {
149       exception = e;
150     }
151     assertTrue(exception instanceof TableNotFoundException);
152 
153     exception = null;
154     try {
155       this.admin.disableTable(nonexistent);
156     } catch (IOException e) {
157       exception = e;
158     }
159     assertTrue(exception instanceof TableNotFoundException);
160 
161     exception = null;
162     try {
163       this.admin.enableTable(nonexistent);
164     } catch (IOException e) {
165       exception = e;
166     }
167     assertTrue(exception instanceof TableNotFoundException);
168 
169     exception = null;
170     try {
171       this.admin.modifyColumn(nonexistent, nonexistentHcd);
172     } catch (IOException e) {
173       exception = e;
174     }
175     assertTrue(exception instanceof TableNotFoundException);
176 
177     exception = null;
178     try {
179       HTableDescriptor htd = new HTableDescriptor(nonexistent);
180       this.admin.modifyTable(htd.getName(), htd);
181     } catch (IOException e) {
182       exception = e;
183     }
184     assertTrue(exception instanceof TableNotFoundException);
185 
186     // Now make it so at least the table exists and then do tests against a
187     // nonexistent column family -- see if we get right exceptions.
188     final String tableName = "t";
189     HTableDescriptor htd = new HTableDescriptor(tableName);
190     htd.addFamily(new HColumnDescriptor("cf"));
191     this.admin.createTable(htd);
192     try {
193       exception = null;
194       try {
195         this.admin.deleteColumn(htd.getName(), nonexistentHcd.getName());
196       } catch (IOException e) {
197         exception = e;
198       }
199       assertTrue(exception instanceof InvalidFamilyOperationException);
200 
201       exception = null;
202       try {
203         this.admin.modifyColumn(htd.getName(), nonexistentHcd);
204       } catch (IOException e) {
205         exception = e;
206       }
207       assertTrue(exception instanceof InvalidFamilyOperationException);
208     } finally {
209       this.admin.disableTable(tableName);
210       this.admin.deleteTable(tableName);
211     }
212   }
213 
214   @Test
215   public void testDisableAndEnableTable() throws IOException {
216     final byte [] row = Bytes.toBytes("row");
217     final byte [] qualifier = Bytes.toBytes("qualifier");
218     final byte [] value = Bytes.toBytes("value");
219     final byte [] table = Bytes.toBytes("testDisableAndEnableTable");
220     HTable ht = TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
221     Put put = new Put(row);
222     put.add(HConstants.CATALOG_FAMILY, qualifier, value);
223     ht.put(put);
224     Get get = new Get(row);
225     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
226     ht.get(get);
227 
228     this.admin.disableTable(table);
229     assertTrue("Table must be disabled.", TEST_UTIL.getHBaseCluster()
230         .getMaster().getAssignmentManager().getZKTable().isDisabledTable(
231             Bytes.toString(table)));
232 
233     // Test that table is disabled
234     get = new Get(row);
235     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
236     boolean ok = false;
237     try {
238       ht.get(get);
239     } catch (DoNotRetryIOException e) {
240       ok = true;
241     }
242     assertTrue(ok);
243     this.admin.enableTable(table);
244     assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster()
245         .getMaster().getAssignmentManager().getZKTable().isEnabledTable(
246             Bytes.toString(table)));
247 
248     // Test that table is enabled
249     try {
250       ht.get(get);
251     } catch (RetriesExhaustedException e) {
252       ok = false;
253     }
254     assertTrue(ok);
255     ht.close();
256   }
257 
258   @Test
259   public void testIsEnabledOnUnknownTable() throws Exception {
260     try {
261       admin.isTableEnabled(Bytes.toBytes("unkownTable"));
262       fail("Test should fail if isTableEnabled called on unknown table.");
263     } catch (IOException e) {
264     }
265   }
266   
267   @Test
268   public void testDisableAndEnableTables() throws IOException {
269     final byte [] row = Bytes.toBytes("row");
270     final byte [] qualifier = Bytes.toBytes("qualifier");
271     final byte [] value = Bytes.toBytes("value");
272     final byte [] table1 = Bytes.toBytes("testDisableAndEnableTable1");
273     final byte [] table2 = Bytes.toBytes("testDisableAndEnableTable2");
274     HTable ht1 = TEST_UTIL.createTable(table1, HConstants.CATALOG_FAMILY);
275     HTable ht2 = TEST_UTIL.createTable(table2, HConstants.CATALOG_FAMILY);
276     Put put = new Put(row);
277     put.add(HConstants.CATALOG_FAMILY, qualifier, value);
278     ht1.put(put);
279     ht2.put(put);
280     Get get = new Get(row);
281     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
282     ht1.get(get);
283     ht2.get(get);
284 
285     this.admin.disableTables("testDisableAndEnableTable.*");
286 
287     // Test that tables are disabled
288     get = new Get(row);
289     get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
290     boolean ok = false;
291     try {
292       ht1.get(get);
293       ht2.get(get);
294     } catch (DoNotRetryIOException e) {
295       ok = true;
296     }
297 
298     assertTrue(ok);
299     this.admin.enableTables("testDisableAndEnableTable.*");
300 
301     // Test that tables are enabled
302     try {
303       ht1.get(get);
304     } catch (IOException e) {
305       ok = false;
306     }
307     try {
308       ht2.get(get);
309     } catch (IOException e) {
310       ok = false;
311     }
312     assertTrue(ok);
313 
314     ht1.close();
315     ht2.close();
316   }
317 
318   @Test
319   public void testCreateTable() throws IOException {
320     HTableDescriptor [] tables = admin.listTables();
321     int numTables = tables.length;
322     TEST_UTIL.createTable(Bytes.toBytes("testCreateTable"),
323       HConstants.CATALOG_FAMILY).close();
324     tables = this.admin.listTables();
325     assertEquals(numTables + 1, tables.length);
326     assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster()
327         .getMaster().getAssignmentManager().getZKTable().isEnabledTable(
328             "testCreateTable"));
329   }
330 
331   @Test
332   public void testGetTableDescriptor() throws IOException {
333     HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
334     HColumnDescriptor fam2 = new HColumnDescriptor("fam2");
335     HColumnDescriptor fam3 = new HColumnDescriptor("fam3");
336     HTableDescriptor htd = new HTableDescriptor("myTestTable");
337     htd.addFamily(fam1);
338     htd.addFamily(fam2);
339     htd.addFamily(fam3);
340     this.admin.createTable(htd);
341     HTable table = new HTable(TEST_UTIL.getConfiguration(), "myTestTable");
342     HTableDescriptor confirmedHtd = table.getTableDescriptor();
343     assertEquals(htd.compareTo(confirmedHtd), 0);
344     table.close();
345   }
346 
347   @Test
348   public void testHColumnValidName() {
349        boolean exceptionThrown = false;
350        try {
351        HColumnDescriptor fam1 = new HColumnDescriptor("\\test\\abc");
352        } catch(IllegalArgumentException iae) {
353            exceptionThrown = true;
354            assertTrue(exceptionThrown);
355        }
356    }
357   /**
358    * Verify schema modification takes.
359    * @throws IOException
360    * @throws InterruptedException
361    */
362   @Test
363   public void testOnlineChangeTableSchema() throws IOException, InterruptedException {
364     final byte [] tableName = Bytes.toBytes("changeTableSchemaOnline");
365     TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean(
366         "hbase.online.schema.update.enable", true);
367     HTableDescriptor [] tables = admin.listTables();
368     int numTables = tables.length;
369     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
370     tables = this.admin.listTables();
371     assertEquals(numTables + 1, tables.length);
372 
373     // FIRST, do htabledescriptor changes.
374     HTableDescriptor htd = this.admin.getTableDescriptor(tableName);
375     // Make a copy and assert copy is good.
376     HTableDescriptor copy = new HTableDescriptor(htd);
377     assertTrue(htd.equals(copy));
378     // Now amend the copy. Introduce differences.
379     long newFlushSize = htd.getMemStoreFlushSize() / 2;
380     if (newFlushSize <=0) {
381       newFlushSize = HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE / 2;
382     }
383     copy.setMemStoreFlushSize(newFlushSize);
384     final String key = "anyoldkey";
385     assertTrue(htd.getValue(key) == null);
386     copy.setValue(key, key);
387     boolean expectedException = false;
388     try {
389       modifyTable(tableName, copy);
390     } catch (TableNotDisabledException re) {
391       expectedException = true;
392     }
393     assertFalse(expectedException);
394     HTableDescriptor modifiedHtd = this.admin.getTableDescriptor(tableName);
395     assertFalse(htd.equals(modifiedHtd));
396     assertTrue(copy.equals(modifiedHtd));
397     assertEquals(newFlushSize, modifiedHtd.getMemStoreFlushSize());
398     assertEquals(key, modifiedHtd.getValue(key));
399 
400     // Now work on column family changes.
401     htd = this.admin.getTableDescriptor(tableName);
402     int countOfFamilies = modifiedHtd.getFamilies().size();
403     assertTrue(countOfFamilies > 0);
404     HColumnDescriptor hcd = modifiedHtd.getFamilies().iterator().next();
405     int maxversions = hcd.getMaxVersions();
406     final int newMaxVersions = maxversions + 1;
407     hcd.setMaxVersions(newMaxVersions);
408     final byte [] hcdName = hcd.getName();
409     expectedException = false;
410     try {
411       this.admin.modifyColumn(tableName, hcd);
412     } catch (TableNotDisabledException re) {
413       expectedException = true;
414     }
415     assertFalse(expectedException);
416     modifiedHtd = this.admin.getTableDescriptor(tableName);
417     HColumnDescriptor modifiedHcd = modifiedHtd.getFamily(hcdName);
418     assertEquals(newMaxVersions, modifiedHcd.getMaxVersions());
419 
420     // Try adding a column
421     assertFalse(this.admin.isTableDisabled(tableName));
422     final String xtracolName = "xtracol";
423     htd = this.admin.getTableDescriptor(tableName);
424     HColumnDescriptor xtracol = new HColumnDescriptor(xtracolName);
425     xtracol.setValue(xtracolName, xtracolName);
426     expectedException = false;
427     try {
428       this.admin.addColumn(tableName, xtracol);
429     } catch (TableNotDisabledException re) {
430       expectedException = true;
431     }
432     // Add column should work even if the table is enabled
433     assertFalse(expectedException);
434     modifiedHtd = this.admin.getTableDescriptor(tableName);
435     hcd = modifiedHtd.getFamily(xtracol.getName());
436     assertTrue(hcd != null);
437     assertTrue(hcd.getValue(xtracolName).equals(xtracolName));
438 
439     // Delete the just-added column.
440     this.admin.deleteColumn(tableName, xtracol.getName());
441     modifiedHtd = this.admin.getTableDescriptor(tableName);
442     hcd = modifiedHtd.getFamily(xtracol.getName());
443     assertTrue(hcd == null);
444 
445     // Delete the table
446     this.admin.disableTable(tableName);
447     this.admin.deleteTable(tableName);
448     this.admin.listTables();
449     assertFalse(this.admin.tableExists(tableName));
450   }
451 
452   @Test
453   public void testShouldFailOnlineSchemaUpdateIfOnlineSchemaIsNotEnabled()
454       throws Exception {
455     final byte[] tableName = Bytes.toBytes("changeTableSchemaOnlineFailure");
456     TEST_UTIL.getMiniHBaseCluster().getMaster().getConfiguration().setBoolean(
457         "hbase.online.schema.update.enable", false);
458     HTableDescriptor[] tables = admin.listTables();
459     int numTables = tables.length;
460     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
461     tables = this.admin.listTables();
462     assertEquals(numTables + 1, tables.length);
463 
464     // FIRST, do htabledescriptor changes.
465     HTableDescriptor htd = this.admin.getTableDescriptor(tableName);
466     // Make a copy and assert copy is good.
467     HTableDescriptor copy = new HTableDescriptor(htd);
468     assertTrue(htd.equals(copy));
469     // Now amend the copy. Introduce differences.
470     long newFlushSize = htd.getMemStoreFlushSize() / 2;
471     if (newFlushSize <=0) {
472       newFlushSize = HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE / 2;
473     }
474     copy.setMemStoreFlushSize(newFlushSize);
475     final String key = "anyoldkey";
476     assertTrue(htd.getValue(key) == null);
477     copy.setValue(key, key);
478     boolean expectedException = false;
479     try {
480       modifyTable(tableName, copy);
481     } catch (TableNotDisabledException re) {
482       expectedException = true;
483     }
484     assertTrue("Online schema update should not happen.", expectedException);
485   }
486 
487   /**
488    * Modify table is async so wait on completion of the table operation in master.
489    * @param tableName
490    * @param htd
491    * @throws IOException
492    */
493   private void modifyTable(final byte [] tableName, final HTableDescriptor htd)
494   throws IOException {
495     MasterServices services = TEST_UTIL.getMiniHBaseCluster().getMaster();
496     ExecutorService executor = services.getExecutorService();
497     AtomicBoolean done = new AtomicBoolean(false);
498     executor.registerListener(EventType.C_M_MODIFY_TABLE, new DoneListener(done));
499     this.admin.modifyTable(tableName, htd);
500     while (!done.get()) {
501       synchronized (done) {
502         try {
503           done.wait(100);
504         } catch (InterruptedException e) {
505           e.printStackTrace();
506         }
507       }
508     }
509     executor.unregisterListener(EventType.C_M_MODIFY_TABLE);
510   }
511 
512   /**
513    * Listens for when an event is done in Master.
514    */
515   static class DoneListener implements EventHandler.EventHandlerListener {
516     private final AtomicBoolean done;
517 
518     DoneListener(final AtomicBoolean done) {
519       super();
520       this.done = done;
521     }
522 
523     @Override
524     public void afterProcess(EventHandler event) {
525       this.done.set(true);
526       synchronized (this.done) {
527         // Wake anyone waiting on this value to change.
528         this.done.notifyAll();
529       }
530     }
531 
532     @Override
533     public void beforeProcess(EventHandler event) {
534       // continue
535     }
536   }
537 
538   protected void verifyRoundRobinDistribution(HTable ht, int expectedRegions) throws IOException {
539     int numRS = ht.getConnection().getCurrentNrHRS();
540     Map<HRegionInfo,HServerAddress> regions = ht.getRegionsInfo();
541     Map<HServerAddress, List<HRegionInfo>> server2Regions = new HashMap<HServerAddress, List<HRegionInfo>>();
542     for (Map.Entry<HRegionInfo,HServerAddress> entry : regions.entrySet()) {
543       HServerAddress server = entry.getValue();
544       List<HRegionInfo> regs = server2Regions.get(server);
545       if (regs == null) {
546         regs = new ArrayList<HRegionInfo>();
547         server2Regions.put(server, regs);
548       }
549       regs.add(entry.getKey());
550     }
551     float average = (float) expectedRegions/numRS;
552     int min = (int)Math.floor(average);
553     int max = (int)Math.ceil(average);
554     for (List<HRegionInfo> regionList : server2Regions.values()) {
555       assertTrue(regionList.size() == min || regionList.size() == max);
556     }
557   }
558 
559   @Test
560   public void testCreateTableNumberOfRegions() throws IOException, InterruptedException {
561     byte[] tableName = Bytes.toBytes("testCreateTableNumberOfRegions");
562     HTableDescriptor desc = new HTableDescriptor(tableName);
563     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
564     admin.createTable(desc);
565     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
566     Map<HRegionInfo, ServerName> regions = ht.getRegionLocations();
567     assertEquals("Table should have only 1 region", 1, regions.size());
568     ht.close();
569 
570     byte[] TABLE_2 = Bytes.add(tableName, Bytes.toBytes("_2"));
571     desc = new HTableDescriptor(TABLE_2);
572     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
573     admin.createTable(desc, new byte[][] { new byte[] { 42 } });
574     HTable ht2 = new HTable(TEST_UTIL.getConfiguration(), TABLE_2);
575     regions = ht2.getRegionLocations();
576     assertEquals("Table should have only 2 region", 2, regions.size());
577     ht2.close();
578 
579     byte[] TABLE_3 = Bytes.add(tableName, Bytes.toBytes("_3"));
580     desc = new HTableDescriptor(TABLE_3);
581     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
582     admin.createTable(desc, "a".getBytes(), "z".getBytes(), 3);
583     HTable ht3 = new HTable(TEST_UTIL.getConfiguration(), TABLE_3);
584     regions = ht3.getRegionLocations();
585     assertEquals("Table should have only 3 region", 3, regions.size());
586     ht3.close();
587 
588     byte[] TABLE_4 = Bytes.add(tableName, Bytes.toBytes("_4"));
589     desc = new HTableDescriptor(TABLE_4);
590     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
591     try {
592       admin.createTable(desc, "a".getBytes(), "z".getBytes(), 2);
593       fail("Should not be able to create a table with only 2 regions using this API.");
594     } catch (IllegalArgumentException eae) {
595       // Expected
596     }
597 
598     byte[] TABLE_5 = Bytes.add(tableName, Bytes.toBytes("_5"));
599     desc = new HTableDescriptor(TABLE_5);
600     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
601     admin.createTable(desc, new byte[] { 1 }, new byte[] { 127 }, 16);
602     HTable ht5 = new HTable(TEST_UTIL.getConfiguration(), TABLE_5);
603     regions = ht5.getRegionLocations();
604     assertEquals("Table should have 16 region", 16, regions.size());
605     ht5.close();
606   }
607  
608   @Test
609   public void testCreateTableWithRegions() throws IOException, InterruptedException {
610 
611     byte[] tableName = Bytes.toBytes("testCreateTableWithRegions");
612 
613     byte [][] splitKeys = {
614         new byte [] { 1, 1, 1 },
615         new byte [] { 2, 2, 2 },
616         new byte [] { 3, 3, 3 },
617         new byte [] { 4, 4, 4 },
618         new byte [] { 5, 5, 5 },
619         new byte [] { 6, 6, 6 },
620         new byte [] { 7, 7, 7 },
621         new byte [] { 8, 8, 8 },
622         new byte [] { 9, 9, 9 },
623     };
624     int expectedRegions = splitKeys.length + 1;
625 
626     HTableDescriptor desc = new HTableDescriptor(tableName);
627     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
628     admin.createTable(desc, splitKeys);
629 
630     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
631     Map<HRegionInfo,HServerAddress> regions = ht.getRegionsInfo();
632     assertEquals("Tried to create " + expectedRegions + " regions " +
633         "but only found " + regions.size(),
634         expectedRegions, regions.size());
635     System.err.println("Found " + regions.size() + " regions");
636 
637     Iterator<HRegionInfo> hris = regions.keySet().iterator();
638     HRegionInfo hri = hris.next();
639     assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
640     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0]));
641     hri = hris.next();
642     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0]));
643     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1]));
644     hri = hris.next();
645     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1]));
646     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2]));
647     hri = hris.next();
648     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2]));
649     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3]));
650     hri = hris.next();
651     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3]));
652     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4]));
653     hri = hris.next();
654     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4]));
655     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5]));
656     hri = hris.next();
657     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5]));
658     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6]));
659     hri = hris.next();
660     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6]));
661     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7]));
662     hri = hris.next();
663     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7]));
664     assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8]));
665     hri = hris.next();
666     assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8]));
667     assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
668 
669     verifyRoundRobinDistribution(ht, expectedRegions);
670     ht.close();
671 
672     // Now test using start/end with a number of regions
673 
674     // Use 80 bit numbers to make sure we aren't limited
675     byte [] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
676     byte [] endKey =   { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
677 
678     // Splitting into 10 regions, we expect (null,1) ... (9, null)
679     // with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle
680 
681     expectedRegions = 10;
682 
683     byte [] TABLE_2 = Bytes.add(tableName, Bytes.toBytes("_2"));
684 
685     desc = new HTableDescriptor(TABLE_2);
686     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
687     admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
688     admin.createTable(desc, startKey, endKey, expectedRegions);
689 
690     HTable ht2 = new HTable(TEST_UTIL.getConfiguration(), TABLE_2);
691     regions = ht2.getRegionsInfo();
692     assertEquals("Tried to create " + expectedRegions + " regions " +
693         "but only found " + regions.size(),
694         expectedRegions, regions.size());
695     System.err.println("Found " + regions.size() + " regions");
696 
697     hris = regions.keySet().iterator();
698     hri = hris.next();
699     assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
700     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {1,1,1,1,1,1,1,1,1,1}));
701     hri = hris.next();
702     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {1,1,1,1,1,1,1,1,1,1}));
703     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {2,2,2,2,2,2,2,2,2,2}));
704     hri = hris.next();
705     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {2,2,2,2,2,2,2,2,2,2}));
706     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {3,3,3,3,3,3,3,3,3,3}));
707     hri = hris.next();
708     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {3,3,3,3,3,3,3,3,3,3}));
709     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {4,4,4,4,4,4,4,4,4,4}));
710     hri = hris.next();
711     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {4,4,4,4,4,4,4,4,4,4}));
712     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {5,5,5,5,5,5,5,5,5,5}));
713     hri = hris.next();
714     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {5,5,5,5,5,5,5,5,5,5}));
715     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {6,6,6,6,6,6,6,6,6,6}));
716     hri = hris.next();
717     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {6,6,6,6,6,6,6,6,6,6}));
718     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {7,7,7,7,7,7,7,7,7,7}));
719     hri = hris.next();
720     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {7,7,7,7,7,7,7,7,7,7}));
721     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {8,8,8,8,8,8,8,8,8,8}));
722     hri = hris.next();
723     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {8,8,8,8,8,8,8,8,8,8}));
724     assertTrue(Bytes.equals(hri.getEndKey(), new byte [] {9,9,9,9,9,9,9,9,9,9}));
725     hri = hris.next();
726     assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {9,9,9,9,9,9,9,9,9,9}));
727     assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
728 
729     verifyRoundRobinDistribution(ht2, expectedRegions);
730     ht2.close();
731 
732     // Try once more with something that divides into something infinite
733 
734     startKey = new byte [] { 0, 0, 0, 0, 0, 0 };
735     endKey = new byte [] { 1, 0, 0, 0, 0, 0 };
736 
737     expectedRegions = 5;
738 
739     byte [] TABLE_3 = Bytes.add(tableName, Bytes.toBytes("_3"));
740 
741     desc = new HTableDescriptor(TABLE_3);
742     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
743     admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
744     admin.createTable(desc, startKey, endKey, expectedRegions);
745 
746 
747     HTable ht3 = new HTable(TEST_UTIL.getConfiguration(), TABLE_3);
748     regions = ht3.getRegionsInfo();
749     assertEquals("Tried to create " + expectedRegions + " regions " +
750         "but only found " + regions.size(),
751         expectedRegions, regions.size());
752     System.err.println("Found " + regions.size() + " regions");
753 
754     verifyRoundRobinDistribution(ht3, expectedRegions);
755     ht3.close();
756 
757 
758     // Try an invalid case where there are duplicate split keys
759     splitKeys = new byte [][] {
760         new byte [] { 1, 1, 1 },
761         new byte [] { 2, 2, 2 },
762         new byte [] { 3, 3, 3 },
763         new byte [] { 2, 2, 2 }
764     };
765 
766     byte [] TABLE_4 = Bytes.add(tableName, Bytes.toBytes("_4"));
767     desc = new HTableDescriptor(TABLE_4);
768     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
769     HBaseAdmin ladmin = new HBaseAdmin(TEST_UTIL.getConfiguration());
770     try {
771       ladmin.createTable(desc, splitKeys);
772       assertTrue("Should not be able to create this table because of " +
773           "duplicate split keys", false);
774     } catch(IllegalArgumentException iae) {
775       // Expected
776     }
777     ladmin.close();
778   }
779   
780     
781   @Test
782   public void testCreateTableWithOnlyEmptyStartRow() throws IOException {
783     byte[] tableName = Bytes.toBytes("testCreateTableWithOnlyEmptyStartRow");
784     byte[][] splitKeys = new byte[1][];
785     splitKeys[0] = HConstants.EMPTY_BYTE_ARRAY;
786     HTableDescriptor desc = new HTableDescriptor(tableName);
787     desc.addFamily(new HColumnDescriptor("col"));
788     try {
789       admin.createTable(desc, splitKeys);
790       fail("Test case should fail as empty split key is passed.");
791     } catch (IllegalArgumentException e) {
792     }
793   }
794     
795   @Test
796   public void testCreateTableWithEmptyRowInTheSplitKeys() throws IOException {
797     byte[] tableName = Bytes
798         .toBytes("testCreateTableWithEmptyRowInTheSplitKeys");
799     byte[][] splitKeys = new byte[3][];
800     splitKeys[0] = "region1".getBytes();
801     splitKeys[1] = HConstants.EMPTY_BYTE_ARRAY;
802     splitKeys[2] = "region2".getBytes();
803     HTableDescriptor desc = new HTableDescriptor(tableName);
804     desc.addFamily(new HColumnDescriptor("col"));
805     try {
806       admin.createTable(desc, splitKeys);
807       fail("Test case should fail as empty split key is passed.");
808     } catch (IllegalArgumentException e) {
809     }
810   }
811 
812   @Test
813   public void testTableExist() throws IOException {
814     final byte [] table = Bytes.toBytes("testTableExist");
815     boolean exist = false;
816     exist = this.admin.tableExists(table);
817     assertEquals(false, exist);
818     TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
819     exist = this.admin.tableExists(table);
820     assertEquals(true, exist);
821   }
822 
823   /**
824    * Tests forcing split from client and having scanners successfully ride over split.
825    * @throws Exception
826    * @throws IOException
827    */
828   @Test
829   public void testForceSplit() throws Exception {
830     byte[][] familyNames = new byte[][] { Bytes.toBytes("cf") };
831     int[] rowCounts = new int[] { 6000 };
832     int numVersions = HColumnDescriptor.DEFAULT_VERSIONS;
833     int blockSize = 256;
834     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
835 
836     byte[] splitKey = Bytes.toBytes(3500);
837     splitTest(splitKey, familyNames, rowCounts, numVersions, blockSize);
838   }
839 
840   /**
841    * Test round-robin assignment on enableTable.
842    *
843    * @throws IOException
844    */
845   @Test
846   public void testEnableTableRoundRobinAssignment() throws IOException {
847     byte[] tableName = Bytes.toBytes("testEnableTableAssignment");
848     byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 },
849         new byte[] { 3, 3, 3 }, new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 },
850         new byte[] { 6, 6, 6 }, new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 },
851         new byte[] { 9, 9, 9 } };
852     int expectedRegions = splitKeys.length + 1;
853     HTableDescriptor desc = new HTableDescriptor(tableName);
854     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
855     admin.createTable(desc, splitKeys);
856     HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName);
857     Map<HRegionInfo, HServerAddress> regions = ht.getRegionsInfo();
858     assertEquals("Tried to create " + expectedRegions + " regions "
859         + "but only found " + regions.size(), expectedRegions, regions.size());
860     // Disable table.
861     admin.disableTable(tableName);
862     // Enable table, use round-robin assignment to assign regions.
863     admin.enableTable(tableName);
864 
865     // Check the assignment.
866     HTable metaTable = new HTable(TEST_UTIL.getConfiguration(),
867         HConstants.META_TABLE_NAME);
868     List<HRegionInfo> regionInfos = admin.getTableRegions(tableName);
869     Map<String, Integer> serverMap = new HashMap<String, Integer>();
870     for (int i = 0, j = regionInfos.size(); i < j; i++) {
871       HRegionInfo hri = regionInfos.get(i);
872       Get get = new Get(hri.getRegionName());
873       Result result = metaTable.get(get);
874       String server = Bytes.toString(result.getValue(HConstants.CATALOG_FAMILY,
875           HConstants.SERVER_QUALIFIER));
876       Integer regioncount = serverMap.get(server);
877       if (regioncount == null) {
878         regioncount = 0;
879       }
880       regioncount++;
881       serverMap.put(server, regioncount);
882     }
883     List<Map.Entry<String, Integer>> entryList = new ArrayList<Map.Entry<String, Integer>>(
884         serverMap.entrySet());
885     Collections.sort(entryList, new Comparator<Map.Entry<String, Integer>>() {
886       public int compare(Map.Entry<String, Integer> oa,
887           Map.Entry<String, Integer> ob) {
888         return (oa.getValue() - ob.getValue());
889       }
890     });
891     assertTrue(entryList.size() == 3);
892     assertTrue((entryList.get(2).getValue() - entryList.get(0).getValue()) < 2);
893   }
894 
895   /**
896    * Multi-family scenario. Tests forcing split from client and
897    * having scanners successfully ride over split.
898    * @throws Exception
899    * @throws IOException
900    */
901   @Test
902   public void testForceSplitMultiFamily() throws Exception {
903     int numVersions = HColumnDescriptor.DEFAULT_VERSIONS;
904 
905     // use small HFile block size so that we can have lots of blocks in HFile
906     // Otherwise, if there is only one block,
907     // HFileBlockIndex.midKey()'s value == startKey
908     int blockSize = 256;
909     byte[][] familyNames = new byte[][] { Bytes.toBytes("cf1"),
910       Bytes.toBytes("cf2") };
911 
912     // one of the column families isn't splittable
913     int[] rowCounts = new int[] { 6000, 1 };
914     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
915 
916     rowCounts = new int[] { 1, 6000 };
917     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
918 
919     // one column family has much smaller data than the other
920     // the split key should be based on the largest column family
921     rowCounts = new int[] { 6000, 300 };
922     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
923 
924     rowCounts = new int[] { 300, 6000 };
925     splitTest(null, familyNames, rowCounts, numVersions, blockSize);
926 
927   }
928 
929   void splitTest(byte[] splitPoint, byte[][] familyNames, int[] rowCounts,
930     int numVersions, int blockSize) throws Exception {
931     byte [] tableName = Bytes.toBytes("testForceSplit");
932     assertFalse(admin.tableExists(tableName));
933     final HTable table = TEST_UTIL.createTable(tableName, familyNames,
934       numVersions, blockSize);
935     int rowCount = 0;
936     byte[] q = new byte[0];
937 
938     // insert rows into column families. The number of rows that have values
939     // in a specific column family is decided by rowCounts[familyIndex]
940     for (int index = 0; index < familyNames.length; index++) {
941       ArrayList<Put> puts = new ArrayList<Put>(rowCounts[index]);
942       for (int i = 0; i < rowCounts[index]; i++) {
943         byte[] k = Bytes.toBytes(i);
944         Put put = new Put(k);
945         put.add(familyNames[index], q, k);
946         puts.add(put);
947       }
948       table.put(puts);
949 
950       if ( rowCount < rowCounts[index] ) {
951         rowCount = rowCounts[index];
952       }
953     }
954 
955     // get the initial layout (should just be one region)
956     Map<HRegionInfo,HServerAddress> m = table.getRegionsInfo();
957     System.out.println("Initial regions (" + m.size() + "): " + m);
958     assertTrue(m.size() == 1);
959 
960     // Verify row count
961     Scan scan = new Scan();
962     ResultScanner scanner = table.getScanner(scan);
963     int rows = 0;
964     for(@SuppressWarnings("unused") Result result : scanner) {
965       rows++;
966     }
967     scanner.close();
968     assertEquals(rowCount, rows);
969 
970     // Have an outstanding scan going on to make sure we can scan over splits.
971     scan = new Scan();
972     scanner = table.getScanner(scan);
973     // Scan first row so we are into first region before split happens.
974     scanner.next();
975 
976     final AtomicInteger count = new AtomicInteger(0);
977     Thread t = new Thread("CheckForSplit") {
978       public void run() {
979         for (int i = 0; i < 20; i++) {
980           try {
981             sleep(1000);
982           } catch (InterruptedException e) {
983             continue;
984           }
985           // check again    table = new HTable(conf, tableName);
986           Map<HRegionInfo, HServerAddress> regions = null;
987           try {
988             regions = table.getRegionsInfo();
989           } catch (IOException e) {
990             e.printStackTrace();
991           }
992           if (regions == null) continue;
993           count.set(regions.size());
994           if (count.get() >= 2) break;
995           LOG.debug("Cycle waiting on split");
996         }
997       }
998     };
999     t.start();
1000     // Split the table
1001     this.admin.split(tableName, splitPoint);
1002     t.join();
1003 
1004     // Verify row count
1005     rows = 1; // We counted one row above.
1006     for (@SuppressWarnings("unused") Result result : scanner) {
1007       rows++;
1008       if (rows > rowCount) {
1009         scanner.close();
1010         assertTrue("Scanned more than expected (" + rowCount + ")", false);
1011       }
1012     }
1013     scanner.close();
1014     assertEquals(rowCount, rows);
1015 
1016     Map<HRegionInfo, HServerAddress> regions = null;
1017     try {
1018       regions = table.getRegionsInfo();
1019     } catch (IOException e) {
1020       e.printStackTrace();
1021     }
1022     assertEquals(2, regions.size());
1023     HRegionInfo[] r = regions.keySet().toArray(new HRegionInfo[0]);
1024     if (splitPoint != null) {
1025       // make sure the split point matches our explicit configuration
1026       assertEquals(Bytes.toString(splitPoint),
1027           Bytes.toString(r[0].getEndKey()));
1028       assertEquals(Bytes.toString(splitPoint),
1029           Bytes.toString(r[1].getStartKey()));
1030       LOG.debug("Properly split on " + Bytes.toString(splitPoint));
1031     } else {
1032       if (familyNames.length > 1) {
1033         int splitKey = Bytes.toInt(r[0].getEndKey());
1034         // check if splitKey is based on the largest column family
1035         // in terms of it store size
1036         int deltaForLargestFamily = Math.abs(rowCount/2 - splitKey);
1037         for (int index = 0; index < familyNames.length; index++) {
1038           int delta = Math.abs(rowCounts[index]/2 - splitKey);
1039           assertTrue(delta >= deltaForLargestFamily);
1040         }
1041       }
1042     }
1043     TEST_UTIL.deleteTable(tableName);
1044     table.close();
1045   }
1046 
1047   /**
1048    * HADOOP-2156
1049    * @throws IOException
1050    */
1051   @Test (expected=IllegalArgumentException.class)
1052   public void testEmptyHHTableDescriptor() throws IOException {
1053     this.admin.createTable(new HTableDescriptor());
1054   }
1055 
1056   @Test (expected=IllegalArgumentException.class)
1057   public void testInvalidHColumnDescriptor() throws IOException {
1058      new HColumnDescriptor("/cfamily/name");
1059   }
1060 
1061   @Test(timeout=36000)
1062   public void testEnableDisableAddColumnDeleteColumn() throws Exception {
1063     ZooKeeperWatcher zkw = HBaseTestingUtility.getZooKeeperWatcher(TEST_UTIL);
1064     byte [] tableName = Bytes.toBytes("testMasterAdmin");
1065     TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
1066     while (!ZKTableReadOnly.isEnabledTable(zkw, "testMasterAdmin")) {
1067       Thread.sleep(10);
1068     }
1069     this.admin.disableTable(tableName);
1070     try {
1071       new HTable(TEST_UTIL.getConfiguration(), tableName);
1072     } catch (DoNotRetryIOException e) {
1073       //expected
1074     }
1075 
1076     this.admin.addColumn(tableName, new HColumnDescriptor("col2"));
1077     this.admin.enableTable(tableName);
1078     try {
1079       this.admin.deleteColumn(tableName, Bytes.toBytes("col2"));
1080     } catch (TableNotDisabledException e) {
1081       LOG.info(e);
1082     }
1083     this.admin.disableTable(tableName);
1084     this.admin.deleteTable(tableName);
1085   }
1086 
1087   @Test
1088   public void testCreateBadTables() throws IOException {
1089     String msg = null;
1090     try {
1091       this.admin.createTable(HTableDescriptor.ROOT_TABLEDESC);
1092     } catch (IllegalArgumentException e) {
1093       msg = e.toString();
1094     }
1095     assertTrue("Unexcepted exception message " + msg, msg != null &&
1096       msg.startsWith(IllegalArgumentException.class.getName()) &&
1097       msg.contains(HTableDescriptor.ROOT_TABLEDESC.getNameAsString()));
1098     msg = null;
1099     try {
1100       this.admin.createTable(HTableDescriptor.META_TABLEDESC);
1101     } catch(IllegalArgumentException e) {
1102       msg = e.toString();
1103     }
1104     assertTrue("Unexcepted exception message " + msg, msg != null &&
1105       msg.startsWith(IllegalArgumentException.class.getName()) &&
1106       msg.contains(HTableDescriptor.META_TABLEDESC.getNameAsString()));
1107 
1108     // Now try and do concurrent creation with a bunch of threads.
1109     final HTableDescriptor threadDesc =
1110       new HTableDescriptor("threaded_testCreateBadTables");
1111     threadDesc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
1112     int count = 10;
1113     Thread [] threads = new Thread [count];
1114     final AtomicInteger successes = new AtomicInteger(0);
1115     final AtomicInteger failures = new AtomicInteger(0);
1116     final HBaseAdmin localAdmin = this.admin;
1117     for (int i = 0; i < count; i++) {
1118       threads[i] = new Thread(Integer.toString(i)) {
1119         @Override
1120         public void run() {
1121           try {
1122             localAdmin.createTable(threadDesc);
1123             successes.incrementAndGet();
1124           } catch (TableExistsException e) {
1125             failures.incrementAndGet();
1126           } catch (IOException e) {
1127             throw new RuntimeException("Failed threaded create" + getName(), e);
1128           }
1129         }
1130       };
1131     }
1132     for (int i = 0; i < count; i++) {
1133       threads[i].start();
1134     }
1135     for (int i = 0; i < count; i++) {
1136       while(threads[i].isAlive()) {
1137         try {
1138           Thread.sleep(100);
1139         } catch (InterruptedException e) {
1140           // continue
1141         }
1142       }
1143     }
1144     // All threads are now dead.  Count up how many tables were created and
1145     // how many failed w/ appropriate exception.
1146     assertEquals(1, successes.get());
1147     assertEquals(count - 1, failures.get());
1148   }
1149 
1150   /**
1151    * Test for hadoop-1581 'HBASE: Unopenable tablename bug'.
1152    * @throws Exception
1153    */
1154   @Test
1155   public void testTableNameClash() throws Exception {
1156     String name = "testTableNameClash";
1157     admin.createTable(new HTableDescriptor(name + "SOMEUPPERCASE"));
1158     admin.createTable(new HTableDescriptor(name));
1159     // Before fix, below would fail throwing a NoServerForRegionException.
1160     new HTable(TEST_UTIL.getConfiguration(), name).close();
1161   }
1162 
1163   /***
1164    * HMaster.createTable used to be kind of synchronous call
1165    * Thus creating of table with lots of regions can cause RPC timeout
1166    * After the fix to make createTable truly async, RPC timeout shouldn't be an
1167    * issue anymore
1168    * @throws Exception
1169    */
1170   @Test
1171   public void testCreateTableRPCTimeOut() throws Exception {
1172     String name = "testCreateTableRPCTimeOut";
1173     int oldTimeout = TEST_UTIL.getConfiguration().
1174       getInt(HConstants.HBASE_RPC_TIMEOUT_KEY, HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
1175     TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1500);
1176     try {
1177       int expectedRegions = 100;
1178       // Use 80 bit numbers to make sure we aren't limited
1179       byte [] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
1180       byte [] endKey =   { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
1181       HBaseAdmin hbaseadmin = new HBaseAdmin(TEST_UTIL.getConfiguration());
1182       hbaseadmin.createTable(new HTableDescriptor(name), startKey, endKey,
1183         expectedRegions);
1184       hbaseadmin.close();
1185     } finally {
1186       TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, oldTimeout);
1187     }
1188   }
1189 
1190   /**
1191    * Test read only tables
1192    * @throws Exception
1193    */
1194   @Test
1195   public void testReadOnlyTable() throws Exception {
1196     byte [] name = Bytes.toBytes("testReadOnlyTable");
1197     HTable table = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
1198     byte[] value = Bytes.toBytes("somedata");
1199     // This used to use an empty row... That must have been a bug
1200     Put put = new Put(value);
1201     put.add(HConstants.CATALOG_FAMILY, HConstants.CATALOG_FAMILY, value);
1202     table.put(put);
1203     table.close();
1204   }
1205 
1206   /**
1207    * Test that user table names can contain '-' and '.' so long as they do not
1208    * start with same. HBASE-771
1209    * @throws IOException
1210    */
1211   @Test
1212   public void testTableNames() throws IOException {
1213     byte[][] illegalNames = new byte[][] {
1214         Bytes.toBytes("-bad"),
1215         Bytes.toBytes(".bad"),
1216         HConstants.ROOT_TABLE_NAME,
1217         HConstants.META_TABLE_NAME
1218     };
1219     for (int i = 0; i < illegalNames.length; i++) {
1220       try {
1221         new HTableDescriptor(illegalNames[i]);
1222         throw new IOException("Did not detect '" +
1223           Bytes.toString(illegalNames[i]) + "' as an illegal user table name");
1224       } catch (IllegalArgumentException e) {
1225         // expected
1226       }
1227     }
1228     byte[] legalName = Bytes.toBytes("g-oo.d");
1229     try {
1230       new HTableDescriptor(legalName);
1231     } catch (IllegalArgumentException e) {
1232       throw new IOException("Legal user table name: '" +
1233         Bytes.toString(legalName) + "' caused IllegalArgumentException: " +
1234         e.getMessage());
1235     }
1236   }
1237 
1238   /**
1239    * For HADOOP-2579
1240    * @throws IOException
1241    */
1242   @Test (expected=TableExistsException.class)
1243   public void testTableExistsExceptionWithATable() throws IOException {
1244     final byte [] name = Bytes.toBytes("testTableExistsExceptionWithATable");
1245     TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY).close();
1246     TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
1247   }
1248 
1249   /**
1250    * Can't disable a table if the table isn't in enabled state
1251    * @throws IOException
1252    */
1253   @Test (expected=TableNotEnabledException.class)
1254   public void testTableNotEnabledExceptionWithATable() throws IOException {
1255     final byte [] name = Bytes.toBytes(
1256       "testTableNotEnabledExceptionWithATable");
1257     TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY).close();
1258     this.admin.disableTable(name);
1259     this.admin.disableTable(name);
1260   }
1261 
1262   /**
1263    * Can't enable a table if the table isn't in disabled state
1264    * @throws IOException
1265    */
1266   @Test (expected=TableNotDisabledException.class)
1267   public void testTableNotDisabledExceptionWithATable() throws IOException {
1268     final byte [] name = Bytes.toBytes(
1269       "testTableNotDisabledExceptionWithATable");
1270     HTable t = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY);
1271     try {
1272     this.admin.enableTable(name);
1273     }finally {
1274        t.close();
1275     }
1276   }
1277 
1278   /**
1279    * For HADOOP-2579
1280    * @throws IOException
1281    */
1282   @Test (expected=TableNotFoundException.class)
1283   public void testTableNotFoundExceptionWithoutAnyTables() throws IOException {
1284     new HTable(TEST_UTIL.getConfiguration(),
1285         "testTableNotFoundExceptionWithoutAnyTables");
1286   }
1287   @Test
1288   public void testShouldCloseTheRegionBasedOnTheEncodedRegionName()
1289       throws Exception {
1290     byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion");
1291     createTableWithDefaultConf(TABLENAME);
1292 
1293     HRegionInfo info = null;
1294     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
1295     List<HRegionInfo> onlineRegions = rs.getOnlineRegions();
1296     for (HRegionInfo regionInfo : onlineRegions) {
1297       if (!regionInfo.isMetaTable()) {
1298         info = regionInfo;
1299         admin.closeRegionWithEncodedRegionName(regionInfo.getEncodedName(), rs
1300             .getServerName().getServerName());
1301       }
1302     }
1303     Thread.sleep(1000);
1304     onlineRegions = rs.getOnlineRegions();
1305     assertFalse("The region should not be present in online regions list.",
1306         onlineRegions.contains(info));
1307   }
1308 
1309   @Test
1310   public void testCloseRegionIfInvalidRegionNameIsPassed() throws Exception {
1311     byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion1");
1312     createTableWithDefaultConf(TABLENAME);
1313 
1314     HRegionInfo info = null;
1315     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
1316     List<HRegionInfo> onlineRegions = rs.getOnlineRegions();
1317     for (HRegionInfo regionInfo : onlineRegions) {
1318       if (!regionInfo.isMetaTable()) {
1319         if (regionInfo.getRegionNameAsString().contains("TestHBACloseRegion1")) {
1320           info = regionInfo;
1321           admin.closeRegionWithEncodedRegionName("sample", rs.getServerName()
1322               .getServerName());
1323         }
1324       }
1325     }
1326     onlineRegions = rs.getOnlineRegions();
1327     assertTrue("The region should be present in online regions list.",
1328         onlineRegions.contains(info));
1329   }
1330 
1331   @Test
1332   public void testCloseRegionThatFetchesTheHRIFromMeta() throws Exception {
1333     byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion2");
1334     createTableWithDefaultConf(TABLENAME);
1335 
1336     HRegionInfo info = null;
1337     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
1338     List<HRegionInfo> onlineRegions = rs.getOnlineRegions();
1339     for (HRegionInfo regionInfo : onlineRegions) {
1340       if (!regionInfo.isMetaTable()) {
1341 
1342         if (regionInfo.getRegionNameAsString().contains("TestHBACloseRegion2")) {
1343           info = regionInfo;
1344           admin.closeRegion(regionInfo.getRegionNameAsString(), rs
1345               .getServerName().getServerName());
1346         }
1347       }
1348     }
1349 
1350     boolean isInList = rs.getOnlineRegions().contains(info);
1351     long timeout = System.currentTimeMillis() + 2000;
1352     while ((System.currentTimeMillis() < timeout) && (isInList)) {
1353       Thread.sleep(100);
1354       isInList = rs.getOnlineRegions().contains(info);
1355     }
1356 
1357     assertFalse("The region should not be present in online regions list.",
1358       isInList);
1359   }
1360 
1361   @Test
1362   public void testCloseRegionWhenServerNameIsNull() throws Exception {
1363     byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion3");
1364     createTableWithDefaultConf(TABLENAME);
1365 
1366     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
1367 
1368     try {
1369       List<HRegionInfo> onlineRegions = rs.getOnlineRegions();
1370       for (HRegionInfo regionInfo : onlineRegions) {
1371         if (!regionInfo.isMetaTable()) {
1372           if (regionInfo.getRegionNameAsString()
1373               .contains("TestHBACloseRegion3")) {
1374             admin.closeRegionWithEncodedRegionName(regionInfo.getEncodedName(),
1375                 null);
1376           }
1377         }
1378       }
1379       fail("The test should throw exception if the servername passed is null.");
1380     } catch (IllegalArgumentException e) {
1381     }
1382   }
1383 
1384 
1385   @Test
1386   public void testCloseRegionWhenServerNameIsEmpty() throws Exception {
1387     byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegionWhenServerNameIsEmpty");
1388     createTableWithDefaultConf(TABLENAME);
1389 
1390     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
1391 
1392     try {
1393       List<HRegionInfo> onlineRegions = rs.getOnlineRegions();
1394       for (HRegionInfo regionInfo : onlineRegions) {
1395         if (!regionInfo.isMetaTable()) {
1396           if (regionInfo.getRegionNameAsString()
1397               .contains("TestHBACloseRegionWhenServerNameIsEmpty")) {
1398             admin.closeRegionWithEncodedRegionName(regionInfo.getEncodedName(),
1399                 " ");
1400           }
1401         }
1402       }
1403       fail("The test should throw exception if the servername passed is empty.");
1404     } catch (IllegalArgumentException e) {
1405     }
1406   }
1407 
1408   @Test
1409   public void testCloseRegionWhenEncodedRegionNameIsNotGiven() throws Exception {
1410     byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion4");
1411     createTableWithDefaultConf(TABLENAME);
1412 
1413     HRegionInfo info = null;
1414     HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
1415 
1416     List<HRegionInfo> onlineRegions = rs.getOnlineRegions();
1417     for (HRegionInfo regionInfo : onlineRegions) {
1418       if (!regionInfo.isMetaTable()) {
1419         if (regionInfo.getRegionNameAsString().contains("TestHBACloseRegion4")) {
1420           info = regionInfo;
1421           admin.closeRegionWithEncodedRegionName(regionInfo
1422               .getRegionNameAsString(), rs.getServerName().getServerName());
1423         }
1424       }
1425     }
1426     onlineRegions = rs.getOnlineRegions();
1427     assertTrue("The region should be present in online regions list.",
1428         onlineRegions.contains(info));
1429   }
1430 
1431   private HBaseAdmin createTable(byte[] TABLENAME) throws IOException {
1432 
1433     Configuration config = TEST_UTIL.getConfiguration();
1434     HBaseAdmin admin = new HBaseAdmin(config);
1435 
1436     HTableDescriptor htd = new HTableDescriptor(TABLENAME);
1437     HColumnDescriptor hcd = new HColumnDescriptor("value");
1438 
1439     htd.addFamily(hcd);
1440     admin.createTable(htd, null);
1441     return admin;
1442   }
1443 
1444   private void createTableWithDefaultConf(byte[] TABLENAME) throws IOException {
1445     HTableDescriptor htd = new HTableDescriptor(TABLENAME);
1446     HColumnDescriptor hcd = new HColumnDescriptor("value");
1447     htd.addFamily(hcd);
1448 
1449     admin.createTable(htd, null);
1450   }
1451 
1452   /**
1453    * For HBASE-2556
1454    * @throws IOException
1455    */
1456   @Test
1457   public void testGetTableRegions() throws IOException {
1458 
1459     byte[] tableName = Bytes.toBytes("testGetTableRegions");
1460 
1461     int expectedRegions = 10;
1462 
1463     // Use 80 bit numbers to make sure we aren't limited
1464     byte [] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
1465     byte [] endKey =   { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
1466 
1467 
1468     HTableDescriptor desc = new HTableDescriptor(tableName);
1469     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
1470     admin.createTable(desc, startKey, endKey, expectedRegions);
1471 
1472     List<HRegionInfo> RegionInfos = admin.getTableRegions(tableName);
1473 
1474     assertEquals("Tried to create " + expectedRegions + " regions " +
1475         "but only found " + RegionInfos.size(),
1476         expectedRegions, RegionInfos.size());
1477 
1478  }
1479 
1480   @Test
1481   public void testHLogRollWriting() throws Exception {
1482     setUpforLogRolling();
1483     String className = this.getClass().getName();
1484     StringBuilder v = new StringBuilder(className);
1485     while (v.length() < 1000) {
1486       v.append(className);
1487     }
1488     byte[] value = Bytes.toBytes(v.toString());
1489     HRegionServer regionServer = startAndWriteData("TestLogRolling", value);
1490     LOG.info("after writing there are "
1491         + HLogUtilsForTests.getNumLogFiles(regionServer.getWAL()) + " log files");
1492 
1493     // flush all regions
1494 
1495     List<HRegion> regions = new ArrayList<HRegion>(regionServer
1496         .getOnlineRegionsLocalContext());
1497     for (HRegion r : regions) {
1498       r.flushcache();
1499     }
1500     admin.rollHLogWriter(regionServer.getServerName().getServerName());
1501     int count = HLogUtilsForTests.getNumLogFiles(regionServer.getWAL());
1502     LOG.info("after flushing all regions and rolling logs there are " +
1503         count + " log files");
1504     assertTrue(("actual count: " + count), count <= 2);
1505   }
1506 
1507   private void setUpforLogRolling() {
1508     // Force a region split after every 768KB
1509     TEST_UTIL.getConfiguration().setLong(HConstants.HREGION_MAX_FILESIZE,
1510         768L * 1024L);
1511 
1512     // We roll the log after every 32 writes
1513     TEST_UTIL.getConfiguration().setInt("hbase.regionserver.maxlogentries", 32);
1514 
1515     TEST_UTIL.getConfiguration().setInt(
1516         "hbase.regionserver.logroll.errors.tolerated", 2);
1517     TEST_UTIL.getConfiguration().setInt("ipc.ping.interval", 10 * 1000);
1518     TEST_UTIL.getConfiguration().setInt("ipc.socket.timeout", 10 * 1000);
1519     TEST_UTIL.getConfiguration().setInt("hbase.rpc.timeout", 10 * 1000);
1520 
1521     // For less frequently updated regions flush after every 2 flushes
1522     TEST_UTIL.getConfiguration().setInt(
1523         "hbase.hregion.memstore.optionalflushcount", 2);
1524 
1525     // We flush the cache after every 8192 bytes
1526     TEST_UTIL.getConfiguration().setInt(HConstants.HREGION_MEMSTORE_FLUSH_SIZE,
1527         8192);
1528 
1529     // Increase the amount of time between client retries
1530     TEST_UTIL.getConfiguration().setLong("hbase.client.pause", 10 * 1000);
1531 
1532     // Reduce thread wake frequency so that other threads can get
1533     // a chance to run.
1534     TEST_UTIL.getConfiguration().setInt(HConstants.THREAD_WAKE_FREQUENCY,
1535         2 * 1000);
1536 
1537     /**** configuration for testLogRollOnDatanodeDeath ****/
1538     // make sure log.hflush() calls syncFs() to open a pipeline
1539     TEST_UTIL.getConfiguration().setBoolean("dfs.support.append", true);
1540     // lower the namenode & datanode heartbeat so the namenode
1541     // quickly detects datanode failures
1542     TEST_UTIL.getConfiguration().setInt("heartbeat.recheck.interval", 5000);
1543     TEST_UTIL.getConfiguration().setInt("dfs.heartbeat.interval", 1);
1544     // the namenode might still try to choose the recently-dead datanode
1545     // for a pipeline, so try to a new pipeline multiple times
1546     TEST_UTIL.getConfiguration().setInt("dfs.client.block.write.retries", 30);
1547     TEST_UTIL.getConfiguration().setInt(
1548         "hbase.regionserver.hlog.tolerable.lowreplication", 2);
1549     TEST_UTIL.getConfiguration().setInt(
1550         "hbase.regionserver.hlog.lowreplication.rolllimit", 3);
1551   }
1552 
1553   private HRegionServer startAndWriteData(String tableName, byte[] value)
1554       throws IOException {
1555     // When the META table can be opened, the region servers are running
1556     new HTable(
1557       TEST_UTIL.getConfiguration(), HConstants.META_TABLE_NAME).close();
1558     HRegionServer regionServer = TEST_UTIL.getHBaseCluster()
1559         .getRegionServerThreads().get(0).getRegionServer();
1560 
1561     // Create the test table and open it
1562     HTableDescriptor desc = new HTableDescriptor(tableName);
1563     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
1564     admin.createTable(desc);
1565     HTable table = new HTable(TEST_UTIL.getConfiguration(), tableName);
1566 
1567     regionServer = TEST_UTIL.getRSForFirstRegionInTable(Bytes
1568         .toBytes(tableName));
1569     for (int i = 1; i <= 256; i++) { // 256 writes should cause 8 log rolls
1570       Put put = new Put(Bytes.toBytes("row" + String.format("%1$04d", i)));
1571       put.add(HConstants.CATALOG_FAMILY, null, value);
1572       table.put(put);
1573       if (i % 32 == 0) {
1574         // After every 32 writes sleep to let the log roller run
1575         try {
1576           Thread.sleep(2000);
1577         } catch (InterruptedException e) {
1578           // continue
1579         }
1580       }
1581     }
1582 
1583     table.close();
1584     return regionServer;
1585   }
1586 
1587   /**
1588    * HBASE-4417 checkHBaseAvailable() doesn't close zk connections
1589    */
1590   @Test
1591   public void testCheckHBaseAvailableClosesConnection() throws Exception {
1592     Configuration conf = TEST_UTIL.getConfiguration();
1593 
1594     int initialCount = HConnectionTestingUtility.getConnectionCount();
1595     HBaseAdmin.checkHBaseAvailable(conf);
1596     int finalCount = HConnectionTestingUtility.getConnectionCount();
1597 
1598     Assert.assertEquals(initialCount, finalCount) ;
1599   }
1600 
1601   @Test
1602   public void testDisableCatalogTable() throws Exception {
1603     try {
1604       this.admin.disableTable(".META.");
1605       fail("Expected to throw IllegalArgumentException");
1606     } catch (IllegalArgumentException e) {
1607     }
1608     // Before the fix for HBASE-6146, the below table creation was failing as the META table
1609     // actually getting disabled by the disableTable() call.
1610     HTableDescriptor htd = new HTableDescriptor("testDisableCatalogTable".getBytes());
1611     HColumnDescriptor hcd = new HColumnDescriptor("cf1".getBytes());
1612     htd.addFamily(hcd);
1613     TEST_UTIL.getHBaseAdmin().createTable(htd);
1614   }
1615 
1616   @Test
1617   public void testGetRegion() throws Exception {
1618     final String name = "testGetRegion";
1619     LOG.info("Started " + name);
1620     final byte [] nameBytes = Bytes.toBytes(name);
1621     HTable t = TEST_UTIL.createTable(nameBytes, HConstants.CATALOG_FAMILY);
1622     TEST_UTIL.createMultiRegions(t, HConstants.CATALOG_FAMILY);
1623     CatalogTracker ct = new CatalogTracker(TEST_UTIL.getConfiguration());
1624     ct.start();
1625     try {
1626       HRegionLocation regionLocation = t.getRegionLocation("mmm");
1627       HRegionInfo region = regionLocation.getRegionInfo();
1628       byte[] regionName = region.getRegionName();
1629       Pair<HRegionInfo, ServerName> pair = admin.getRegion(regionName, ct);
1630       assertTrue(Bytes.equals(regionName, pair.getFirst().getRegionName()));
1631       pair = admin.getRegion(region.getEncodedNameAsBytes(), ct);
1632       assertTrue(Bytes.equals(regionName, pair.getFirst().getRegionName()));
1633     } finally {
1634       ct.stop();
1635     }
1636   }
1637   
1638   @Test
1639   public void testRootTableSplit() throws Exception {
1640     Scan s = new Scan();
1641     HTable rootTable = new HTable(TEST_UTIL.getConfiguration(), HConstants.ROOT_TABLE_NAME);
1642     ResultScanner scanner = rootTable.getScanner(s);
1643     Result metaEntry = scanner.next();
1644     this.admin.split(HConstants.ROOT_TABLE_NAME, metaEntry.getRow());
1645     Thread.sleep(1000);
1646     List<HRegion> regions = TEST_UTIL.getMiniHBaseCluster().getRegions(HConstants.ROOT_TABLE_NAME);
1647     assertEquals("ROOT region should not be splitted.",1, regions.size());
1648   }
1649   
1650   @org.junit.Rule
1651   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
1652     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
1653 }