1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.client;
20
21 import java.io.IOException;
22 import java.util.Arrays;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.hadoop.classification.InterfaceAudience;
27 import org.apache.hadoop.classification.InterfaceStability;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.hbase.HConstants;
30 import org.apache.hadoop.hbase.TableName;
31 import org.apache.hadoop.hbase.util.Bytes;
32
33
34
35
36 @InterfaceAudience.Public
37 @InterfaceStability.Evolving
38 public class ReversedClientScanner extends ClientScanner {
39 private static final Log LOG = LogFactory.getLog(ReversedClientScanner.class);
40
41
42 static byte[] MAX_BYTE_ARRAY = Bytes.createMaxByteArray(9);
43
44
45
46
47
48
49
50
51
52 public ReversedClientScanner(Configuration conf, Scan scan,
53 TableName tableName, HConnection connection) throws IOException {
54 super(conf, scan, tableName, connection);
55 }
56
57 @Override
58 protected boolean nextScanner(int nbRows, final boolean done)
59 throws IOException {
60
61 if (this.callable != null) {
62 this.callable.setClose();
63 this.caller.callWithRetries(callable);
64 this.callable = null;
65 }
66
67
68 byte[] localStartKey;
69 boolean locateTheClosestFrontRow = true;
70
71 if (this.currentRegion != null) {
72 byte[] startKey = this.currentRegion.getStartKey();
73 if (startKey == null
74 || Bytes.equals(startKey, HConstants.EMPTY_BYTE_ARRAY)
75 || checkScanStopRow(startKey) || done) {
76 close();
77 if (LOG.isDebugEnabled()) {
78 LOG.debug("Finished " + this.currentRegion);
79 }
80 return false;
81 }
82 localStartKey = startKey;
83 if (LOG.isDebugEnabled()) {
84 LOG.debug("Finished " + this.currentRegion);
85 }
86 } else {
87 localStartKey = this.scan.getStartRow();
88 if (!Bytes.equals(localStartKey, HConstants.EMPTY_BYTE_ARRAY)) {
89 locateTheClosestFrontRow = false;
90 }
91 }
92
93 if (LOG.isDebugEnabled() && this.currentRegion != null) {
94
95 LOG.debug("Advancing internal scanner to startKey at '"
96 + Bytes.toStringBinary(localStartKey) + "'");
97 }
98 try {
99
100
101
102
103
104
105
106 byte[] locateStartRow = locateTheClosestFrontRow ? createClosestRowBefore(localStartKey)
107 : null;
108 callable = getScannerCallable(localStartKey, nbRows, locateStartRow);
109
110
111 this.caller.callWithRetries(callable);
112 this.currentRegion = callable.getHRegionInfo();
113 if (this.scanMetrics != null) {
114 this.scanMetrics.countOfRegions.incrementAndGet();
115 }
116 } catch (IOException e) {
117 close();
118 throw e;
119 }
120 return true;
121 }
122
123 protected ScannerCallable getScannerCallable(byte[] localStartKey,
124 int nbRows, byte[] locateStartRow) {
125 scan.setStartRow(localStartKey);
126 ScannerCallable s = new ReversedScannerCallable(getConnection(),
127 getTable(), scan, this.scanMetrics, locateStartRow);
128 s.setCaching(nbRows);
129 return s;
130 }
131
132 @Override
133
134 protected boolean checkScanStopRow(final byte[] startKey) {
135 if (this.scan.getStopRow().length > 0) {
136
137 byte[] stopRow = scan.getStopRow();
138 int cmp = Bytes.compareTo(stopRow, 0, stopRow.length, startKey, 0,
139 startKey.length);
140 if (cmp >= 0) {
141
142
143 return true;
144 }
145 }
146 return false;
147 }
148
149
150
151
152
153
154 private byte[] createClosestRowBefore(byte[] row) {
155 if (row == null) {
156 throw new IllegalArgumentException("The passed row is empty");
157 }
158 if (Bytes.equals(row, HConstants.EMPTY_BYTE_ARRAY)) {
159 return MAX_BYTE_ARRAY;
160 }
161 if (row[row.length - 1] == 0) {
162 return Arrays.copyOf(row, row.length - 1);
163 } else {
164 byte[] closestFrontRow = Arrays.copyOf(row, row.length);
165 closestFrontRow[row.length - 1] = (byte) ((closestFrontRow[row.length - 1] & 0xff) - 1);
166 closestFrontRow = Bytes.add(closestFrontRow, MAX_BYTE_ARRAY);
167 return closestFrontRow;
168 }
169 }
170
171 }