package guang.crawler.siteManager.jobQueue;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;
/**
* 基于JE(Berkeley DB Java Edition)构建的MapQueue.
*
* @author yang
*/
public class JEQueue<T> extends MapQueue<T> implements Sync {
/**
* 存放URL的Database
*/
protected Database urlsDB = null;
/**
* Berkeley DB的配置环境
*/
protected Environment env;
/**
* 是否是可以重复使用的数据库.如果是,那么程序退出后不删除数据文件,否则就清除所有的数据.类似于临时文件的deleteOnExit方式.
*/
protected boolean resumable;
/**
* 实现T类型数据的转换器,将T类型的数据转换成Berkeledy DB中的数据
*/
private final JEQueueElementTransfer<T> transfer;
/**
* 当前队列是否被关闭了.
*/
private boolean shutdown = false;
/**
* 在指定目录下创建JEQueue
*
* @param envHome
* JEQueue的本地文件目录
* @param dbName
* 数据库的名称
* @param resumable
* 数据库是否是可以被重复利用的;如果可以重复利用,那么在系统退出后并不删除数据文件,否则,所有的数据文件将被删除.
* @param transfer
* T类型的数据对象转换成Berkeley DB中存放的数据类型.
* @throws Exception
*/
public JEQueue(final File envHome, final String dbName,
final boolean resumable, final JEQueueElementTransfer<T> transfer)
throws Exception {
// 每个不同的siteManager都有其自身的工作目录
EnvironmentConfig envConfig = new EnvironmentConfig();
envConfig.setAllowCreate(true);
envConfig.setTransactional(resumable);
envConfig.setLocking(resumable);
this.env = new Environment(envHome, envConfig);
this.resumable = resumable;
DatabaseConfig dbConfig = new DatabaseConfig();
dbConfig.setAllowCreate(true);
dbConfig.setTransactional(resumable);
dbConfig.setDeferredWrite(!resumable);
this.urlsDB = this.env.openDatabase(null, dbName, dbConfig);
this.transfer = transfer;
}
@Override
public synchronized void close() {
this.shutdown = true;
try {
this.urlsDB.close();
} catch (DatabaseException e) {
e.printStackTrace();
}
}
@Override
public synchronized boolean delete(final T data) {
boolean success = false;
DatabaseEntry value = new DatabaseEntry();
this.transfer.objectToEntry(data, value);
Transaction txn;
if (this.resumable) {
txn = this.env.beginTransaction(null, null);
} else {
txn = null;
}
OperationStatus status = this.urlsDB.delete(txn,
this.transfer.getDatabaseEntryKey(data));
if ((status == OperationStatus.SUCCESS)
|| (status == OperationStatus.NOTFOUND)) {
success = true;
}
if (this.resumable) {
if (txn != null) {
txn.commit();
}
}
return success;
}
/**
* 最多获取max个元素
*/
@Override
public synchronized List<T> get(final int max) throws DatabaseException {
int matches = 0;
List<T> results = new ArrayList<T>(max);
Cursor cursor = null;
OperationStatus result;
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry value = new DatabaseEntry();
Transaction txn;
if (this.resumable) {
txn = this.env.beginTransaction(null, null);
} else {
txn = null;
}
try {
cursor = this.urlsDB.openCursor(txn, null);
result = cursor.getFirst(key, value, null);
while ((matches < max) && (result == OperationStatus.SUCCESS)) {
if (value.getData().length > 0) {
results.add(this.transfer.entryToObject(value));
cursor.delete();
matches++;
}
result = cursor.getNext(key, value, null);
}
} catch (DatabaseException e) {
if (txn != null) {
txn.abort();
txn = null;
}
throw e;
} finally {
if (cursor != null) {
cursor.close();
}
if (txn != null) {
txn.commit();
}
}
return results;
}
/**
* 获取工作队列的长度,也就是DB中存储的条目。
*/
@Override
public synchronized long getLength() {
if (this.shutdown) {
return -1;
}
try {
return this.urlsDB.count();
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
@Override
public MapQueueIterator<T> iterator() {
Cursor cursor = null;
Transaction txn;
if (this.resumable) {
txn = this.env.beginTransaction(null, null);
} else {
txn = null;
}
cursor = this.urlsDB.openCursor(txn, null);
return new JECursorIterator<T>(cursor, this.transfer);
}
@Override
public synchronized void put(final T data) {
DatabaseEntry value = new DatabaseEntry();
this.transfer.objectToEntry(data, value);
Transaction txn;
if (this.resumable) {
txn = this.env.beginTransaction(null, null);
} else {
txn = null;
}
this.urlsDB.put(txn, this.transfer.getDatabaseEntryKey(data), value);
if (this.resumable) {
if (txn != null) {
txn.commit();
}
}
}
/**
* 对数据进行一下同步,从而能够使数据与磁盘的同步。
*/
@Override
public synchronized void sync() {
if (this.resumable) {
return;
}
if (this.urlsDB == null) {
return;
}
try {
this.urlsDB.sync();
} catch (DatabaseException e) {
e.printStackTrace();
}
}
}