001/*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020package org.apache.hadoop.hbase.client;
021
022import java.io.IOException;
023import java.lang.reflect.InvocationTargetException;
024import java.lang.reflect.Method;
025import java.util.Collections;
026import java.util.Map;
027import org.apache.commons.lang3.builder.EqualsBuilder;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.filter.Filter;
030import org.apache.hadoop.hbase.filter.FilterList;
031import org.apache.hadoop.hbase.security.access.Permission;
032import org.apache.hadoop.hbase.security.visibility.Authorizations;
033import org.apache.hadoop.hbase.testclassification.ClientTests;
034import org.apache.hadoop.hbase.testclassification.SmallTests;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.junit.ClassRule;
037import org.junit.Test;
038import org.junit.experimental.categories.Category;
039import org.mockito.Mockito;
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042import static org.junit.Assert.assertArrayEquals;
043import static org.junit.Assert.assertEquals;
044
045/**
046 * Small tests for ImmutableScan
047 */
048@Category({ ClientTests.class, SmallTests.class })
049public class TestImmutableScan {
050
051  @ClassRule
052  public static final HBaseClassTestRule CLASS_RULE =
053    HBaseClassTestRule.forClass(TestImmutableScan.class);
054
055  private static final Logger LOG = LoggerFactory.getLogger(TestImmutableScan.class);
056
057  @Test
058  public void testScanCopyConstructor() throws Exception {
059    Scan scan = new Scan();
060
061    scan.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("q"))
062      .setACL("test_user2", new Permission(Permission.Action.READ))
063      .setAllowPartialResults(true)
064      .setAsyncPrefetch(false)
065      .setAttribute("test_key", Bytes.toBytes("test_value"))
066      .setAuthorizations(new Authorizations("test_label"))
067      .setBatch(10)
068      .setCacheBlocks(false)
069      .setCaching(10)
070      .setConsistency(Consistency.TIMELINE)
071      .setFilter(new FilterList())
072      .setId("scan_copy_constructor")
073      .setIsolationLevel(IsolationLevel.READ_COMMITTED)
074      .setLimit(100)
075      .setLoadColumnFamiliesOnDemand(false)
076      .setMaxResultSize(100)
077      .setMaxResultsPerColumnFamily(1000)
078      .readVersions(9999)
079      .setMvccReadPoint(5)
080      .setNeedCursorResult(true)
081      .setPriority(1)
082      .setRaw(true)
083      .setReplicaId(3)
084      .setReversed(true)
085      .setRowOffsetPerColumnFamily(5)
086      .setRowPrefixFilter(Bytes.toBytes("row_"))
087      .setScanMetricsEnabled(true)
088      .setSmall(true)
089      .setReadType(Scan.ReadType.STREAM)
090      .withStartRow(Bytes.toBytes("row_1"))
091      .withStopRow(Bytes.toBytes("row_2"))
092      .setTimeRange(0, 13);
093
094    // create a copy of existing scan object
095    Scan scanCopy = new ImmutableScan(scan);
096
097    // validate fields of copied scan object match with the original scan object
098    assertArrayEquals(scan.getACL(), scanCopy.getACL());
099    assertEquals(scan.getAllowPartialResults(), scanCopy.getAllowPartialResults());
100    assertArrayEquals(scan.getAttribute("test_key"), scanCopy.getAttribute("test_key"));
101    assertEquals(scan.getAttributeSize(), scanCopy.getAttributeSize());
102    assertEquals(scan.getAttributesMap(), scanCopy.getAttributesMap());
103    assertEquals(scan.getAuthorizations().getLabels(), scanCopy.getAuthorizations().getLabels());
104    assertEquals(scan.getBatch(), scanCopy.getBatch());
105    assertEquals(scan.getCacheBlocks(), scanCopy.getCacheBlocks());
106    assertEquals(scan.getCaching(), scanCopy.getCaching());
107    assertEquals(scan.getConsistency(), scanCopy.getConsistency());
108    assertEquals(scan.getFamilies().length, scanCopy.getFamilies().length);
109    assertArrayEquals(scan.getFamilies()[0], scanCopy.getFamilies()[0]);
110    assertEquals(scan.getFamilyMap(), scanCopy.getFamilyMap());
111    assertEquals(scan.getFilter(), scanCopy.getFilter());
112    assertEquals(scan.getId(), scanCopy.getId());
113    assertEquals(scan.getIsolationLevel(), scanCopy.getIsolationLevel());
114    assertEquals(scan.getLimit(), scanCopy.getLimit());
115    assertEquals(scan.getLoadColumnFamiliesOnDemandValue(),
116      scanCopy.getLoadColumnFamiliesOnDemandValue());
117    assertEquals(scan.getMaxResultSize(), scanCopy.getMaxResultSize());
118    assertEquals(scan.getMaxResultsPerColumnFamily(), scanCopy.getMaxResultsPerColumnFamily());
119    assertEquals(scan.getMaxVersions(), scanCopy.getMaxVersions());
120    assertEquals(scan.getMvccReadPoint(), scanCopy.getMvccReadPoint());
121    assertEquals(scan.getPriority(), scanCopy.getPriority());
122    assertEquals(scan.getReadType(), scanCopy.getReadType());
123    assertEquals(scan.getReplicaId(), scanCopy.getReplicaId());
124    assertEquals(scan.getRowOffsetPerColumnFamily(), scanCopy.getRowOffsetPerColumnFamily());
125    assertArrayEquals(scan.getStartRow(), scanCopy.getStartRow());
126    assertArrayEquals(scan.getStopRow(), scanCopy.getStopRow());
127    assertEquals(scan.getTimeRange(), scanCopy.getTimeRange());
128    assertEquals(scan.getFingerprint(), scanCopy.getFingerprint());
129    assertEquals(scan.toMap(1), scanCopy.toMap(1));
130    assertEquals(scan.toString(2), scanCopy.toString(2));
131    assertEquals(scan.toJSON(2), scanCopy.toJSON(2));
132
133    LOG.debug("Compare all getters of scan and scanCopy.");
134    compareGetters(scan, scanCopy);
135
136    testUnmodifiableSetters(scanCopy);
137  }
138
139  private void testUnmodifiableSetters(Scan scanCopy) throws IOException {
140    try {
141      scanCopy.setFilter(Mockito.mock(Filter.class));
142      throw new RuntimeException("Should not reach here");
143    } catch (UnsupportedOperationException e) {
144      assertEquals("ImmutableScan does not allow access to setFilter", e.getMessage());
145    }
146    try {
147      scanCopy.addFamily(new byte[] { 0, 1 });
148      throw new RuntimeException("Should not reach here");
149    } catch (UnsupportedOperationException e) {
150      assertEquals("ImmutableScan does not allow access to addFamily", e.getMessage());
151    }
152    try {
153      scanCopy.addColumn(new byte[] { 0, 1 }, new byte[] { 2, 3 });
154      throw new RuntimeException("Should not reach here");
155    } catch (UnsupportedOperationException e) {
156      assertEquals("ImmutableScan does not allow access to addColumn", e.getMessage());
157    }
158    try {
159      scanCopy.setTimeRange(1L, 2L);
160      throw new RuntimeException("Should not reach here");
161    } catch (UnsupportedOperationException e) {
162      assertEquals("ImmutableScan does not allow access to setTimeRange", e.getMessage());
163    }
164    try {
165      scanCopy.setTimestamp(1L);
166      throw new RuntimeException("Should not reach here");
167    } catch (UnsupportedOperationException e) {
168      assertEquals("ImmutableScan does not allow access to setTimestamp", e.getMessage());
169    }
170    try {
171      scanCopy.setColumnFamilyTimeRange(new byte[] { 0 }, 1L, 2L);
172      throw new RuntimeException("Should not reach here");
173    } catch (UnsupportedOperationException e) {
174      assertEquals("ImmutableScan does not allow access to setColumnFamilyTimeRange",
175        e.getMessage());
176    }
177    try {
178      scanCopy.withStopRow(new byte[] { 1, 2 });
179      throw new RuntimeException("Should not reach here");
180    } catch (UnsupportedOperationException e) {
181      assertEquals("ImmutableScan does not allow access to withStopRow", e.getMessage());
182    }
183    try {
184      scanCopy.setRowPrefixFilter(new byte[] { 1, 2 });
185      throw new RuntimeException("Should not reach here");
186    } catch (UnsupportedOperationException e) {
187      assertEquals("ImmutableScan does not allow access to setRowPrefixFilter", e.getMessage());
188    }
189    try {
190      scanCopy.readAllVersions();
191      throw new RuntimeException("Should not reach here");
192    } catch (UnsupportedOperationException e) {
193      assertEquals("ImmutableScan does not allow access to readAllVersions", e.getMessage());
194    }
195    try {
196      scanCopy.setBatch(1);
197      throw new RuntimeException("Should not reach here");
198    } catch (UnsupportedOperationException e) {
199      assertEquals("ImmutableScan does not allow access to setBatch", e.getMessage());
200    }
201    try {
202      scanCopy.setRowOffsetPerColumnFamily(1);
203      throw new RuntimeException("Should not reach here");
204    } catch (UnsupportedOperationException e) {
205      assertEquals("ImmutableScan does not allow access to setRowOffsetPerColumnFamily",
206        e.getMessage());
207    }
208    try {
209      scanCopy.setCaching(1);
210      throw new RuntimeException("Should not reach here");
211    } catch (UnsupportedOperationException e) {
212      assertEquals("ImmutableScan does not allow access to setCaching",
213        e.getMessage());
214    }
215    try {
216      scanCopy.setLoadColumnFamiliesOnDemand(true);
217      throw new RuntimeException("Should not reach here");
218    } catch (UnsupportedOperationException e) {
219      assertEquals("ImmutableScan does not allow access to setLoadColumnFamiliesOnDemand",
220        e.getMessage());
221    }
222    try {
223      scanCopy.setRaw(true);
224      throw new RuntimeException("Should not reach here");
225    } catch (UnsupportedOperationException e) {
226      assertEquals("ImmutableScan does not allow access to setRaw", e.getMessage());
227    }
228    try {
229      scanCopy.setAuthorizations(new Authorizations("test"));
230      throw new RuntimeException("Should not reach here");
231    } catch (UnsupportedOperationException e) {
232      assertEquals("ImmutableScan does not allow access to setAuthorizations", e.getMessage());
233    }
234    try {
235      scanCopy.setACL("user1", new Permission(Permission.Action.READ));
236      throw new RuntimeException("Should not reach here");
237    } catch (UnsupportedOperationException e) {
238      assertEquals("ImmutableScan does not allow access to setACL", e.getMessage());
239    }
240    try {
241      scanCopy.setReplicaId(12);
242      throw new RuntimeException("Should not reach here");
243    } catch (UnsupportedOperationException e) {
244      assertEquals("ImmutableScan does not allow access to setReplicaId", e.getMessage());
245    }
246    try {
247      scanCopy.setReadType(Scan.ReadType.STREAM);
248      throw new RuntimeException("Should not reach here");
249    } catch (UnsupportedOperationException e) {
250      assertEquals("ImmutableScan does not allow access to setReadType", e.getMessage());
251    }
252    try {
253      scanCopy.setOneRowLimit();
254      throw new RuntimeException("Should not reach here");
255    } catch (UnsupportedOperationException e) {
256      assertEquals("ImmutableScan does not allow access to setOneRowLimit", e.getMessage());
257    }
258    try {
259      scanCopy.setNeedCursorResult(false);
260      throw new RuntimeException("Should not reach here");
261    } catch (UnsupportedOperationException e) {
262      assertEquals("ImmutableScan does not allow access to setNeedCursorResult", e.getMessage());
263    }
264    try {
265      scanCopy.resetMvccReadPoint();
266      throw new RuntimeException("Should not reach here");
267    } catch (UnsupportedOperationException e) {
268      assertEquals("ImmutableScan does not allow access to resetMvccReadPoint", e.getMessage());
269    }
270    try {
271      scanCopy.setMvccReadPoint(1L);
272      throw new RuntimeException("Should not reach here");
273    } catch (UnsupportedOperationException e) {
274      assertEquals("ImmutableScan does not allow access to setMvccReadPoint", e.getMessage());
275    }
276    try {
277      scanCopy.setIsolationLevel(IsolationLevel.READ_UNCOMMITTED);
278      throw new RuntimeException("Should not reach here");
279    } catch (UnsupportedOperationException e) {
280      assertEquals("ImmutableScan does not allow access to setIsolationLevel", e.getMessage());
281    }
282    try {
283      scanCopy.setPriority(10);
284      throw new RuntimeException("Should not reach here");
285    } catch (UnsupportedOperationException e) {
286      assertEquals("ImmutableScan does not allow access to setPriority", e.getMessage());
287    }
288    try {
289      scanCopy.setConsistency(Consistency.TIMELINE);
290      throw new RuntimeException("Should not reach here");
291    } catch (UnsupportedOperationException e) {
292      assertEquals("ImmutableScan does not allow access to setConsistency", e.getMessage());
293    }
294    try {
295      scanCopy.setCacheBlocks(true);
296      throw new RuntimeException("Should not reach here");
297    } catch (UnsupportedOperationException e) {
298      assertEquals("ImmutableScan does not allow access to setCacheBlocks", e.getMessage());
299    }
300    try {
301      scanCopy.setAllowPartialResults(true);
302      throw new RuntimeException("Should not reach here");
303    } catch (UnsupportedOperationException e) {
304      assertEquals("ImmutableScan does not allow access to setAllowPartialResults",
305        e.getMessage());
306    }
307    try {
308      scanCopy.setId("id");
309      throw new RuntimeException("Should not reach here");
310    } catch (UnsupportedOperationException e) {
311      assertEquals("ImmutableScan does not allow access to setId", e.getMessage());
312    }
313    try {
314      scanCopy.setMaxResultSize(100);
315      throw new RuntimeException("Should not reach here");
316    } catch (UnsupportedOperationException e) {
317      assertEquals("ImmutableScan does not allow access to setMaxResultSize", e.getMessage());
318    }
319    try {
320      scanCopy.setMaxResultsPerColumnFamily(100);
321      throw new RuntimeException("Should not reach here");
322    } catch (UnsupportedOperationException e) {
323      assertEquals("ImmutableScan does not allow access to setMaxResultsPerColumnFamily",
324        e.getMessage());
325    }
326  }
327
328  private void compareGetters(Scan scan, Scan scanCopy) {
329    Method[] methods = Scan.class.getMethods();
330    for (Method method : methods) {
331      if (isGetter(method)) {
332        LOG.debug("Comparing return values of method: {}", method);
333        try {
334          Object obj1;
335          Object obj2;
336          switch (method.getName()) {
337            case "toMap": {
338              if (method.getParameterCount() == 1) {
339                obj1 = method.invoke(scan, 2);
340                obj2 = method.invoke(scanCopy, 2);
341                break;
342              }
343            }
344            case "getAttribute": {
345              if (method.getParameterCount() == 1) {
346                obj1 = method.invoke(scan, "acl");
347                obj2 = method.invoke(scanCopy, "acl");
348                break;
349              }
350            }
351            case "toString": {
352              if (method.getParameterCount() == 1) {
353                obj1 = method.invoke(scan, 25);
354                obj2 = method.invoke(scanCopy, 25);
355                break;
356              }
357            }
358            case "toJSON": {
359              if (method.getParameterCount() == 1) {
360                obj1 = method.invoke(scan, 25);
361                obj2 = method.invoke(scanCopy, 25);
362                break;
363              }
364            }
365            default: {
366              obj1 = method.invoke(scan);
367              obj2 = method.invoke(scanCopy);
368            }
369          }
370          if (obj1 instanceof Map && obj2 instanceof Map) {
371            obj1 = Collections.unmodifiableMap((Map<?, ?>) obj1);
372          }
373          if (!EqualsBuilder.reflectionEquals(obj1, obj2)) {
374            throw new AssertionError("Method " + method + " does not return equal values");
375          }
376        } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) {
377          throw new AssertionError("Error invoking method " + method, e);
378        }
379      }
380    }
381  }
382
383  private static boolean isGetter(Method method) {
384    if ("hashCode".equals(method.getName()) || "equals".equals(method.getName())
385        || method.getName().startsWith("set")) {
386      return false;
387    }
388    return !void.class.equals(method.getReturnType())
389      && !Scan.class.equals(method.getReturnType());
390  }
391
392}