View Javadoc

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