/*
* Copyright 2015 NAVER Corp.
*
* Licensed 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 com.navercorp.pinpoint.common.hbase.parallel;
import com.navercorp.pinpoint.common.hbase.TableFactory;
import com.sematext.hbase.wd.AbstractRowKeyDistributor;
import com.sematext.hbase.wd.DistributedScanner;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Table;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* @author HyunGil Jeong
*/
public class ScanTask implements Runnable {
private static final Result END_RESULT = new Result();
private final TableName tableName;
private final TableFactory tableFactory;
private final AbstractRowKeyDistributor rowKeyDistributor;
private final Scan[] scans;
private final BlockingQueue<Result> resultQueue;
private volatile Throwable throwable;
private volatile boolean isQueueClosed = false;
private volatile boolean isDone = false;
public ScanTask(ScanTaskConfig scanTaskConfig, Scan... scans) {
if (scanTaskConfig == null) {
throw new NullPointerException("scanTaskConfig must not be null");
}
if (scans == null) {
throw new NullPointerException("scans must not be null");
}
if (scans.length == 0) {
throw new IllegalArgumentException("scans must not be empty");
}
this.tableName = scanTaskConfig.getTableName();
this.tableFactory = scanTaskConfig.getTableFactory();
this.rowKeyDistributor = scanTaskConfig.getRowKeyDistributor();
this.scans = scans;
this.resultQueue = new ArrayBlockingQueue<>(scanTaskConfig.getScanTaskQueueSize());
}
@Override
public void run() {
Table table = null;
try {
table = tableFactory.getTable(this.tableName);
ResultScanner scanner = createResultScanner(table);
try {
for (Result result : scanner) {
this.resultQueue.put(result);
if (this.isDone) {
break;
}
}
} finally {
this.isDone = true;
this.resultQueue.put(END_RESULT);
scanner.close();
}
} catch (Throwable th) {
this.throwable = th;
this.resultQueue.clear();
this.resultQueue.offer(END_RESULT);
} finally {
tableFactory.releaseTable(table);
}
}
private ResultScanner createResultScanner(Table table) throws IOException {
if (scans.length == 1) {
Scan scan = scans[0];
return table.getScanner(scan);
} else {
ResultScanner[] scanners = new ResultScanner[this.scans.length];
for (int i = 0; i < scanners.length; ++i) {
scanners[i] = table.getScanner(this.scans[i]);
}
return new DistributedScanner(this.rowKeyDistributor, scanners);
}
}
public Result getResult() throws InterruptedException {
if (this.isQueueClosed) {
return null;
}
Result take = this.resultQueue.take();
if (take == END_RESULT) {
this.isQueueClosed = true;
return null;
}
return take;
}
public void close() {
this.isDone = true;
// signal threads blocked on resultQueue
this.resultQueue.clear();
this.resultQueue.add(END_RESULT);
}
public Throwable getThrowable() {
return this.throwable;
}
}