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