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.filter; 019 020import static org.junit.Assert.assertNull; 021import static org.junit.Assert.assertTrue; 022import static org.junit.Assert.fail; 023 024import java.io.IOException; 025import java.math.BigDecimal; 026import java.nio.charset.Charset; 027import java.util.Collections; 028import java.util.regex.Pattern; 029import org.apache.commons.io.IOUtils; 030import org.apache.commons.text.StringSubstitutor; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.HBaseCommonTestingUtil; 034import org.apache.hadoop.hbase.HBaseConfiguration; 035import org.apache.hadoop.hbase.HBaseTestingUtil; 036import org.apache.hadoop.hbase.testclassification.FilterTests; 037import org.apache.hadoop.hbase.testclassification.SmallTests; 038import org.apache.hadoop.hbase.util.Bytes; 039import org.apache.hadoop.hbase.util.ClassLoaderTestHelper; 040import org.junit.AfterClass; 041import org.junit.ClassRule; 042import org.junit.Test; 043import org.junit.experimental.categories.Category; 044import org.junit.runner.RunWith; 045import org.junit.runners.Parameterized; 046 047import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 048import org.apache.hadoop.hbase.shaded.protobuf.generated.ComparatorProtos; 049 050@RunWith(Parameterized.class) 051@Category({ FilterTests.class, SmallTests.class }) 052public class TestComparatorSerialization { 053 054 @ClassRule 055 public static final HBaseClassTestRule CLASS_RULE = 056 HBaseClassTestRule.forClass(TestComparatorSerialization.class); 057 058 @Parameterized.Parameter(0) 059 public boolean allowFastReflectionFallthrough; 060 061 @Parameterized.Parameters(name = "{index}: allowFastReflectionFallthrough={0}") 062 public static Iterable<Object[]> data() { 063 return HBaseCommonTestingUtil.BOOLEAN_PARAMETERIZED; 064 } 065 066 @AfterClass 067 public static void afterClass() throws Exception { 068 // set back to true so that it doesn't affect any other tests 069 ProtobufUtil.setAllowFastReflectionFallthrough(true); 070 } 071 072 @Test 073 public void testBinaryComparator() throws Exception { 074 BinaryComparator binaryComparator = new BinaryComparator(Bytes.toBytes("binaryComparator")); 075 assertTrue(binaryComparator.areSerializedFieldsEqual( 076 ProtobufUtil.toComparator(ProtobufUtil.toComparator(binaryComparator)))); 077 } 078 079 @Test 080 public void testBinaryPrefixComparator() throws Exception { 081 BinaryPrefixComparator binaryPrefixComparator = 082 new BinaryPrefixComparator(Bytes.toBytes("binaryPrefixComparator")); 083 assertTrue(binaryPrefixComparator.areSerializedFieldsEqual( 084 ProtobufUtil.toComparator(ProtobufUtil.toComparator(binaryPrefixComparator)))); 085 } 086 087 @Test 088 public void testBitComparator() throws Exception { 089 BitComparator bitComparator = 090 new BitComparator(Bytes.toBytes("bitComparator"), BitComparator.BitwiseOp.XOR); 091 assertTrue(bitComparator.areSerializedFieldsEqual( 092 ProtobufUtil.toComparator(ProtobufUtil.toComparator(bitComparator)))); 093 } 094 095 @Test 096 public void testNullComparator() throws Exception { 097 NullComparator nullComparator = new NullComparator(); 098 assertTrue(nullComparator.areSerializedFieldsEqual( 099 ProtobufUtil.toComparator(ProtobufUtil.toComparator(nullComparator)))); 100 } 101 102 @Test 103 public void testRegexStringComparator() throws Exception { 104 // test without specifying flags 105 RegexStringComparator regexStringComparator = new RegexStringComparator(".+-2"); 106 assertTrue(regexStringComparator.areSerializedFieldsEqual( 107 ProtobufUtil.toComparator(ProtobufUtil.toComparator(regexStringComparator)))); 108 109 // test with specifying flags 110 try { 111 new RegexStringComparator("regex", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); 112 } catch (Throwable t) { 113 assertNull("Exception occurred while created the RegexStringComparator object", t); 114 } 115 } 116 117 @Test 118 public void testSubstringComparator() throws Exception { 119 SubstringComparator substringComparator = new SubstringComparator("substr"); 120 assertTrue(substringComparator.areSerializedFieldsEqual( 121 ProtobufUtil.toComparator(ProtobufUtil.toComparator(substringComparator)))); 122 } 123 124 @Test 125 public void testBigDecimalComparator() throws Exception { 126 BigDecimal bigDecimal = new BigDecimal(Double.MIN_VALUE); 127 BigDecimalComparator bigDecimalComparator = new BigDecimalComparator(bigDecimal); 128 assertTrue(bigDecimalComparator.areSerializedFieldsEqual( 129 ProtobufUtil.toComparator(ProtobufUtil.toComparator(bigDecimalComparator)))); 130 } 131 132 /** 133 * Test that we can load and deserialize custom comparators. Good to have generally, but also 134 * proves that this still works after HBASE-27276 despite not going through our fast function 135 * caches. 136 */ 137 @Test 138 public void testCustomComparator() throws Exception { 139 ByteArrayComparable baseFilter = new BinaryComparator("foo".getBytes()); 140 ComparatorProtos.Comparator proto = ProtobufUtil.toComparator(baseFilter); 141 String suffix = "" + System.currentTimeMillis() + allowFastReflectionFallthrough; 142 String className = "CustomLoadedComparator" + suffix; 143 proto = proto.toBuilder().setName(className).build(); 144 145 Configuration conf = HBaseConfiguration.create(); 146 HBaseTestingUtil testUtil = new HBaseTestingUtil(); 147 String dataTestDir = testUtil.getDataTestDir().toString(); 148 149 // First make sure the test bed is clean, delete any pre-existing class. 150 // Below toComparator call is expected to fail because the comparator is not loaded now 151 ClassLoaderTestHelper.deleteClass(className, dataTestDir, conf); 152 try { 153 ProtobufUtil.toComparator(proto); 154 fail("expected to fail"); 155 } catch (IOException e) { 156 // do nothing, this is expected 157 } 158 159 // Write a jar to be loaded into the classloader 160 String code = StringSubstitutor.replace( 161 IOUtils.toString(getClass().getResourceAsStream("/CustomLoadedComparator.java.template"), 162 Charset.defaultCharset()), 163 Collections.singletonMap("suffix", suffix)); 164 ClassLoaderTestHelper.buildJar(dataTestDir, className, code, 165 ClassLoaderTestHelper.localDirPath(conf)); 166 167 // Disallow fallthrough at first. We expect below to fail because the custom comparator is not 168 // available at initialization so not in the cache. 169 ProtobufUtil.setAllowFastReflectionFallthrough(false); 170 try { 171 ProtobufUtil.toComparator(proto); 172 fail("expected to fail"); 173 } catch (IOException e) { 174 // do nothing, this is expected 175 } 176 177 // Now the deserialization should pass with fallthrough enabled. This proves that custom 178 // comparators can work despite not being supported by cache. 179 ProtobufUtil.setAllowFastReflectionFallthrough(true); 180 ProtobufUtil.toComparator(proto); 181 } 182 183}