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