/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 2008.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.sail.rdbms.managers.base;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.SQLException;
import java.util.concurrent.BlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.openrdf.sail.rdbms.managers.helpers.BatchBlockingQueue;
import org.openrdf.sail.rdbms.schema.Batch;
public abstract class ManagerBase {
public static int BATCH_SIZE = 8 * 1024;
public static int MIN_QUEUE = 128;
public static int MAX_QUEUE = 96 * 1024;
Exception exc;
private Logger logger = LoggerFactory.getLogger(ManagerBase.class);
public final BlockingQueue<Batch> queue = new BatchBlockingQueue(MAX_QUEUE);
private final Object working = new Object();
private Batch wb;
private Thread thread;
private int count;
@SuppressWarnings("unchecked")
public BlockingQueue<Batch> getQueue() {
ClassLoader cl = getClass().getClassLoader();
Class<?>[] classes = new Class[] { BlockingQueue.class };
InvocationHandler h = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
Object result = method.invoke(queue, args);
checkQueueSize();
return result;
}
};
Object proxy = Proxy.newProxyInstance(cl, classes, h);
return (BlockingQueue<Batch>)proxy;
}
public void close()
throws SQLException
{
try {
flush();
if (thread != null) {
queue.put(Batch.CLOSED_SIGNAL);
thread.join();
}
}
catch (InterruptedException e) {
logger.warn(e.toString(), e);
}
throwException();
}
public void flush()
throws SQLException, InterruptedException
{
throwException();
synchronized (working) {
throwException();
for (Batch b = queue.poll(); isFlushable(b); b = queue.poll()) {
flush(b);
}
if (wb != null) {
flush(wb);
wb = null;
}
count = 0;
}
}
public void clear() {
queue.clear();
}
protected void optimize()
throws SQLException
{
// allow subclasses to optimise table
}
void checkQueueSize() {
if (++count >= MIN_QUEUE && thread == null) {
String name = getClass().getSimpleName() + "-flusher";
thread = new Thread(new Runnable() {
public void run() {
try {
insertThread(working);
}
catch (Exception e) {
exc = e;
logger.error(e.toString(), e);
}
}
}, name);
thread.start();
}
}
protected void flush(Batch batch)
throws SQLException
{
batch.flush();
}
void insertThread(Object working)
throws SQLException, InterruptedException
{
String name = Thread.currentThread().getName();
logger.debug("Starting helper thread {}", name);
int notReadyCount = 0;
for (wb = queue.take(); isFlushable(wb); wb = queue.take()) {
if (wb.isReady() || queue.size() <= notReadyCount) {
synchronized (working) {
if (wb != null) {
flush(wb);
wb = null;
}
}
optimize();
notReadyCount = 0;
}
else {
queue.add(wb);
notReadyCount++;
}
}
logger.debug("Closing helper thread {}", name);
}
private boolean isFlushable(Batch batch) {
return batch != null && batch != Batch.CLOSED_SIGNAL;
}
private void throwException()
throws SQLException
{
if (exc instanceof SQLException) {
SQLException e = (SQLException)exc;
exc = null;
throw e;
}
else if (exc instanceof RuntimeException) {
RuntimeException e = (RuntimeException)exc;
exc = null;
throw e;
}
}
}