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.assertTrue;
023import static org.junit.Assert.fail;
024
025import java.io.IOException;
026import java.util.regex.Pattern;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HColumnDescriptor;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.exceptions.DeserializationException;
031import org.apache.hadoop.hbase.testclassification.MiscTests;
032import org.apache.hadoop.hbase.testclassification.SmallTests;
033import org.apache.hadoop.hbase.util.BuilderStyleTest;
034import org.apache.hadoop.hbase.util.Bytes;
035import org.junit.ClassRule;
036import org.junit.Rule;
037import org.junit.Test;
038import org.junit.experimental.categories.Category;
039import org.junit.rules.TestName;
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042
043/**
044 * Test setting values in the descriptor.
045 */
046@Category({MiscTests.class, SmallTests.class})
047public class TestTableDescriptorBuilder {
048  @ClassRule
049  public static final HBaseClassTestRule CLASS_RULE =
050      HBaseClassTestRule.forClass(TestTableDescriptorBuilder.class);
051
052  private static final Logger LOG = LoggerFactory.getLogger(TestTableDescriptorBuilder.class);
053
054  @Rule
055  public TestName name = new TestName();
056
057  @Test (expected=IOException.class)
058  public void testAddCoprocessorTwice() throws IOException {
059    String cpName = "a.b.c.d";
060    TableDescriptor htd
061      = TableDescriptorBuilder.newBuilder(TableName.META_TABLE_NAME)
062            .setCoprocessor(cpName)
063            .setCoprocessor(cpName)
064            .build();
065  }
066
067  @Test
068  public void testPb() throws DeserializationException, IOException {
069    final int v = 123;
070    TableDescriptor htd
071      = TableDescriptorBuilder.newBuilder(TableName.META_TABLE_NAME)
072          .setMaxFileSize(v)
073          .setDurability(Durability.ASYNC_WAL)
074          .setReadOnly(true)
075          .setRegionReplication(2)
076          .build();
077
078    byte [] bytes = TableDescriptorBuilder.toByteArray(htd);
079    TableDescriptor deserializedHtd = TableDescriptorBuilder.parseFrom(bytes);
080    assertEquals(htd, deserializedHtd);
081    assertEquals(v, deserializedHtd.getMaxFileSize());
082    assertTrue(deserializedHtd.isReadOnly());
083    assertEquals(Durability.ASYNC_WAL, deserializedHtd.getDurability());
084    assertEquals(2, deserializedHtd.getRegionReplication());
085  }
086
087  /**
088   * Test cps in the table description.
089   *
090   * @throws Exception if setting a coprocessor fails
091   */
092  @Test
093  public void testGetSetRemoveCP() throws Exception {
094    // simple CP
095    String className = "org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver";
096    TableDescriptor desc
097      = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
098         .setCoprocessor(className) // add and check that it is present
099        .build();
100    assertTrue(desc.hasCoprocessor(className));
101    desc = TableDescriptorBuilder.newBuilder(desc)
102         .removeCoprocessor(className) // remove it and check that it is gone
103        .build();
104    assertFalse(desc.hasCoprocessor(className));
105  }
106
107  /**
108   * Test cps in the table description.
109   *
110   * @throws Exception if setting a coprocessor fails
111   */
112  @Test
113  public void testSetListRemoveCP() throws Exception {
114    TableDescriptor desc
115      = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName())).build();
116    // Check that any coprocessor is present.
117    assertTrue(desc.getCoprocessorDescriptors().isEmpty());
118
119    // simple CP
120    String className1 = "org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver";
121    String className2 = "org.apache.hadoop.hbase.coprocessor.SampleRegionWALObserver";
122    desc = TableDescriptorBuilder.newBuilder(desc)
123            .setCoprocessor(className1) // Add the 1 coprocessor and check if present.
124            .build();
125    assertTrue(desc.getCoprocessorDescriptors().size() == 1);
126    assertTrue(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
127      .anyMatch(name -> name.equals(className1)));
128
129    desc = TableDescriptorBuilder.newBuilder(desc)
130            // Add the 2nd coprocessor and check if present.
131            // remove it and check that it is gone
132            .setCoprocessor(className2)
133            .build();
134    assertTrue(desc.getCoprocessorDescriptors().size() == 2);
135    assertTrue(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
136      .anyMatch(name -> name.equals(className2)));
137
138    desc = TableDescriptorBuilder.newBuilder(desc)
139            // Remove one and check
140            .removeCoprocessor(className1)
141            .build();
142    assertTrue(desc.getCoprocessorDescriptors().size() == 1);
143    assertFalse(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
144      .anyMatch(name -> name.equals(className1)));
145    assertTrue(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
146      .anyMatch(name -> name.equals(className2)));
147
148    desc = TableDescriptorBuilder.newBuilder(desc)
149            // Remove the last and check
150            .removeCoprocessor(className2)
151            .build();
152    assertTrue(desc.getCoprocessorDescriptors().isEmpty());
153    assertFalse(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
154      .anyMatch(name -> name.equals(className1)));
155    assertFalse(desc.getCoprocessorDescriptors().stream().map(CoprocessorDescriptor::getClassName)
156      .anyMatch(name -> name.equals(className2)));
157  }
158
159  /**
160   * Test that we add and remove strings from settings properly.
161   */
162  @Test
163  public void testRemoveString() {
164    byte[] key = Bytes.toBytes("Some");
165    byte[] value = Bytes.toBytes("value");
166    TableDescriptor desc
167      = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
168            .setValue(key, value)
169            .build();
170    assertTrue(Bytes.equals(value, desc.getValue(key)));
171    desc = TableDescriptorBuilder.newBuilder(desc)
172            .removeValue(key)
173            .build();
174    assertTrue(desc.getValue(key) == null);
175  }
176
177  String[] legalTableNames = { "foo", "with-dash_under.dot", "_under_start_ok",
178    "with-dash.with_underscore", "02-01-2012.my_table_01-02", "xyz._mytable_", "9_9_0.table_02",
179    "dot1.dot2.table", "new.-mytable", "with-dash.with.dot", "legal..t2", "legal..legal.t2",
180    "trailingdots..", "trailing.dots...", "ns:mytable", "ns:_mytable_", "ns:my_table_01-02"};
181  String[] illegalTableNames = { ".dot_start_illegal", "-dash_start_illegal", "spaces not ok",
182    "-dash-.start_illegal", "new.table with space", "01 .table", "ns:-illegaldash",
183    "new:.illegaldot", "new:illegalcolon1:", "new:illegalcolon1:2"};
184
185  @Test
186  public void testLegalTableNames() {
187    for (String tn : legalTableNames) {
188      TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(tn));
189    }
190  }
191
192  @Test
193  public void testIllegalTableNames() {
194    for (String tn : illegalTableNames) {
195      try {
196        TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(tn));
197        fail("invalid tablename " + tn + " should have failed");
198      } catch (Exception e) {
199        // expected
200      }
201    }
202  }
203
204  @Test
205  public void testLegalTableNamesRegex() {
206    for (String tn : legalTableNames) {
207      TableName tName = TableName.valueOf(tn);
208      assertTrue("Testing: '" + tn + "'", Pattern.matches(TableName.VALID_USER_TABLE_REGEX,
209          tName.getNameAsString()));
210    }
211  }
212
213  @Test
214  public void testIllegalTableNamesRegex() {
215    for (String tn : illegalTableNames) {
216      LOG.info("Testing: '" + tn + "'");
217      assertFalse(Pattern.matches(TableName.VALID_USER_TABLE_REGEX, tn));
218    }
219  }
220
221    /**
222   * Test default value handling for maxFileSize
223   */
224  @Test
225  public void testGetMaxFileSize() {
226    TableDescriptor desc = TableDescriptorBuilder
227            .newBuilder(TableName.valueOf(name.getMethodName())).build();
228    assertEquals(-1, desc.getMaxFileSize());
229    desc = TableDescriptorBuilder
230            .newBuilder(TableName.valueOf(name.getMethodName()))
231            .setMaxFileSize(1111L).build();
232    assertEquals(1111L, desc.getMaxFileSize());
233  }
234
235  /**
236   * Test default value handling for memStoreFlushSize
237   */
238  @Test
239  public void testGetMemStoreFlushSize() {
240    TableDescriptor desc = TableDescriptorBuilder
241            .newBuilder(TableName.valueOf(name.getMethodName())).build();
242    assertEquals(-1, desc.getMemStoreFlushSize());
243    desc = TableDescriptorBuilder
244            .newBuilder(TableName.valueOf(name.getMethodName()))
245            .setMemStoreFlushSize(1111L).build();
246    assertEquals(1111L, desc.getMemStoreFlushSize());
247  }
248
249  @Test
250  public void testClassMethodsAreBuilderStyle() {
251    BuilderStyleTest.assertClassesAreBuilderStyle(TableDescriptorBuilder.class);
252  }
253
254  @Test
255  public void testModifyFamily() {
256    byte[] familyName = Bytes.toBytes("cf");
257    ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName)
258            .setBlocksize(1000)
259            .setDFSReplication((short) 3)
260            .build();
261    TableDescriptor htd
262      = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
263              .setColumnFamily(hcd)
264              .build();
265
266    assertEquals(1000, htd.getColumnFamily(familyName).getBlocksize());
267    assertEquals(3, htd.getColumnFamily(familyName).getDFSReplication());
268    hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName)
269            .setBlocksize(2000)
270            .setDFSReplication((short) 1)
271            .build();
272    htd = TableDescriptorBuilder.newBuilder(htd)
273              .modifyColumnFamily(hcd)
274              .build();
275    assertEquals(2000, htd.getColumnFamily(familyName).getBlocksize());
276    assertEquals(1, htd.getColumnFamily(familyName).getDFSReplication());
277  }
278
279  @Test(expected=IllegalArgumentException.class)
280  public void testModifyInexistentFamily() {
281    byte[] familyName = Bytes.toBytes("cf");
282    HColumnDescriptor hcd = new HColumnDescriptor(familyName);
283    TableDescriptor htd = TableDescriptorBuilder
284            .newBuilder(TableName.valueOf(name.getMethodName()))
285            .modifyColumnFamily(hcd)
286            .build();
287  }
288
289  @Test(expected=IllegalArgumentException.class)
290  public void testAddDuplicateFamilies() {
291    byte[] familyName = Bytes.toBytes("cf");
292    ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName)
293            .setBlocksize(1000)
294            .build();
295    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
296            .setColumnFamily(hcd)
297            .build();
298    assertEquals(1000, htd.getColumnFamily(familyName).getBlocksize());
299    hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName)
300            .setBlocksize(2000)
301            .build();
302    // add duplicate column
303    TableDescriptorBuilder.newBuilder(htd).setColumnFamily(hcd).build();
304  }
305
306  @Test
307  public void testPriority() {
308    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
309            .setPriority(42)
310            .build();
311    assertEquals(42, htd.getPriority());
312  }
313
314  @Test
315  public void testStringCustomizedValues() {
316    byte[] familyName = Bytes.toBytes("cf");
317    ColumnFamilyDescriptor hcd = ColumnFamilyDescriptorBuilder.newBuilder(familyName)
318            .setBlocksize(1000)
319            .build();
320    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
321            .setColumnFamily(hcd)
322            .setDurability(Durability.ASYNC_WAL)
323            .build();
324
325    assertEquals(
326      "'testStringCustomizedValues', " +
327        "{TABLE_ATTRIBUTES => {DURABILITY => 'ASYNC_WAL'}}, {NAME => 'cf', BLOCKSIZE => '1000'}",
328      htd.toStringCustomizedValues());
329  }
330}