package org.gametrack.bf42.poll; import java.util.*; import org.gametrack.bf42.*; import org.gametrack.bf42.db.*; import org.gametrack.bf42.game.*; import org.gametrack.util.*; /** *
Title: BFTracker
*Description: Tracks online Battlefield 1942 games, outputs game records
*Copyright: Copyright (c) 2003
*Company: bf1942.gametrack.org
* @author Brian Cairns * @version 1.0 */ // This thread is responsible for polling, and checking up on results public final class PollManager extends Thread { boolean firstPoll = true; boolean DEBUG = false; volatile boolean bAlive = true; volatile Connection[] newCons = null; final MersenneTwisterFast random = new MersenneTwisterFast(); final TreeSet pollingQueue = new TreeSet(); final LinkedList[] checks = new LinkedList[Config.POLL_ATTEMPTS]; long currentTime; long waitTime; PollCheck currentCheck; Connection firstConnection; int status; String[] STATES = { "Unset", "ADDING", "SLEEP", "PROCESS" }; int ADD = 1, SLEEP = 2, PROC = 3; /** * Initial creation of polling queue * @param cons */ public PollManager( Connection[] cons ) { this.setName( "Polling Engine" ); this.setPriority( Thread.NORM_PRIORITY + 1 ); addRandomlyToPollingQueue( cons ); firstConnection = ( ( PollCheck )pollingQueue.first() ).connection; } private void addRandomlyToPollingQueue( Connection cons[] ) { status = ADD; Log.log( "AddRandomlyToPollingQueue( " + cons.length + " )" ); long currentTime = System.currentTimeMillis(); int pollingPeriod = Config.POLL_PERIOD * 1000; for( int i = cons.length - 1; i >= 0; i-- ) pollingQueue.add( new PollCheck( cons[ i ], currentTime + random.nextInt( pollingPeriod ), 0 ) ); } public void deliverNewConnections( Connection[] cons ) { newCons = cons; } public void checkNewConnections() { if( newCons != null ) { Connection[] tempCons = newCons; newCons = null; if( tempCons.length > 0 ) { Log.log( "PollManager: Starting polling for " + tempCons.length + " new servers" ); // for( int i = 0; i < cons.length; i++ ) // cons[i] = Main.startTracking( ( Server )list.get( i ) ); // addRandomlyToPollingQueue( tempCons ); } } } public String toString() { return this.getName() + " " + STATES[status]; } public void run() { int retryLevel = 0; Log.log( "PollManager started. Queue size: " + pollingQueue.size() ); while( bAlive ) { try { checkNewConnections(); if( DEBUG ) { dump(); } // get next task try { currentCheck = ( PollCheck )pollingQueue.first(); pollingQueue.remove( currentCheck ); retryLevel = currentCheck.attempt; currentTime = System.currentTimeMillis(); // if there's nothing scheduled for the next 30 ms, go to sleep if( ( waitTime = currentCheck.timestamp - currentTime ) > 30 ) { try { // Log.debug( "PollMan: Sleeping for " + waitTime + " (-30)" ); status = SLEEP; sleep( waitTime-30 ); currentTime = System.currentTimeMillis(); } catch( InterruptedException ex ) { Log.debug( "PollMan(): Sleeep interrupted" ); } } status = PROC; if( currentTime - currentCheck.timestamp > 1000 ) { // if we're more than a second behind, print a message Log.debug( "PollMan(): Behind by " + ( currentTime - currentCheck.timestamp ) + " ms for " + currentCheck.connection ); } // ok, process the task now if( currentCheck.connection.pollCheck( retryLevel ) ) { // new poll was sent, add a new check to the right level Log.debug( 6, "Adding a poll check for " + currentCheck.connection ); pollingQueue.add( new PollCheck( currentCheck.connection, currentTime + Config.POLL_VERIFY_DELAY * 1000, currentCheck.attempt + 1 ) ); } if( retryLevel == 0 ) { // first poll level always moves self themselves to next polling cycle // if we've received no data on this for at least X minutes, don't poll for X minutes (currently 10) if( currentCheck.connection.noResponses > Config.POLL_REDUCED ) { if( currentCheck.reduced ) currentCheck.timestamp += Config.POLL_PERIOD * Config.POLL_REDUCED * 1000; else { // first time, add to random spot in next 1-11 minutes for even distribution currentCheck.reduced = true; currentCheck.timestamp += random.nextInt( Config.POLL_PERIOD * Config.POLL_REDUCED * 1000 ) + Config.POLL_PERIOD * 1000; } Log.debug( 3, "PollMan: Added reduced poll (" + ( ( currentCheck.timestamp - currentTime ) / 1000 ) + ") for: " + currentCheck.connection ); } else { // otherwise poll next minute currentCheck.timestamp += Config.POLL_PERIOD * 1000; Log.debug( 6, "Copying primary poll to next minute: " + currentCheck.connection ); } pollingQueue.add( currentCheck ); } } catch( NoSuchElementException ex ) { Log.err( "PollMan.run(): Attempted to poll empty queue! RetryLvl=" + retryLevel + " - " + ex ); } retryLevel = 0; } catch( Exception ex ) { Log.err( "Uncaught exception in PollManager.run()! " + ex ); ex.printStackTrace(); } } } public void kill() { bAlive = false; } /** * Outputs a representation of the query queues */ public void dump() { System.out.println("DUMP is not implemented for new treeset"); if( true )return; LinkedList list; HashMap map = new HashMap(); Iterator iter; PollCheck pc = null; for( int i = 0; i < checks.length; i++ ) { list = checks[i]; if( list != null && list.size() > 0 ) { map.clear(); System.out.print( "A" + i + ":" ); for( iter = list.iterator(); iter.hasNext(); ) { pc = ( PollCheck )iter.next(); map.put( new Integer( pc.secsFromNow() ), pc ); } for( int sec = 0; sec < 100; sec++ ) { pc = ( PollCheck )map.get( new Integer( sec ) ); if( pc != null ) { System.out.print( pc.connection.getChar() ); } else { System.out.print( "." ); } } System.out.println(); } } } }