package prefuse.data.io.sql; import java.util.logging.Logger; import prefuse.data.Table; import prefuse.data.io.DataIOException; import prefuse.util.PrefuseConfig; import prefuse.util.StringLib; import prefuse.util.collections.CopyOnWriteArrayList; /** * Worker thread that asynchronously handles a queue of jobs, with each job * responsible for issuing a query and processing the results. Currently * involves just a single thread, in the future this may be expanded to * thread pool for greater concurrency. * * @author <a href="http://jheer.org">jeffrey heer</a> * @see DatabaseDataSource */ public class DataSourceWorker extends Thread { private static Logger s_logger = Logger.getLogger(DataSourceWorker.class.getName()); // TODO: in future, may want to expand this to a thread pool private static DataSourceWorker s_instance; private static CopyOnWriteArrayList s_queue; /** * Submit a job to the worker thread. * @param e an {@link DataSourceWorker.Entry} instance that contains * the parameters of the job. */ public synchronized static void submit(Entry e) { // perform lazily initialization as needed if ( s_queue == null ) s_queue = new CopyOnWriteArrayList(); if ( s_instance == null ) s_instance = new DataSourceWorker(); // queue it up s_queue.add(e); // wake up a sleepy thread synchronized ( s_instance ) { s_instance.notify(); } } // ------------------------------------------------------------------------ /** * Create a new DataSourceWorker. */ private DataSourceWorker() { super("prefuse_DatabaseWorker"); int priority = PrefuseConfig.getInt("data.io.worker.threadPriority"); if ( priority >= Thread.MIN_PRIORITY && priority <= Thread.MAX_PRIORITY ) { this.setPriority(priority); } this.setDaemon(true); this.start(); } /** * @see java.lang.Runnable#run() */ public void run() { while ( true ) { Entry e = null; synchronized ( s_queue ) { if ( s_queue.size() > 0 ) e = (Entry)s_queue.remove(0); } if ( e != null ) { try { if ( e.listener != null ) e.listener.preQuery(e); e.ds.getData(e.table, e.query, e.keyField, e.lock); if ( e.listener != null ) e.listener.postQuery(e); } catch ( DataIOException dre ) { s_logger.warning(dre.getMessage() + "\n" + StringLib.getStackTrace(dre)); } } else { // nothing to do, chill out until notified try { synchronized (this) { wait(); } } catch (InterruptedException ex) { } } } } /** * Stores the parameters of a data query and processing job. * @author <a href="http://jheer.org">jeffrey heer</a> */ public static class Entry { /** * Create a new Entry. * @param ds the DatabaseDataSource to query * @param table the Table for storing the results * @param query the query to issue * @param keyField the key field that should be used to identify * when duplicate results occur * @param lock an optional lock to synchronize on when processing * data and adding it to the Table * @param listener an optional callback listener that allows * notifications to be issued before and after query processing */ public Entry(DatabaseDataSource ds, Table table, String query, String keyField, Object lock, Listener listener) { this.ds = ds; this.table = table; this.query = query; this.keyField = keyField; this.lock = lock; this.listener = listener; } /** The DatabaseDataSource to query. */ DatabaseDataSource ds; /** An optional callback listener that allows * notifications to be issued before and after query processing. */ Listener listener; /** The Table for storing the results. */ Table table; /** The query to issue. */ String query; /** The key field that should be used to identify * when duplicate results occur. */ String keyField; /** An optional lock to synchronize on when processing * data and adding it to the Table. */ Object lock; } /** * Listener interface for receiving notifications about the status of * a submitted data query and processing job. * @author <a href="http://jheer.org">jeffrey heer</a> */ public static interface Listener { /** * Notification that the query is about to be issued. * @param job the current job being processed */ public void preQuery(DataSourceWorker.Entry job); /** * Notification that the query processing has just completed. * @param job the current job being processed */ public void postQuery(DataSourceWorker.Entry job); } } // end of class DataSourceWorker