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.HColumnDescriptor;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.exceptions.DeserializationException;
032import org.apache.hadoop.hbase.exceptions.HBaseException;
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    desc = TableDescriptorBuilder.newBuilder(desc).setCoprocessor(className1) // Add the 1
119                                                                              // coprocessor and
120                                                                              // check if present.
121      .build();
122    assertTrue(desc.getCoprocessorDescriptors().size() == 1);
123    assertTrue(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
124      .anyMatch(name -> name.equals(className1)));
125
126    desc = TableDescriptorBuilder.newBuilder(desc)
127      // Add the 2nd coprocessor and check if present.
128      // remove it and check that it is gone
129      .setCoprocessor(className2).build();
130    assertTrue(desc.getCoprocessorDescriptors().size() == 2);
131    assertTrue(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
132      .anyMatch(name -> name.equals(className2)));
133
134    desc = TableDescriptorBuilder.newBuilder(desc)
135      // Remove one and check
136      .removeCoprocessor(className1).build();
137    assertTrue(desc.getCoprocessorDescriptors().size() == 1);
138    assertFalse(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
139      .anyMatch(name -> name.equals(className1)));
140    assertTrue(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
141      .anyMatch(name -> name.equals(className2)));
142
143    desc = TableDescriptorBuilder.newBuilder(desc)
144      // Remove the last and check
145      .removeCoprocessor(className2).build();
146    assertTrue(desc.getCoprocessorDescriptors().isEmpty());
147    assertFalse(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
148      .anyMatch(name -> name.equals(className1)));
149    assertFalse(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
150      .anyMatch(name -> name.equals(className2)));
151  }
152
153  /**
154   * Test removing cps in the table description that does not exist
155   */
156  @Test
157  public void testRemoveNonExistingCoprocessor() throws Exception {
158    String className = "org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver";
159    TableDescriptor desc =
160      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build();
161    assertFalse(desc.hasCoprocessor(className));
162    desc = TableDescriptorBuilder.newBuilder(desc).removeCoprocessor(className).build();
163    assertFalse(desc.hasCoprocessor(className));
164  }
165
166  /**
167   * Test that we add and remove strings from settings properly.
168   */
169  @Test
170  public void testRemoveString() {
171    byte[] key = Bytes.toBytes("Some");
172    byte[] value = Bytes.toBytes("value");
173    TableDescriptor desc = TableDescriptorBuilder
174      .newBuilder(TableName.valueOf(name.getMethodName())).setValue(key, value).build();
175    assertTrue(Bytes.equals(value, desc.getValue(key)));
176    desc = TableDescriptorBuilder.newBuilder(desc).removeValue(key).build();
177    assertTrue(desc.getValue(key) == null);
178  }
179
180  String[] legalTableNames = { "foo", "with-dash_under.dot", "_under_start_ok",
181    "with-dash.with_underscore", "02-01-2012.my_table_01-02", "xyz._mytable_", "9_9_0.table_02",
182    "dot1.dot2.table", "new.-mytable", "with-dash.with.dot", "legal..t2", "legal..legal.t2",
183    "trailingdots..", "trailing.dots...", "ns:mytable", "ns:_mytable_", "ns:my_table_01-02" };
184  String[] illegalTableNames = { ".dot_start_illegal", "-dash_start_illegal", "spaces not ok",
185    "-dash-.start_illegal", "new.table with space", "01 .table", "ns:-illegaldash",
186    "new:.illegaldot", "new:illegalcolon1:", "new:illegalcolon1:2" };
187
188  @Test
189  public void testLegalTableNames() {
190    for (String tn : legalTableNames) {
191      TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(tn));
192    }
193  }
194
195  @Test
196  public void testIllegalTableNames() {
197    for (String tn : illegalTableNames) {
198      try {
199        TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(tn));
200        fail("invalid tablename " + tn + " should have failed");
201      } catch (Exception e) {
202        // expected
203      }
204    }
205  }
206
207  @Test
208  public void testLegalTableNamesRegex() {
209    for (String tn : legalTableNames) {
210      TableName tName = TableName.valueOf(tn);
211      assertTrue("Testing: '" + tn + "'",
212        Pattern.matches(TableName.VALID_USER_TABLE_REGEX, tName.getNameAsString()));
213    }
214  }
215
216  @Test
217  public void testIllegalTableNamesRegex() {
218    for (String tn : illegalTableNames) {
219      LOG.info("Testing: '" + tn + "'");
220      assertFalse(Pattern.matches(TableName.VALID_USER_TABLE_REGEX, tn));
221    }
222  }
223
224  /**
225   * Test default value handling for maxFileSize
226   */
227  @Test
228  public void testGetMaxFileSize() {
229    TableDescriptor desc =
230      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build();
231    assertEquals(-1, desc.getMaxFileSize());
232    desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
233      .setMaxFileSize(1111L).build();
234    assertEquals(1111L, desc.getMaxFileSize());
235  }
236
237  @Test
238  public void testSetMaxFileSize() throws HBaseException {
239    TableDescriptorBuilder builder =
240      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()));
241
242    String maxFileSize = "1073741824";
243    builder.setMaxFileSize(maxFileSize);
244    assertEquals(1073741824, builder.build().getMaxFileSize());
245
246    maxFileSize = "1GB";
247    builder.setMaxFileSize(maxFileSize);
248    assertEquals(1073741824, builder.build().getMaxFileSize());
249
250    maxFileSize = "10GB 25MB";
251    builder.setMaxFileSize(maxFileSize);
252    assertEquals(10763632640L, builder.build().getMaxFileSize());
253
254    // ignore case
255    maxFileSize = "10GB 512mb 512KB 512b";
256    builder.setMaxFileSize(maxFileSize);
257    assertEquals(11274813952L, builder.build().getMaxFileSize());
258
259    maxFileSize = "10737942528 B (10GB 512KB)";
260    builder.setMaxFileSize(maxFileSize);
261    assertEquals(10737942528L, builder.build().getMaxFileSize());
262  }
263
264  /**
265   * Test default value handling for memStoreFlushSize
266   */
267  @Test
268  public void testGetMemStoreFlushSize() {
269    TableDescriptor desc =
270      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build();
271    assertEquals(-1, desc.getMemStoreFlushSize());
272    desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
273      .setMemStoreFlushSize(1111L).build();
274    assertEquals(1111L, desc.getMemStoreFlushSize());
275  }
276
277  @Test
278  public void testSetMemStoreFlushSize() throws HBaseException {
279    TableDescriptorBuilder builder =
280      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()));
281
282    String memstoreFlushSize = "1073741824";
283    builder.setMemStoreFlushSize(memstoreFlushSize);
284    assertEquals(1073741824, builder.build().getMemStoreFlushSize());
285
286    memstoreFlushSize = "1GB";
287    builder.setMemStoreFlushSize(memstoreFlushSize);
288    assertEquals(1073741824, builder.build().getMemStoreFlushSize());
289
290    memstoreFlushSize = "10GB 25MB";
291    builder.setMemStoreFlushSize(memstoreFlushSize);
292    assertEquals(10763632640L, builder.build().getMemStoreFlushSize());
293
294    // ignore case
295    memstoreFlushSize = "10GB 512mb 512KB 512b";
296    builder.setMemStoreFlushSize(memstoreFlushSize);
297    assertEquals(11274813952L, builder.build().getMemStoreFlushSize());
298
299    memstoreFlushSize = "10737942528 B (10GB 512KB)";
300    builder.setMemStoreFlushSize(memstoreFlushSize);
301    assertEquals(10737942528L, builder.build().getMemStoreFlushSize());
302  }
303
304  @Test
305  public void testClassMethodsAreBuilderStyle() {
306    BuilderStyleTest.assertClassesAreBuilderStyle(TableDescriptorBuilder.class);
307  }
308
309  @Test
310  public void testModifyFamily() {
311    byte[] familyName = Bytes.toBytes("cf");
312    ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName)
313      .setBlocksize(1000).setDFSReplication((short) 3).build();
314    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
315      .setColumnFamily(hcd).build();
316
317    assertEquals(1000, htd.getColumnFamily(familyName).getBlocksize());
318    assertEquals(3, htd.getColumnFamily(familyName).getDFSReplication());
319    hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName).setBlocksize(2000)
320      .setDFSReplication((short) 1).build();
321    htd = TableDescriptorBuilder.newBuilder(htd).modifyColumnFamily(hcd).build();
322    assertEquals(2000, htd.getColumnFamily(familyName).getBlocksize());
323    assertEquals(1, htd.getColumnFamily(familyName).getDFSReplication());
324  }
325
326  @Test(expected = IllegalArgumentException.class)
327  public void testModifyInexistentFamily() {
328    byte[] familyName = Bytes.toBytes("cf");
329    HColumnDescriptor hcd = new HColumnDescriptor(familyName);
330    TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
331      .modifyColumnFamily(hcd).build();
332  }
333
334  @Test(expected = IllegalArgumentException.class)
335  public void testAddDuplicateFamilies() {
336    byte[] familyName = Bytes.toBytes("cf");
337    ColumnFamilyDescriptor hcd =
338      ColumnFamilyDescriptorBuilder.newBuilder(familyName).setBlocksize(1000).build();
339    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
340      .setColumnFamily(hcd).build();
341    assertEquals(1000, htd.getColumnFamily(familyName).getBlocksize());
342    hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName).setBlocksize(2000).build();
343    // add duplicate column
344    TableDescriptorBuilder.newBuilder(htd).setColumnFamily(hcd).build();
345  }
346
347  @Test
348  public void testPriority() {
349    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
350      .setPriority(42).build();
351    assertEquals(42, htd.getPriority());
352  }
353
354  @Test
355  public void testStringCustomizedValues() throws HBaseException {
356    byte[] familyName = Bytes.toBytes("cf");
357    ColumnFamilyDescriptor hcd =
358      ColumnFamilyDescriptorBuilder.newBuilder(familyName).setBlocksize(131072).build();
359    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
360      .setColumnFamily(hcd).setDurability(Durability.ASYNC_WAL).build();
361
362    assertEquals(
363      "'testStringCustomizedValues', " + "{TABLE_ATTRIBUTES => {DURABILITY => 'ASYNC_WAL'}}, "
364        + "{NAME => 'cf', BLOCKSIZE => '131072 B (128KB)'}",
365      htd.toStringCustomizedValues());
366
367    htd = TableDescriptorBuilder.newBuilder(htd).setMaxFileSize("10737942528")
368      .setMemStoreFlushSize("256MB").setErasureCodingPolicy("RS-6-3-1024k").build();
369    assertEquals(
370      "'testStringCustomizedValues', " + "{TABLE_ATTRIBUTES => {DURABILITY => 'ASYNC_WAL', "
371        + "ERASURE_CODING_POLICY => 'RS-6-3-1024k', MAX_FILESIZE => '10737942528 B (10GB 512KB)', "
372        + "MEMSTORE_FLUSHSIZE => '268435456 B (256MB)'}}, "
373        + "{NAME => 'cf', BLOCKSIZE => '131072 B (128KB)'}",
374      htd.toStringCustomizedValues());
375  }
376
377  @Test
378  public void testSetEmptyValue() {
379    TableDescriptorBuilder builder =
380      TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()));
381    String testValue = "TestValue";
382    // test setValue
383    builder.setValue(testValue, "2");
384    assertEquals("2", builder.build().getValue(testValue));
385    builder.setValue(testValue, "");
386    assertNull(builder.build().getValue(Bytes.toBytes(testValue)));
387
388    // test setFlushPolicyClassName
389    builder.setFlushPolicyClassName("class");
390    assertEquals("class", builder.build().getFlushPolicyClassName());
391    builder.setFlushPolicyClassName("");
392    assertNull(builder.build().getFlushPolicyClassName());
393  }
394}