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.Get;
035import org.apache.hadoop.hbase.client.Scan;
036import org.apache.hadoop.hbase.filter.BinaryComparator;
037import org.apache.hadoop.hbase.filter.Filter;
038import org.apache.hadoop.hbase.filter.PrefixFilter;
039import org.apache.hadoop.hbase.filter.RowFilter;
040import org.apache.hadoop.hbase.io.TimeRange;
041import org.apache.hadoop.hbase.testclassification.MiscTests;
042import org.apache.hadoop.hbase.testclassification.SmallTests;
043import org.apache.hadoop.hbase.util.Bytes;
044import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
045import org.apache.hadoop.io.DataInputBuffer;
046import org.junit.ClassRule;
047import org.junit.Test;
048import org.junit.experimental.categories.Category;
049
050import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
051import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos;
052
053/**
054 * Test HBase Writables serializations
055 */
056@Category({ MiscTests.class, SmallTests.class })
057public class TestSerialization {
058
059  @ClassRule
060  public static final HBaseClassTestRule CLASS_RULE =
061    HBaseClassTestRule.forClass(TestSerialization.class);
062
063  @Test
064  public void testKeyValue() throws Exception {
065    final String name = "testKeyValue2";
066    byte[] row = name.getBytes();
067    byte[] fam = "fam".getBytes();
068    byte[] qf = "qf".getBytes();
069    long ts = EnvironmentEdgeManager.currentTime();
070    byte[] val = "val".getBytes();
071    KeyValue kv = new KeyValue(row, fam, qf, ts, val);
072    ByteArrayOutputStream baos = new ByteArrayOutputStream();
073    DataOutputStream dos = new DataOutputStream(baos);
074    long l = KeyValueUtil.write(kv, dos);
075    dos.close();
076    byte[] mb = baos.toByteArray();
077    ByteArrayInputStream bais = new ByteArrayInputStream(mb);
078    DataInputStream dis = new DataInputStream(bais);
079    KeyValue deserializedKv = KeyValueUtil.create(dis);
080    assertTrue(Bytes.equals(kv.getBuffer(), deserializedKv.getBuffer()));
081    assertEquals(kv.getOffset(), deserializedKv.getOffset());
082    assertEquals(kv.getLength(), deserializedKv.getLength());
083  }
084
085  @Test
086  public void testCreateKeyValueInvalidNegativeLength() {
087
088    KeyValue kv_0 = new KeyValue(Bytes.toBytes("myRow"), Bytes.toBytes("myCF"), // 51 bytes
089      Bytes.toBytes("myQualifier"), 12345L, Bytes.toBytes("my12345"));
090
091    KeyValue kv_1 = new KeyValue(Bytes.toBytes("myRow"), Bytes.toBytes("myCF"), // 49 bytes
092      Bytes.toBytes("myQualifier"), 12345L, Bytes.toBytes("my123"));
093
094    ByteArrayOutputStream baos = new ByteArrayOutputStream();
095    DataOutputStream dos = new DataOutputStream(baos);
096
097    long l = 0;
098    try {
099      l = KeyValue.oswrite(kv_0, dos, false);
100      l += KeyValue.oswrite(kv_1, dos, false);
101      assertEquals(100L, l);
102    } catch (IOException e) {
103      fail("Unexpected IOException" + e.getMessage());
104    }
105
106    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
107    DataInputStream dis = new DataInputStream(bais);
108
109    try {
110      KeyValueUtil.create(dis);
111      assertTrue(kv_0.equals(kv_1));
112    } catch (Exception e) {
113      fail("Unexpected Exception" + e.getMessage());
114    }
115
116    // length -1
117    try {
118      // even if we have a good kv now in dis we will just pass length with -1 for simplicity
119      KeyValueUtil.create(-1, dis);
120      fail("Expected corrupt stream");
121    } catch (Exception e) {
122      assertEquals("Failed read -1 bytes, stream corrupt?", e.getMessage());
123    }
124
125  }
126
127  @Test
128  public void testCompareFilter() throws Exception {
129    Filter f =
130      new RowFilter(CompareOperator.EQUAL, new BinaryComparator(Bytes.toBytes("testRowOne-2")));
131    byte[] bytes = f.toByteArray();
132    Filter ff = RowFilter.parseFrom(bytes);
133    assertNotNull(ff);
134  }
135
136  @Test
137  public void testTableDescriptor() throws Exception {
138    final String name = "testTableDescriptor";
139    HTableDescriptor htd = createTableDescriptor(name);
140    byte[] mb = htd.toByteArray();
141    HTableDescriptor deserializedHtd = HTableDescriptor.parseFrom(mb);
142    assertEquals(htd.getTableName(), deserializedHtd.getTableName());
143  }
144
145  /**
146   * Test RegionInfo serialization
147   */
148  @Test
149  public void testRegionInfo() throws Exception {
150    HRegionInfo hri = createRandomRegion("testRegionInfo");
151
152    // test toByteArray()
153    byte[] hrib = hri.toByteArray();
154    HRegionInfo deserializedHri = HRegionInfo.parseFrom(hrib);
155    assertEquals(hri.getEncodedName(), deserializedHri.getEncodedName());
156    assertEquals(hri, deserializedHri);
157
158    // test toDelimitedByteArray()
159    hrib = hri.toDelimitedByteArray();
160    DataInputBuffer buf = new DataInputBuffer();
161    try {
162      buf.reset(hrib, hrib.length);
163      deserializedHri = HRegionInfo.parseFrom(buf);
164      assertEquals(hri.getEncodedName(), deserializedHri.getEncodedName());
165      assertEquals(hri, deserializedHri);
166    } finally {
167      buf.close();
168    }
169  }
170
171  @Test
172  public void testRegionInfos() throws Exception {
173    HRegionInfo hri = createRandomRegion("testRegionInfos");
174    byte[] triple = HRegionInfo.toDelimitedByteArray(hri, hri, hri);
175    List<HRegionInfo> regions = HRegionInfo.parseDelimitedFrom(triple, 0, triple.length);
176    assertTrue(regions.size() == 3);
177    assertTrue(regions.get(0).equals(regions.get(1)));
178    assertTrue(regions.get(0).equals(regions.get(2)));
179  }
180
181  private HRegionInfo createRandomRegion(final String name) {
182    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name));
183    String[] families = new String[] { "info", "anchor" };
184    for (int i = 0; i < families.length; i++) {
185      htd.addFamily(new HColumnDescriptor(families[i]));
186    }
187    return new HRegionInfo(htd.getTableName(), HConstants.EMPTY_START_ROW,
188      HConstants.EMPTY_END_ROW);
189  }
190
191  @Test
192  public void testGet() throws Exception {
193    byte[] row = "row".getBytes();
194    byte[] fam = "fam".getBytes();
195    byte[] qf1 = "qf1".getBytes();
196    long ts = EnvironmentEdgeManager.currentTime();
197    int maxVersions = 2;
198
199    Get get = new Get(row);
200    get.addColumn(fam, qf1);
201    get.setTimeRange(ts, ts + 1);
202    get.setMaxVersions(maxVersions);
203
204    ClientProtos.Get getProto = ProtobufUtil.toGet(get);
205    Get desGet = ProtobufUtil.toGet(getProto);
206
207    assertTrue(Bytes.equals(get.getRow(), desGet.getRow()));
208    Set<byte[]> set = null;
209    Set<byte[]> desSet = null;
210
211    for (Map.Entry<byte[], NavigableSet<byte[]>> entry : get.getFamilyMap().entrySet()) {
212      assertTrue(desGet.getFamilyMap().containsKey(entry.getKey()));
213      set = entry.getValue();
214      desSet = desGet.getFamilyMap().get(entry.getKey());
215      for (byte[] qualifier : set) {
216        assertTrue(desSet.contains(qualifier));
217      }
218    }
219
220    assertEquals(get.getMaxVersions(), desGet.getMaxVersions());
221    TimeRange tr = get.getTimeRange();
222    TimeRange desTr = desGet.getTimeRange();
223    assertEquals(tr.getMax(), desTr.getMax());
224    assertEquals(tr.getMin(), desTr.getMin());
225  }
226
227  @Test
228  public void testScan() throws Exception {
229    byte[] startRow = "startRow".getBytes();
230    byte[] stopRow = "stopRow".getBytes();
231    byte[] fam = "fam".getBytes();
232    byte[] qf1 = "qf1".getBytes();
233    long ts = EnvironmentEdgeManager.currentTime();
234    int maxVersions = 2;
235
236    Scan scan = new Scan(startRow, stopRow);
237    scan.addColumn(fam, qf1);
238    scan.setTimeRange(ts, ts + 1);
239    scan.setMaxVersions(maxVersions);
240
241    ClientProtos.Scan scanProto = ProtobufUtil.toScan(scan);
242    Scan desScan = ProtobufUtil.toScan(scanProto);
243
244    assertTrue(Bytes.equals(scan.getStartRow(), desScan.getStartRow()));
245    assertTrue(Bytes.equals(scan.getStopRow(), desScan.getStopRow()));
246    assertEquals(scan.getCacheBlocks(), desScan.getCacheBlocks());
247    Set<byte[]> set = null;
248    Set<byte[]> desSet = null;
249
250    for (Map.Entry<byte[], NavigableSet<byte[]>> entry : scan.getFamilyMap().entrySet()) {
251      assertTrue(desScan.getFamilyMap().containsKey(entry.getKey()));
252      set = entry.getValue();
253      desSet = desScan.getFamilyMap().get(entry.getKey());
254      for (byte[] column : set) {
255        assertTrue(desSet.contains(column));
256      }
257
258      // Test filters are serialized properly.
259      scan = new Scan(startRow);
260      final String name = "testScan";
261      byte[] prefix = Bytes.toBytes(name);
262      scan.setFilter(new PrefixFilter(prefix));
263      scanProto = ProtobufUtil.toScan(scan);
264      desScan = ProtobufUtil.toScan(scanProto);
265      Filter f = desScan.getFilter();
266      assertTrue(f instanceof PrefixFilter);
267    }
268
269    assertEquals(scan.getMaxVersions(), desScan.getMaxVersions());
270    TimeRange tr = scan.getTimeRange();
271    TimeRange desTr = desScan.getTimeRange();
272    assertEquals(tr.getMax(), desTr.getMax());
273    assertEquals(tr.getMin(), desTr.getMin());
274  }
275
276  /*
277   * TODO
278   * @Test public void testResultEmpty() throws Exception { List<KeyValue> keys = new
279   * ArrayList<KeyValue>(); Result r = Result.newResult(keys); assertTrue(r.isEmpty()); byte [] rb =
280   * Writables.getBytes(r); Result deserializedR = (Result)Writables.getWritable(rb, new Result());
281   * assertTrue(deserializedR.isEmpty()); }
282   * @Test public void testResult() throws Exception { byte [] rowA = Bytes.toBytes("rowA"); byte []
283   * famA = Bytes.toBytes("famA"); byte [] qfA = Bytes.toBytes("qfA"); byte [] valueA =
284   * Bytes.toBytes("valueA"); byte [] rowB = Bytes.toBytes("rowB"); byte [] famB =
285   * Bytes.toBytes("famB"); byte [] qfB = Bytes.toBytes("qfB"); byte [] valueB =
286   * Bytes.toBytes("valueB"); KeyValue kvA = new KeyValue(rowA, famA, qfA, valueA); KeyValue kvB =
287   * new KeyValue(rowB, famB, qfB, valueB); Result result = Result.newResult(new KeyValue[]{kvA,
288   * kvB}); byte [] rb = Writables.getBytes(result); Result deResult =
289   * (Result)Writables.getWritable(rb, new Result());
290   * assertTrue("results are not equivalent, first key mismatch",
291   * result.raw()[0].equals(deResult.raw()[0]));
292   * assertTrue("results are not equivalent, second key mismatch",
293   * result.raw()[1].equals(deResult.raw()[1])); // Test empty Result Result r = new Result(); byte
294   * [] b = Writables.getBytes(r); Result deserialized = (Result)Writables.getWritable(b, new
295   * Result()); assertEquals(r.size(), deserialized.size()); }
296   * @Test public void testResultDynamicBuild() throws Exception { byte [] rowA =
297   * Bytes.toBytes("rowA"); byte [] famA = Bytes.toBytes("famA"); byte [] qfA =
298   * Bytes.toBytes("qfA"); byte [] valueA = Bytes.toBytes("valueA"); byte [] rowB =
299   * Bytes.toBytes("rowB"); byte [] famB = Bytes.toBytes("famB"); byte [] qfB =
300   * Bytes.toBytes("qfB"); byte [] valueB = Bytes.toBytes("valueB"); KeyValue kvA = new
301   * KeyValue(rowA, famA, qfA, valueA); KeyValue kvB = new KeyValue(rowB, famB, qfB, valueB); Result
302   * result = Result.newResult(new KeyValue[]{kvA, kvB}); byte [] rb = Writables.getBytes(result);
303   * // Call getRow() first Result deResult = (Result)Writables.getWritable(rb, new Result()); byte
304   * [] row = deResult.getRow(); assertTrue(Bytes.equals(row, rowA)); // Call sorted() first
305   * deResult = (Result)Writables.getWritable(rb, new Result());
306   * assertTrue("results are not equivalent, first key mismatch",
307   * result.raw()[0].equals(deResult.raw()[0]));
308   * assertTrue("results are not equivalent, second key mismatch",
309   * result.raw()[1].equals(deResult.raw()[1])); // Call raw() first deResult =
310   * (Result)Writables.getWritable(rb, new Result());
311   * assertTrue("results are not equivalent, first key mismatch",
312   * result.raw()[0].equals(deResult.raw()[0]));
313   * assertTrue("results are not equivalent, second key mismatch",
314   * result.raw()[1].equals(deResult.raw()[1])); }
315   * @Test public void testResultArray() throws Exception { byte [] rowA = Bytes.toBytes("rowA");
316   * byte [] famA = Bytes.toBytes("famA"); byte [] qfA = Bytes.toBytes("qfA"); byte [] valueA =
317   * Bytes.toBytes("valueA"); byte [] rowB = Bytes.toBytes("rowB"); byte [] famB =
318   * Bytes.toBytes("famB"); byte [] qfB = Bytes.toBytes("qfB"); byte [] valueB =
319   * Bytes.toBytes("valueB"); KeyValue kvA = new KeyValue(rowA, famA, qfA, valueA); KeyValue kvB =
320   * new KeyValue(rowB, famB, qfB, valueB); Result result1 = Result.newResult(new KeyValue[]{kvA,
321   * kvB}); Result result2 = Result.newResult(new KeyValue[]{kvB}); Result result3 =
322   * Result.newResult(new KeyValue[]{kvB}); Result [] results = new Result [] {result1, result2,
323   * result3}; ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream out
324   * = new DataOutputStream(byteStream); Result.writeArray(out, results); byte [] rb =
325   * byteStream.toByteArray(); DataInputBuffer in = new DataInputBuffer(); in.reset(rb, 0,
326   * rb.length); Result [] deResults = Result.readArray(in); assertTrue(results.length ==
327   * deResults.length); for(int i=0;i<results.length;i++) { KeyValue [] keysA = results[i].raw();
328   * KeyValue [] keysB = deResults[i].raw(); assertTrue(keysA.length == keysB.length); for(int
329   * j=0;j<keysA.length;j++) { assertTrue("Expected equivalent keys but found:\n" + "KeyA : " +
330   * keysA[j].toString() + "\n" + "KeyB : " + keysB[j].toString() + "\n" + keysA.length +
331   * " total keys, " + i + "th so far" ,keysA[j].equals(keysB[j])); } } }
332   * @Test public void testResultArrayEmpty() throws Exception { List<KeyValue> keys = new
333   * ArrayList<KeyValue>(); Result r = Result.newResult(keys); Result [] results = new Result []
334   * {r}; ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream out = new
335   * DataOutputStream(byteStream); Result.writeArray(out, results); results = null; byteStream = new
336   * ByteArrayOutputStream(); out = new DataOutputStream(byteStream); Result.writeArray(out,
337   * results); byte [] rb = byteStream.toByteArray(); DataInputBuffer in = new DataInputBuffer();
338   * in.reset(rb, 0, rb.length); Result [] deResults = Result.readArray(in);
339   * assertTrue(deResults.length == 0); results = new Result[0]; byteStream = new
340   * ByteArrayOutputStream(); out = new DataOutputStream(byteStream); Result.writeArray(out,
341   * results); rb = byteStream.toByteArray(); in = new DataInputBuffer(); in.reset(rb, 0,
342   * rb.length); deResults = Result.readArray(in); assertTrue(deResults.length == 0); }
343   */
344
345  protected static final int MAXVERSIONS = 3;
346  protected final static byte[] fam1 = Bytes.toBytes("colfamily1");
347  protected final static byte[] fam2 = Bytes.toBytes("colfamily2");
348  protected final static byte[] fam3 = Bytes.toBytes("colfamily3");
349  protected static final byte[][] COLUMNS = { fam1, fam2, fam3 };
350
351  /**
352   * Create a table of name <code>name</code> with {@link #COLUMNS} for families.
353   * @param name Name to give table.
354   * @return Column descriptor.
355   */
356  protected HTableDescriptor createTableDescriptor(final String name) {
357    return createTableDescriptor(name, MAXVERSIONS);
358  }
359
360  /**
361   * Create a table of name <code>name</code> with {@link #COLUMNS} for families.
362   * @param name     Name to give table.
363   * @param versions How many versions to allow per column.
364   * @return Column descriptor.
365   */
366  protected HTableDescriptor createTableDescriptor(final String name, final int versions) {
367    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name));
368    htd.addFamily(new HColumnDescriptor(fam1).setMaxVersions(versions).setBlockCacheEnabled(false));
369    htd.addFamily(new HColumnDescriptor(fam2).setMaxVersions(versions).setBlockCacheEnabled(false));
370    htd.addFamily(new HColumnDescriptor(fam3).setMaxVersions(versions).setBlockCacheEnabled(false));
371    return htd;
372  }
373}