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