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}