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; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertNotNull; 022import static org.junit.Assert.assertTrue; 023import static org.junit.Assert.fail; 024 025import java.io.ByteArrayInputStream; 026import java.io.ByteArrayOutputStream; 027import java.io.DataInputStream; 028import java.io.DataOutputStream; 029import java.io.IOException; 030import java.util.List; 031import java.util.Map; 032import java.util.NavigableSet; 033import java.util.Set; 034import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 035import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 036import org.apache.hadoop.hbase.client.Get; 037import org.apache.hadoop.hbase.client.RegionInfo; 038import org.apache.hadoop.hbase.client.RegionInfoBuilder; 039import org.apache.hadoop.hbase.client.Scan; 040import org.apache.hadoop.hbase.client.TableDescriptor; 041import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 042import org.apache.hadoop.hbase.filter.BinaryComparator; 043import org.apache.hadoop.hbase.filter.Filter; 044import org.apache.hadoop.hbase.filter.PrefixFilter; 045import org.apache.hadoop.hbase.filter.RowFilter; 046import org.apache.hadoop.hbase.io.TimeRange; 047import org.apache.hadoop.hbase.testclassification.MiscTests; 048import org.apache.hadoop.hbase.testclassification.SmallTests; 049import org.apache.hadoop.hbase.util.ByteBufferUtils; 050import org.apache.hadoop.hbase.util.Bytes; 051import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 052import org.apache.hadoop.io.DataInputBuffer; 053import org.junit.ClassRule; 054import org.junit.Test; 055import org.junit.experimental.categories.Category; 056 057import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 058import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos; 059 060/** 061 * Test HBase Writables serializations 062 */ 063@Category({ MiscTests.class, SmallTests.class }) 064public class TestSerialization { 065 066 @ClassRule 067 public static final HBaseClassTestRule CLASS_RULE = 068 HBaseClassTestRule.forClass(TestSerialization.class); 069 070 @Test 071 public void testKeyValue() throws Exception { 072 final String name = "testKeyValue2"; 073 byte[] row = Bytes.toBytes(name); 074 byte[] fam = Bytes.toBytes("fam"); 075 byte[] qf = Bytes.toBytes("qf"); 076 long ts = EnvironmentEdgeManager.currentTime(); 077 byte[] val = Bytes.toBytes("val"); 078 KeyValue kv = new KeyValue(row, fam, qf, ts, val); 079 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 080 DataOutputStream dos = new DataOutputStream(baos); 081 KeyValueUtil.write(kv, dos); 082 dos.close(); 083 byte[] mb = baos.toByteArray(); 084 ByteArrayInputStream bais = new ByteArrayInputStream(mb); 085 DataInputStream dis = new DataInputStream(bais); 086 KeyValue deserializedKv = KeyValueUtil.create(dis); 087 assertTrue(Bytes.equals(kv.getBuffer(), deserializedKv.getBuffer())); 088 assertEquals(kv.getOffset(), deserializedKv.getOffset()); 089 assertEquals(kv.getLength(), deserializedKv.getLength()); 090 } 091 092 @Test 093 public void testCreateKeyValueInvalidNegativeLength() { 094 095 KeyValue kv_0 = new KeyValue(Bytes.toBytes("myRow"), Bytes.toBytes("myCF"), // 51 bytes 096 Bytes.toBytes("myQualifier"), 12345L, Bytes.toBytes("my12345")); 097 098 KeyValue kv_1 = new KeyValue(Bytes.toBytes("myRow"), Bytes.toBytes("myCF"), // 49 bytes 099 Bytes.toBytes("myQualifier"), 12345L, Bytes.toBytes("my123")); 100 101 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 102 DataOutputStream dos = new DataOutputStream(baos); 103 104 long l = 0; 105 try { 106 ByteBufferUtils.putInt(dos, kv_0.getSerializedSize(false)); 107 l = (long) kv_0.write(dos, false) + Bytes.SIZEOF_INT; 108 ByteBufferUtils.putInt(dos, kv_1.getSerializedSize(false)); 109 l += (long) kv_1.write(dos, false) + Bytes.SIZEOF_INT; 110 assertEquals(100L, l); 111 } catch (IOException e) { 112 fail("Unexpected IOException" + e.getMessage()); 113 } 114 115 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 116 DataInputStream dis = new DataInputStream(bais); 117 118 try { 119 KeyValueUtil.create(dis); 120 assertTrue(kv_0.equals(kv_1)); 121 } catch (Exception e) { 122 fail("Unexpected Exception" + e.getMessage()); 123 } 124 125 // length -1 126 try { 127 // even if we have a good kv now in dis we will just pass length with -1 for simplicity 128 KeyValueUtil.create(-1, dis); 129 fail("Expected corrupt stream"); 130 } catch (Exception e) { 131 assertEquals("Failed read -1 bytes, stream corrupt?", e.getMessage()); 132 } 133 134 } 135 136 @Test 137 public void testCompareFilter() throws Exception { 138 Filter f = 139 new RowFilter(CompareOperator.EQUAL, new BinaryComparator(Bytes.toBytes("testRowOne-2"))); 140 byte[] bytes = f.toByteArray(); 141 Filter ff = RowFilter.parseFrom(bytes); 142 assertNotNull(ff); 143 } 144 145 @Test 146 public void testTableDescriptor() throws Exception { 147 final String name = "testTableDescriptor"; 148 TableDescriptor htd = createTableDescriptor(name); 149 byte[] mb = TableDescriptorBuilder.toByteArray(htd); 150 TableDescriptor deserializedHtd = TableDescriptorBuilder.parseFrom(mb); 151 assertEquals(htd.getTableName(), deserializedHtd.getTableName()); 152 } 153 154 /** 155 * Test RegionInfo serialization 156 */ 157 @Test 158 public void testRegionInfo() throws Exception { 159 RegionInfo hri = createRandomRegion("testRegionInfo"); 160 161 // test toByteArray() 162 byte[] hrib = RegionInfo.toByteArray(hri); 163 RegionInfo deserializedHri = RegionInfo.parseFrom(hrib); 164 assertEquals(hri.getEncodedName(), deserializedHri.getEncodedName()); 165 assertEquals(hri, deserializedHri); 166 167 // test toDelimitedByteArray() 168 hrib = RegionInfo.toDelimitedByteArray(hri); 169 DataInputBuffer buf = new DataInputBuffer(); 170 try { 171 buf.reset(hrib, hrib.length); 172 deserializedHri = RegionInfo.parseFrom(buf); 173 assertEquals(hri.getEncodedName(), deserializedHri.getEncodedName()); 174 assertEquals(hri, deserializedHri); 175 } finally { 176 buf.close(); 177 } 178 } 179 180 @Test 181 public void testRegionInfos() throws Exception { 182 RegionInfo hri = createRandomRegion("testRegionInfos"); 183 byte[] triple = RegionInfo.toDelimitedByteArray(hri, hri, hri); 184 List<RegionInfo> regions = RegionInfo.parseDelimitedFrom(triple, 0, triple.length); 185 assertTrue(regions.size() == 3); 186 assertTrue(regions.get(0).equals(regions.get(1))); 187 assertTrue(regions.get(0).equals(regions.get(2))); 188 } 189 190 private RegionInfo createRandomRegion(final String name) { 191 TableDescriptorBuilder tableDescriptorBuilder = 192 TableDescriptorBuilder.newBuilder(TableName.valueOf(name)); 193 String[] families = new String[] { "info", "anchor" }; 194 for (int i = 0; i < families.length; i++) { 195 ColumnFamilyDescriptor columnFamilyDescriptor = 196 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(families[i])).build(); 197 tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor); 198 } 199 TableDescriptor tableDescriptor = tableDescriptorBuilder.build(); 200 return RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build(); 201 } 202 203 @Test 204 public void testGet() throws Exception { 205 byte[] row = Bytes.toBytes("row"); 206 byte[] fam = Bytes.toBytes("fam"); 207 byte[] qf1 = Bytes.toBytes("qf1"); 208 long ts = EnvironmentEdgeManager.currentTime(); 209 int maxVersions = 2; 210 211 Get get = new Get(row); 212 get.addColumn(fam, qf1); 213 get.setTimeRange(ts, ts + 1); 214 get.readVersions(maxVersions); 215 216 ClientProtos.Get getProto = ProtobufUtil.toGet(get); 217 Get desGet = ProtobufUtil.toGet(getProto); 218 219 assertTrue(Bytes.equals(get.getRow(), desGet.getRow())); 220 Set<byte[]> set = null; 221 Set<byte[]> desSet = null; 222 223 for (Map.Entry<byte[], NavigableSet<byte[]>> entry : get.getFamilyMap().entrySet()) { 224 assertTrue(desGet.getFamilyMap().containsKey(entry.getKey())); 225 set = entry.getValue(); 226 desSet = desGet.getFamilyMap().get(entry.getKey()); 227 for (byte[] qualifier : set) { 228 assertTrue(desSet.contains(qualifier)); 229 } 230 } 231 232 assertEquals(get.getMaxVersions(), desGet.getMaxVersions()); 233 TimeRange tr = get.getTimeRange(); 234 TimeRange desTr = desGet.getTimeRange(); 235 assertEquals(tr.getMax(), desTr.getMax()); 236 assertEquals(tr.getMin(), desTr.getMin()); 237 } 238 239 @Test 240 public void testScan() throws Exception { 241 242 byte[] startRow = Bytes.toBytes("startRow"); 243 byte[] stopRow = Bytes.toBytes("stopRow"); 244 byte[] fam = Bytes.toBytes("fam"); 245 byte[] qf1 = Bytes.toBytes("qf1"); 246 long ts = EnvironmentEdgeManager.currentTime(); 247 int maxVersions = 2; 248 249 Scan scan = new Scan().withStartRow(startRow).withStopRow(stopRow); 250 scan.addColumn(fam, qf1); 251 scan.setTimeRange(ts, ts + 1); 252 scan.readVersions(maxVersions); 253 254 ClientProtos.Scan scanProto = ProtobufUtil.toScan(scan); 255 Scan desScan = ProtobufUtil.toScan(scanProto); 256 257 assertTrue(Bytes.equals(scan.getStartRow(), desScan.getStartRow())); 258 assertTrue(Bytes.equals(scan.getStopRow(), desScan.getStopRow())); 259 assertEquals(scan.getCacheBlocks(), desScan.getCacheBlocks()); 260 Set<byte[]> set = null; 261 Set<byte[]> desSet = null; 262 263 for (Map.Entry<byte[], NavigableSet<byte[]>> entry : scan.getFamilyMap().entrySet()) { 264 assertTrue(desScan.getFamilyMap().containsKey(entry.getKey())); 265 set = entry.getValue(); 266 desSet = desScan.getFamilyMap().get(entry.getKey()); 267 for (byte[] column : set) { 268 assertTrue(desSet.contains(column)); 269 } 270 271 // Test filters are serialized properly. 272 scan = new Scan().withStartRow(startRow); 273 final String name = "testScan"; 274 byte[] prefix = Bytes.toBytes(name); 275 scan.setFilter(new PrefixFilter(prefix)); 276 scanProto = ProtobufUtil.toScan(scan); 277 desScan = ProtobufUtil.toScan(scanProto); 278 Filter f = desScan.getFilter(); 279 assertTrue(f instanceof PrefixFilter); 280 } 281 282 assertEquals(scan.getMaxVersions(), desScan.getMaxVersions()); 283 TimeRange tr = scan.getTimeRange(); 284 TimeRange desTr = desScan.getTimeRange(); 285 assertEquals(tr.getMax(), desTr.getMax()); 286 assertEquals(tr.getMin(), desTr.getMin()); 287 } 288 289 protected static final int MAXVERSIONS = 3; 290 protected final static byte[] fam1 = Bytes.toBytes("colfamily1"); 291 protected final static byte[] fam2 = Bytes.toBytes("colfamily2"); 292 protected final static byte[] fam3 = Bytes.toBytes("colfamily3"); 293 protected static final byte[][] COLUMNS = { fam1, fam2, fam3 }; 294 295 /** 296 * Create a table of name <code>name</code> with {@link #COLUMNS} for families. 297 * @param name Name to give table. 298 * @return Column descriptor. 299 */ 300 protected TableDescriptor createTableDescriptor(final String name) { 301 return createTableDescriptor(name, MAXVERSIONS); 302 } 303 304 /** 305 * Create a table of name <code>name</code> with {@link #COLUMNS} for families. 306 * @param name Name to give table. 307 * @param versions How many versions to allow per column. 308 * @return Column descriptor. 309 */ 310 protected TableDescriptor createTableDescriptor(final String name, final int versions) { 311 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(TableName.valueOf(name)); 312 builder 313 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(fam1).setMaxVersions(versions) 314 .setBlockCacheEnabled(false).build()) 315 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(fam2).setMaxVersions(versions) 316 .setBlockCacheEnabled(false).build()) 317 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(fam3).setMaxVersions(versions) 318 .setBlockCacheEnabled(false).build()); 319 return builder.build(); 320 } 321}