1   /*
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements. See the NOTICE file distributed with this
6    * work for additional information regarding copyright ownership. The ASF
7    * licenses this file to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance with the License.
9    * 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, WITHOUT
15   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16   * License for the specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.hadoop.hbase.regionserver.metrics;
21  
22  import static org.junit.Assert.*;
23  
24  import java.util.regex.Pattern;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.fs.Path;
30  import org.apache.hadoop.hbase.HBaseConfiguration;
31  import org.apache.hadoop.hbase.SmallTests;
32  import org.apache.hadoop.hbase.regionserver.HRegion;
33  import org.codehaus.jettison.json.JSONException;
34  import org.codehaus.jettison.json.JSONStringer;
35  import org.junit.Test;
36  import org.junit.experimental.categories.Category;
37  
38  @Category(SmallTests.class)
39  public class TestSchemaConfigured {
40    private static final Log LOG = LogFactory.getLog(TestSchemaConfigured.class);
41    private final String TABLE_NAME = "myTable";
42    private final String CF_NAME = "myColumnFamily";
43  
44    private static final Path TMP_HFILE_PATH = new Path(
45        "/hbase/myTable/myRegion/" + HRegion.REGION_TEMP_SUBDIR + "/hfilename");
46  
47    /** Test if toString generates real JSON */
48    @Test
49    public void testToString() throws JSONException {
50      SchemaConfigured sc = new SchemaConfigured(null, TABLE_NAME, CF_NAME);
51      JSONStringer json = new JSONStringer();
52      json.object();
53      json.key("tableName");
54      json.value(TABLE_NAME);
55      json.key("cfName");
56      json.value(CF_NAME);
57      json.endObject();
58      assertEquals(json.toString(), sc.schemaConfAsJSON());
59    }
60  
61    /** Don't allow requesting metrics before setting table/CF name */
62    @Test
63    public void testDelayedInitialization() {
64      SchemaConfigured unconfigured = new SchemaConfigured();
65      try {
66        unconfigured.getSchemaMetrics();
67        fail(IllegalStateException.class.getSimpleName() + " expected");
68      } catch (IllegalStateException ex) {
69        assertTrue("Unexpected exception message: " + ex.getMessage(),
70            Pattern.matches(".* metrics requested before .* initialization.*",
71            ex.getMessage()));
72        LOG.debug("Expected exception: " + ex.getMessage());
73      }
74  
75      SchemaMetrics.setUseTableNameInTest(false);
76      SchemaConfigured other = new SchemaConfigured(null, TABLE_NAME, CF_NAME);
77      other.passSchemaMetricsTo(unconfigured);
78      unconfigured.getSchemaMetrics();  // now this should succeed
79    }
80  
81    /** Don't allow setting table/CF name twice */
82    @Test
83    public void testInitializingTwice() {
84      Configuration conf = HBaseConfiguration.create();
85      for (int i = 0; i < 4; ++i) {
86        SchemaConfigured sc = new SchemaConfigured(conf, TABLE_NAME, CF_NAME);
87        SchemaConfigured target =
88            new SchemaConfigured(conf, TABLE_NAME + (i % 2 == 1 ? "1" : ""),
89                CF_NAME + ((i & 2) != 0 ? "1" : ""));
90        if (i == 0) {
91          sc.passSchemaMetricsTo(target);  // No exception expected.
92          continue;
93        }
94  
95        String testDesc =
96            "Trying to re-configure " + target.schemaConfAsJSON() + " with "
97                + sc.schemaConfAsJSON();
98        try {
99          sc.passSchemaMetricsTo(target);
100         fail(IllegalArgumentException.class.getSimpleName() + " expected");
101       } catch (IllegalArgumentException ex) {
102         final String errorMsg = testDesc + ". Unexpected exception message: " +
103             ex.getMessage();
104         final String exceptionRegex = "Trying to change table .* CF .*";
105         assertTrue(errorMsg, Pattern.matches(exceptionRegex, ex.getMessage()));
106         LOG.debug("Expected exception: " + ex.getMessage());
107       }
108     }
109   }
110 
111   @Test(expected=IllegalStateException.class)
112   public void testConfigureWithUnconfigured1() {
113     SchemaConfigured unconfigured = new SchemaConfigured(null, "t1", null);
114     SchemaConfigured target = new SchemaConfigured();
115     unconfigured.passSchemaMetricsTo(target);
116   }
117 
118   @Test(expected=IllegalStateException.class)
119   public void testConfigureWithUnconfigured2() {
120     SchemaConfigured unconfigured = new SchemaConfigured(null, null, "cf1");
121     SchemaConfigured target = new SchemaConfigured();
122     unconfigured.passSchemaMetricsTo(target);
123   }
124 
125   /**
126    * Configuring with an uninitialized object is equivalent to re-setting
127    * schema metrics configuration.
128    */
129   public void testConfigureWithNull() {
130     SchemaConfigured unconfigured = new SchemaConfigured();
131     SchemaConfigured target = new SchemaConfigured(null, "t1", "cf1");
132     unconfigured.passSchemaMetricsTo(target);
133     assertTrue(target.getTableName() == null);
134     assertTrue(target.getColumnFamilyName() == null);
135   }
136 
137   public void testConfigurePartiallyDefined() {
138     final SchemaConfigured sc = new SchemaConfigured(null, "t1", "cf1");
139     final SchemaConfigured target1 = new SchemaConfigured(null, "t2", null);
140     sc.passSchemaMetricsTo(target1);
141     assertEquals("t2", target1.getColumnFamilyName());
142     assertEquals("cf1", target1.getColumnFamilyName());
143 
144     final SchemaConfigured target2 = new SchemaConfigured(null, null, "cf2");
145     sc.passSchemaMetricsTo(target2);
146     assertEquals("t1", target2.getColumnFamilyName());
147     assertEquals("cf2", target2.getColumnFamilyName());
148 
149     final SchemaConfigured target3 = new SchemaConfigured(null, null, null);
150     sc.passSchemaMetricsTo(target3);
151     assertEquals("t1", target2.getColumnFamilyName());
152     assertEquals("cf1", target2.getColumnFamilyName());
153   }
154 
155   @Test(expected=IllegalArgumentException.class)
156   public void testConflictingConf() {
157     SchemaConfigured sc = new SchemaConfigured(null, "t1", "cf1");
158     SchemaConfigured target = new SchemaConfigured(null, "t2", "cf1");
159     sc.passSchemaMetricsTo(target);
160   }
161 
162   /** We allow setting CF to unknown and then reconfiguring it */
163   public void testReconfigureUnknownCF() {
164     SchemaConfigured sc = new SchemaConfigured(null, "t1", "cf1");
165     SchemaConfigured target =
166         new SchemaConfigured(null, "t1", SchemaMetrics.UNKNOWN);
167     sc.passSchemaMetricsTo(target);
168   }
169 
170   /**
171    * When the "column family" deduced from the path is ".tmp" (this happens
172    * for files written on compaction) we allow re-setting the CF to another
173    * value.
174    */
175   @Test
176   public void testTmpPath() {
177     SchemaConfigured sc = new SchemaConfigured(null, "myTable", "myCF");
178     SchemaConfigured target = new SchemaConfigured(TMP_HFILE_PATH);
179     sc.passSchemaMetricsTo(target);
180   }
181 
182   /**
183    * Even if CF is initially undefined (".tmp"), we don't allow to change
184    * table name.
185    */
186   @Test(expected=IllegalArgumentException.class)
187   public void testTmpPathButInvalidTable() {
188     SchemaConfigured sc = new SchemaConfigured(null, "anotherTable", "myCF");
189     SchemaConfigured target = new SchemaConfigured(TMP_HFILE_PATH);
190     sc.passSchemaMetricsTo(target);
191   }
192 
193   @Test
194   public void testSchemaConfigurationHook() {
195     SchemaConfigured sc = new SchemaConfigured(null, "myTable", "myCF");
196     final StringBuilder newCF = new StringBuilder();
197     final StringBuilder newTable = new StringBuilder();
198     SchemaConfigured target = new SchemaConfigured() {
199       @Override
200       protected void schemaConfigurationChanged() {
201         newCF.append(getColumnFamilyName());
202         newTable.append(getTableName());
203       }
204     };
205     sc.passSchemaMetricsTo(target);
206     assertEquals("myTable", newTable.toString());
207     assertEquals("myCF", newCF.toString());
208   }
209 
210   @Test
211   public void testResetSchemaMetricsConf() {
212     SchemaConfigured target = new SchemaConfigured(null, "t1", "cf1");
213     SchemaConfigured.resetSchemaMetricsConf(target);
214     new SchemaConfigured(null, "t2", "cf2").passSchemaMetricsTo(target);
215     assertEquals("t2", target.getTableName());
216     assertEquals("cf2", target.getColumnFamilyName());
217   }
218 
219   @Test
220   public void testPathTooShort() {
221     // This has too few path components (four, the first one is empty).
222     SchemaConfigured sc1 = new SchemaConfigured(new Path("/a/b/c/d"));
223     assertEquals(SchemaMetrics.UNKNOWN, sc1.getTableName());
224     assertEquals(SchemaMetrics.UNKNOWN, sc1.getColumnFamilyName());
225 
226     SchemaConfigured sc2 = new SchemaConfigured(new Path("a/b/c/d"));
227     assertEquals(SchemaMetrics.UNKNOWN, sc2.getTableName());
228     assertEquals(SchemaMetrics.UNKNOWN, sc2.getColumnFamilyName());
229 
230     SchemaConfigured sc3 = new SchemaConfigured(
231         new Path("/hbase/tableName/regionId/cfName/hfileName"));
232     assertEquals("tableName", sc3.getTableName());
233     assertEquals("cfName", sc3.getColumnFamilyName());
234 
235     SchemaConfigured sc4 = new SchemaConfigured(
236         new Path("hbase/tableName/regionId/cfName/hfileName"));
237     assertEquals("tableName", sc4.getTableName());
238     assertEquals("cfName", sc4.getColumnFamilyName());
239   }
240 
241 
242   @org.junit.Rule
243   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
244     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
245 }
246