package gr.ntua.ivml.mint.concurrent;
import gr.ntua.ivml.mint.persistent.DataUpload;
import gr.ntua.ivml.mint.persistent.Transformation;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.log4j.Logger;
/**
* Use queue to put your runnables into the appropriate queue. Add one if you think
* you need your own.
* @author Arne Stabenau
*
*/
public class Queues {
static Map<String, ExecutorService> queues = new HashMap<String,ExecutorService>();
static Map<Runnable, Future<?>> futures = new HashMap<Runnable, Future<?>>();
// only one queue at the moment
static {
// db queue allows 2 parallel jobs on db heavy stuff
// transformation goes here and upload part two ( parse and index )
ExecutorService e = Executors.newFixedThreadPool(2);
queues.put( "db", e);
// net queue allows for parallel network heavy operations
// upload part one goes here (with blob to db part)
e = Executors.newFixedThreadPool(4);
queues.put( "net", e);
// for stuff that is not thread safe
e = Executors.newFixedThreadPool(1);
queues.put( "single", e);
// Jobs that need a thread very often and immediately use this one
e = Executors.newCachedThreadPool();
queues.put( "now", e);
}
static Logger log = Logger.getLogger(Queues.class);
/**
* Lookup of the queue and put the Job there.
*
* "db" - queue for database heavy tasks (parsing / indexing)
* "net" - queue for network access (currently oai / ftp / http download )
* "now" - queue for simple thread pool access and immediate execution
* @param r
* @param queueName
*/
public synchronized static void queue( Runnable r, String queueName ) {
ExecutorService es = queues.get( queueName );
log.info( "Submitting "+ r.getClass().getCanonicalName() + " to " + queueName );
Future<?> f = es.submit(r);
// cleanup the futures
Iterator<Entry<Runnable,Future<?>>> i = futures.entrySet().iterator();
while( i.hasNext()) {
Entry<Runnable,Future<?>> e = i.next();
if( e.getValue().isDone())
i.remove();
}
// put the next
futures.put( r, f );
}
/**
* Convenience to queue transform. Its db heavy, so goes to db queue
* @param tr
*/
public static void queueTransformation(Transformation tr){
XSLTransform ts = new XSLTransform(tr);
queue( ts, "db" );
}
/**
* Try to cancel an upload. If its in the Queue or running
* this will do something, but it might not be here yet...
* It might still be in its own thread.
* @param du
*/
public synchronized static boolean cancelUpload( DataUpload du ) {
Long id = du.getDbID();
boolean success = false;
for( Entry<Runnable, Future<?>> e: futures.entrySet()) {
try {
Runnable r = e.getKey();
if( r instanceof UploadIndexer ) {
UploadIndexer ui = (UploadIndexer) r;
if( ui.getDataUpload().getDbID().equals( du.getDbID())) {
success = e.getValue().cancel(true);
}
}
} catch( Exception e2 ) {
log.error( "couldnt cancel Upload "+du.getDbID(), e2);
}
}
return success;
}
// TODO: This might work simply with Hash access .. :-) too scared to try
private static Future<?> getFuture( Runnable r ) {
for( Entry<Runnable, Future<?>> e: futures.entrySet())
if( e.getKey() == r ) return e.getValue();
return null;
}
/**
* Wait until the given Runnable has finished.
* @param r
*/
public static void join( Runnable r ) {
Future<?> f = getFuture(r);
if( f == null ) return;
else {
try {
log.debug( "Waiting for "+ (r.getClass().toString()));
f.get();
log.debug( "Done");
} catch( Exception e ) {
log.error( "Task didnt complete well ", e );
}
}
}
}