/** * Copyright 2011 The Apache Software Foundation * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.index.coprocessor.regionserver; import java.io.IOException; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.index.IndexSpecification; import org.apache.hadoop.hbase.regionserver.RegionScanner; import org.apache.hadoop.hbase.util.Bytes; public class LeafIndexRegionScanner implements IndexRegionScanner { private static final Log LOG = LogFactory.getLog(LeafIndexRegionScanner.class); private RegionScanner delegator = null; private KeyValue currentKV = null; private boolean hadMore = true; private final IndexSpecification index; private boolean isRangeScanner = false; private TTLExpiryChecker ttlExpiryChecker; private int scannerIndex = -1; public LeafIndexRegionScanner(IndexSpecification index, RegionScanner delegator, TTLExpiryChecker ttlExpiryChecker) { this.delegator = delegator; this.index = index; this.ttlExpiryChecker = ttlExpiryChecker; } @Override public void advance() { this.currentKV = null; } @Override public HRegionInfo getRegionInfo() { return this.delegator.getRegionInfo(); } @Override public boolean isFilterDone() { return this.delegator.isFilterDone(); } @Override public void setRangeFlag(boolean range) { isRangeScanner = range; } @Override public boolean isRange() { return this.isRangeScanner; } @Override public void setScannerIndex(int index) { scannerIndex = index; } @Override public int getScannerIndex() { return scannerIndex; } @Override public boolean hasChildScanners() { return false; } // TODO the passing row to be the full key in the index table. // The callee need to take care of this creation.. @Override public synchronized boolean reseek(byte[] row) throws IOException { if (!hadMore) return false; byte[] targetRowKey = createRowKeyForReseek(row); return this.delegator.reseek(targetRowKey); } private byte[] createRowKeyForReseek(byte[] targetRow) { byte[] curRK = this.currentKV.getRow(); byte[] curValue = this.currentKV.getValue(); short actualTabRKOffset = Bytes.toShort(curValue, 2); byte[] newRowKey = new byte[actualTabRKOffset + targetRow.length]; System.arraycopy(curRK, 0, newRowKey, 0, actualTabRKOffset); System.arraycopy(targetRow, 0, newRowKey, actualTabRKOffset, targetRow.length); return newRowKey; } @Override public synchronized void close() throws IOException { this.delegator.close(); } @Override public synchronized boolean next(List<KeyValue> results) throws IOException { boolean hasMore = false; do { // this check here will prevent extra next call when in the previous // next last row was fetched and after that an advance was called on this // scanner. So instead of making a next call again we can return from here. if (!this.hadMore) return false; hasMore = this.delegator.next(results); if (results != null && results.size() > 0) { KeyValue kv = results.get(0); if (this.ttlExpiryChecker.checkIfTTLExpired(this.index.getTTL(), kv.getTimestamp())) { results.clear(); LOG.info("The ttl has expired for the kv " + kv); } else { if (!isRangeScanner) { // This is need to reseek in case of EQUAL scanners. this.currentKV = kv; break; } } } } while (results.size() < 1 && hasMore); this.hadMore = hasMore; return hasMore; } @Override public boolean next(List<KeyValue> result, int limit) throws IOException { // We wont call this method at all.. As we have only CF:qualifier per row in index table. throw new UnsupportedOperationException("Use only next(List<KeyValue> results) method."); } @Override public long getMvccReadPoint() { // TODO Implement RegionScanner.getMvccReadPoint return 0; } @Override public boolean nextRaw(List<KeyValue> result, String metric) throws IOException { // TODO Implement RegionScanner.nextRaw return false; } @Override public boolean nextRaw(List<KeyValue> result, int limit, String metric) throws IOException { // TODO Implement RegionScanner.nextRaw return false; } @Override public boolean next(List<KeyValue> results, String metric) throws IOException { // TODO Implement InternalScanner.next return false; } @Override public boolean next(List<KeyValue> result, int limit, String metric) throws IOException { // TODO Implement InternalScanner.next return false; } }