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