001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024import static org.junit.Assert.fail;
025
026import java.io.IOException;
027import java.util.regex.Pattern;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.exceptions.DeserializationException;
031import org.apache.hadoop.hbase.exceptions.HBaseException;
032import org.apache.hadoop.hbase.rsgroup.RSGroupInfo;
033import org.apache.hadoop.hbase.testclassification.MiscTests;
034import org.apache.hadoop.hbase.testclassification.SmallTests;
035import org.apache.hadoop.hbase.util.BuilderStyleTest;
036import org.apache.hadoop.hbase.util.Bytes;
037import org.junit.ClassRule;
038import org.junit.Rule;
039import org.junit.Test;
040import org.junit.experimental.categories.Category;
041import org.junit.rules.TestName;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045/**
046 * Test setting values in the descriptor.
047 */
048@Category({ MiscTests.class, SmallTests.class })
049public class TestTableDescriptorBuilder {
050  @ClassRule
051  public static final HBaseClassTestRule CLASS_RULE =
052    HBaseClassTestRule.forClass(TestTableDescriptorBuilder.class);
053
054  private static final Logger LOG = LoggerFactory.getLogger(TestTableDescriptorBuilder.class);
055
056  @Rule
057  public TestName name = new TestName();
058
059  @Test(expected = IOException.class)
060  public void testAddCoprocessorTwice() throws IOException {
061    String cpName = "a.b.c.d";
062    TableDescriptorBuilder.newBuilder(TableName.META_TABLE_NAME).setCoprocessor(cpName)
063      .setCoprocessor(cpName).build();
064  }
065
066  @Test
067  public void testPb() throws DeserializationException, IOException {
068    final int v = 123;
069    TableDescriptor htd =
070      TableDescriptorBuilder.newBuilder(TableName.META_TABLE_NAME).setMaxFileSize(v)
071        .setDurability(Durability.ASYNC_WAL).setReadOnly(true).setRegionReplication(2).build();
072
073    byte[] bytes = TableDescriptorBuilder.toByteArray(htd);
074    TableDescriptor deserializedHtd = TableDescriptorBuilder.parseFrom(bytes);
075    assertEquals(htd, deserializedHtd);
076    assertEquals(v, deserializedHtd.getMaxFileSize());
077    assertTrue(deserializedHtd.isReadOnly());
078    assertEquals(Durability.ASYNC_WAL, deserializedHtd.getDurability());
079    assertEquals(2, deserializedHtd.getRegionReplication());
080  }
081
082  /**
083   * Test cps in the table description.
084   * @throws Exception if setting a coprocessor fails
085   */
086  @Test
087  public void testGetSetRemoveCP() throws Exception {
088    // simple CP
089    String className = "org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver";
090    TableDescriptor desc = TableDescriptorBuilder
091      .newBuilder(TableName.valueOf(name.getMethodName())).setCoprocessor(className) // add and
092                                                                                     // check that
093                                                                                     // it is
094                                                                                     // present
095      .build();
096    assertTrue(desc.hasCoprocessor(className));
097    desc = TableDescriptorBuilder.newBuilder(desc).removeCoprocessor(className) // remove it and
098                                                                                // check that it is
099                                                                                // gone
100      .build();
101    assertFalse(desc.hasCoprocessor(className));
102  }
103
104  /**
105   * Test cps in the table description.
106   * @throws Exception if setting a coprocessor fails
107   */
108  @Test
109  public void testSetListRemoveCP() throws Exception {
110    TableDescriptor desc =
111      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build();
112    // Check that any coprocessor is present.
113    assertTrue(desc.getCoprocessorDescriptors().isEmpty());
114
115    // simple CP
116    String className1 = "org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver";
117    String className2 = "org.apache.hadoop.hbase.coprocessor.SampleRegionWALObserver";
118    // Add the 1 coprocessor and check if present.
119    desc = TableDescriptorBuilder.newBuilder(desc).setCoprocessor(className1).build();
120    assertTrue(desc.getCoprocessorDescriptors().size() == 1);
121    assertTrue(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
122      .anyMatch(name -> name.equals(className1)));
123    // Add the 2nd coprocessor and check if present.
124    // remove it and check that it is gone
125    desc = TableDescriptorBuilder.newBuilder(desc)
126
127      .setCoprocessor(className2).build();
128    assertTrue(desc.getCoprocessorDescriptors().size() == 2);
129    assertTrue(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
130      .anyMatch(name -> name.equals(className2)));
131    // Remove one and check
132    desc = TableDescriptorBuilder.newBuilder(desc)
133
134      .removeCoprocessor(className1).build();
135    assertTrue(desc.getCoprocessorDescriptors().size() == 1);
136    assertFalse(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
137      .anyMatch(name -> name.equals(className1)));
138    assertTrue(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
139      .anyMatch(name -> name.equals(className2)));
140    // Remove the last and check
141    desc = TableDescriptorBuilder.newBuilder(desc)
142
143      .removeCoprocessor(className2).build();
144    assertTrue(desc.getCoprocessorDescriptors().isEmpty());
145    assertFalse(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
146      .anyMatch(name -> name.equals(className1)));
147    assertFalse(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
148      .anyMatch(name -> name.equals(className2)));
149  }
150
151  /**
152   * Test removing cps in the table description that does not exist
153   * @throws Exception if removing a coprocessor fails other than IllegalArgumentException
154   */
155  @Test(expected = IllegalArgumentException.class)
156  public void testRemoveNonExistingCoprocessor() throws Exception {
157    String className = "org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver";
158    TableDescriptor desc =
159      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build();
160    assertFalse(desc.hasCoprocessor(className));
161    TableDescriptorBuilder.newBuilder(desc).removeCoprocessor(className).build();
162  }
163
164  /**
165   * Test that we add and remove strings from settings properly.
166   */
167  @Test
168  public void testRemoveString() {
169    byte[] key = Bytes.toBytes("Some");
170    byte[] value = Bytes.toBytes("value");
171    TableDescriptor desc = TableDescriptorBuilder
172      .newBuilder(TableName.valueOf(name.getMethodName())).setValue(key, value).build();
173    assertTrue(Bytes.equals(value, desc.getValue(key)));
174    desc = TableDescriptorBuilder.newBuilder(desc).removeValue(key).build();
175    assertTrue(desc.getValue(key) == null);
176  }
177
178  String[] legalTableNames = { "foo", "with-dash_under.dot", "_under_start_ok",
179    "with-dash.with_underscore", "02-01-2012.my_table_01-02", "xyz._mytable_", "9_9_0.table_02",
180    "dot1.dot2.table", "new.-mytable", "with-dash.with.dot", "legal..t2", "legal..legal.t2",
181    "trailingdots..", "trailing.dots...", "ns:mytable", "ns:_mytable_", "ns:my_table_01-02" };
182  String[] illegalTableNames = { ".dot_start_illegal", "-dash_start_illegal", "spaces not ok",
183    "-dash-.start_illegal", "new.table with space", "01 .table", "ns:-illegaldash",
184    "new:.illegaldot", "new:illegalcolon1:", "new:illegalcolon1:2" };
185
186  @Test
187  public void testLegalTableNames() {
188    for (String tn : legalTableNames) {
189      TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(tn));
190    }
191  }
192
193  @Test
194  public void testIllegalTableNames() {
195    for (String tn : illegalTableNames) {
196      try {
197        TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(tn));
198        fail("invalid tablename " + tn + " should have failed");
199      } catch (Exception e) {
200        // expected
201      }
202    }
203  }
204
205  @Test
206  public void testLegalTableNamesRegex() {
207    for (String tn : legalTableNames) {
208      TableName tName = TableName.valueOf(tn);
209      assertTrue("Testing: '" + tn + "'",
210        Pattern.matches(TableName.VALID_USER_TABLE_REGEX, tName.getNameAsString()));
211    }
212  }
213
214  @Test
215  public void testIllegalTableNamesRegex() {
216    for (String tn : illegalTableNames) {
217      LOG.info("Testing: '" + tn + "'");
218      assertFalse(Pattern.matches(TableName.VALID_USER_TABLE_REGEX, tn));
219    }
220  }
221
222  /**
223   * Test default value handling for maxFileSize
224   */
225  @Test
226  public void testGetMaxFileSize() {
227    TableDescriptor desc =
228      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build();
229    assertEquals(-1, desc.getMaxFileSize());
230    desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
231      .setMaxFileSize(1111L).build();
232    assertEquals(1111L, desc.getMaxFileSize());
233  }
234
235  @Test
236  public void testSetMaxFileSize() throws HBaseException {
237    TableDescriptorBuilder builder =
238      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()));
239
240    String maxFileSize = "1073741824";
241    builder.setMaxFileSize(maxFileSize);
242    assertEquals(1073741824, builder.build().getMaxFileSize());
243
244    maxFileSize = "1GB";
245    builder.setMaxFileSize(maxFileSize);
246    assertEquals(1073741824, builder.build().getMaxFileSize());
247
248    maxFileSize = "10GB 25MB";
249    builder.setMaxFileSize(maxFileSize);
250    assertEquals(10763632640L, builder.build().getMaxFileSize());
251
252    // ignore case
253    maxFileSize = "10GB 512mb 512KB 512b";
254    builder.setMaxFileSize(maxFileSize);
255    assertEquals(11274813952L, builder.build().getMaxFileSize());
256
257    maxFileSize = "10737942528 B (10GB 512KB)";
258    builder.setMaxFileSize(maxFileSize);
259    assertEquals(10737942528L, builder.build().getMaxFileSize());
260  }
261
262  /**
263   * Test default value handling for memStoreFlushSize
264   */
265  @Test
266  public void testGetMemStoreFlushSize() {
267    TableDescriptor desc =
268      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build();
269    assertEquals(-1, desc.getMemStoreFlushSize());
270    desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
271      .setMemStoreFlushSize(1111L).build();
272    assertEquals(1111L, desc.getMemStoreFlushSize());
273  }
274
275  @Test
276  public void testSetMemStoreFlushSize() throws HBaseException {
277    TableDescriptorBuilder builder =
278      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()));
279
280    String memstoreFlushSize = "1073741824";
281    builder.setMemStoreFlushSize(memstoreFlushSize);
282    assertEquals(1073741824, builder.build().getMemStoreFlushSize());
283
284    memstoreFlushSize = "1GB";
285    builder.setMemStoreFlushSize(memstoreFlushSize);
286    assertEquals(1073741824, builder.build().getMemStoreFlushSize());
287
288    memstoreFlushSize = "10GB 25MB";
289    builder.setMemStoreFlushSize(memstoreFlushSize);
290    assertEquals(10763632640L, builder.build().getMemStoreFlushSize());
291
292    // ignore case
293    memstoreFlushSize = "10GB 512mb 512KB 512b";
294    builder.setMemStoreFlushSize(memstoreFlushSize);
295    assertEquals(11274813952L, builder.build().getMemStoreFlushSize());
296
297    memstoreFlushSize = "10737942528 B (10GB 512KB)";
298    builder.setMemStoreFlushSize(memstoreFlushSize);
299    assertEquals(10737942528L, builder.build().getMemStoreFlushSize());
300  }
301
302  @Test
303  public void testClassMethodsAreBuilderStyle() {
304    BuilderStyleTest.assertClassesAreBuilderStyle(TableDescriptorBuilder.class);
305  }
306
307  @Test
308  public void testModifyFamily() {
309    byte[] familyName = Bytes.toBytes("cf");
310    ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName)
311      .setBlocksize(1000).setDFSReplication((short) 3).build();
312    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
313      .setColumnFamily(hcd).build();
314
315    assertEquals(1000, htd.getColumnFamily(familyName).getBlocksize());
316    assertEquals(3, htd.getColumnFamily(familyName).getDFSReplication());
317    hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName).setBlocksize(2000)
318      .setDFSReplication((short) 1).build();
319    htd = TableDescriptorBuilder.newBuilder(htd).modifyColumnFamily(hcd).build();
320    assertEquals(2000, htd.getColumnFamily(familyName).getBlocksize());
321    assertEquals(1, htd.getColumnFamily(familyName).getDFSReplication());
322  }
323
324  @Test(expected = IllegalArgumentException.class)
325  public void testModifyInexistentFamily() {
326    byte[] familyName = Bytes.toBytes("cf");
327    TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
328      .modifyColumnFamily(ColumnFamilyDescriptorBuilder.of(familyName)).build();
329  }
330
331  @Test(expected = IllegalArgumentException.class)
332  public void testAddDuplicateFamilies() {
333    byte[] familyName = Bytes.toBytes("cf");
334    ColumnFamilyDescriptor hcd =
335      ColumnFamilyDescriptorBuilder.newBuilder(familyName).setBlocksize(1000).build();
336    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
337      .setColumnFamily(hcd).build();
338    assertEquals(1000, htd.getColumnFamily(familyName).getBlocksize());
339    hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName).setBlocksize(2000).build();
340    // add duplicate column
341    TableDescriptorBuilder.newBuilder(htd).setColumnFamily(hcd).build();
342  }
343
344  @Test
345  public void testPriority() {
346    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
347      .setPriority(42).build();
348    assertEquals(42, htd.getPriority());
349  }
350
351  @Test
352  public void testStringCustomizedValues() throws HBaseException {
353    byte[] familyName = Bytes.toBytes("cf");
354    ColumnFamilyDescriptor hcd =
355      ColumnFamilyDescriptorBuilder.newBuilder(familyName).setBlocksize(131072).build();
356    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
357      .setColumnFamily(hcd).setDurability(Durability.ASYNC_WAL).build();
358
359    assertEquals(
360      "'testStringCustomizedValues', " + "{TABLE_ATTRIBUTES => {DURABILITY => 'ASYNC_WAL'}}, "
361        + "{NAME => 'cf', BLOCKSIZE => '131072 B (128KB)'}",
362      htd.toStringCustomizedValues());
363
364    htd = TableDescriptorBuilder.newBuilder(htd).setMaxFileSize("10737942528")
365      .setMemStoreFlushSize("256MB").setErasureCodingPolicy("RS-6-3-1024k").build();
366    assertEquals(
367      "'testStringCustomizedValues', " + "{TABLE_ATTRIBUTES => {DURABILITY => 'ASYNC_WAL', "
368        + "ERASURE_CODING_POLICY => 'RS-6-3-1024k', MAX_FILESIZE => '10737942528 B (10GB 512KB)', "
369        + "MEMSTORE_FLUSHSIZE => '268435456 B (256MB)'}}, "
370        + "{NAME => 'cf', BLOCKSIZE => '131072 B (128KB)'}",
371      htd.toStringCustomizedValues());
372  }
373
374  @Test
375  public void testGetSetRegionServerGroup() {
376    String groupName = name.getMethodName();
377    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
378      .setRegionServerGroup(groupName).build();
379    assertEquals(htd.getValue(RSGroupInfo.TABLE_DESC_PROP_GROUP), groupName);
380    htd = TableDescriptorBuilder.newBuilder(htd).setRegionServerGroup(null).build();
381    assertNull(htd.getValue(RSGroupInfo.TABLE_DESC_PROP_GROUP));
382  }
383
384  @Test
385  public void testSetEmptyValue() {
386    TableDescriptorBuilder builder =
387      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()));
388    String testValue = "TestValue";
389    // test setValue
390    builder.setValue(testValue, "2");
391    assertEquals("2", builder.build().getValue(testValue));
392    builder.setValue(testValue, "");
393    assertNull(builder.build().getValue(Bytes.toBytes(testValue)));
394
395    // test setFlushPolicyClassName
396    builder.setFlushPolicyClassName("class");
397    assertEquals("class", builder.build().getFlushPolicyClassName());
398    builder.setFlushPolicyClassName("");
399    assertNull(builder.build().getFlushPolicyClassName());
400  }
401}