gt: 			throw new EofException();
context: package to.etc.dbreplay;
  
  import org.eclipse.jdt.annotation.Nullable;
  import to.etc.dbpool.ConnectionPool;
  import to.etc.dbpool.DbPoolUtil;
  import to.etc.dbpool.PoolManager;
  
  import java.io.BufferedInputStream;
  import java.io.BufferedOutputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.OutputStreamWriter;
  import java.io.PrintWriter;
  import java.text.DateFormat;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  
  /**
   * This utility will replay a database logfile, for performance evaluation purposes.
   *
   * @author <a href="mailto:jal@etc.to">Frits Jalvingh</a>
   * Created on Aug 31, 2011
   */
  public class DbReplay {
  	private File m_inputFile;
  
  	private File m_driverPath;
  
  	/** The buffered input file containing statements. */
  	private BufferedInputStream m_bis;
  
  	private long m_firstTime;
  
  	private long m_lastRecordTime;
  
  	private long m_startTime;
  
  	private long m_endTime;
  
  	private File m_poolFile;
  
  	private String m_poolId;
  
  	private ConnectionPool m_pool;
  
  	private String m_runSchema;
  
  	private String m_dbHost, m_dbSid, m_dbUser, m_dbPass;
  
  	/** The #of separate executor threads to start. */
  	private int m_executors = 20;
  
  	/** The #of executors that are actually running/ready. */
  	private int m_runningExecutors;
  
  	/** When set by -maxwait, this limits the max time to wait between statements, ignoring the time delta's in the log file. */
  	private long m_maxStatementDelay = Long.MAX_VALUE;
  
  	private XType m_runType;
  
  	private PrintWriter m_log;
  
  	private boolean m_stopped;
  
  	private IReplayer m_replayer;
  
  	private enum XType {
  		DUMP, RUN
  	}
  
  	private void run(String[] args) throws Exception {
  		if(!decodeOptions(args))
  			return;
  
  		try {
  			openSource();
  			switch(m_runType){
  				default:
  					throw new IllegalStateException(m_runType + ": unknown");
  
  				case DUMP:
  					runDump();
  					break;
  				case RUN:
  					runEmulation();
  					break;
  			}
  		} catch(Exception x) {
  			System.err.println("Error: " + x);
  			System.err.println("   -at record " + m_recordNumber + ", file offset " + m_fileOffset);
  			x.printStackTrace();
  		} finally {
  			releaseAll();
  		}
  	}
  
  	private void openLog() throws Exception {
  		File log = new File("dbreplay.log");
  		m_log = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(log), 65536), "utf-8"));
  		m_log.println("Log file start @" + new Date());
  	}
  
  	public void log(String s) {
  		if(m_log == null)
  			return;
  		m_log.println(s);
  	}
  
  	public boolean isLogging() {
  		return m_log != null;
  	}
  
  	public long getMaxStatementDelay() {
  		return m_maxStatementDelay;
  	}
  
  	private void runDump() throws Exception {
  		// TODO Auto-generated method stub
  
  	}
  
  
  	private void runEmulation() throws Exception {
  		initialize();
  
  		//-- Input distributor loop.
  		m_startTime = System.currentTimeMillis();
  		for(; ; ) {
  			ReplayRecord rr = ReplayRecord.readRecord(this);
  			if(null == rr)
  				break;
  			if(m_recordNumber == 0) {
  				m_firstTime = rr.getStatementTime();
  			}
  			m_lastRecordTime = rr.getStatementTime();
  			m_recordNumber++;
  
  			handleRecord(rr);
  		}
  		waitForIdle(60 * 1000);
  		synchronized(this) {
  			m_stopped = true;
  		}
  
  		m_endTime = System.currentTimeMillis();
  		System.out.println("Normal EOF after " + m_recordNumber + " records and " + m_fileOffset + " file bytes");
  		Date st = new Date(m_firstTime);
  		Date et = new Date(m_lastRecordTime);
  		DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  		System.out.println("  - input time from " + df.format(st) + " till " + df.format(et) + ", " + DbPoolUtil.strMillis(m_lastRecordTime - m_firstTime));
  		System.out.println("  - real time spent: " + DbPoolUtil.strMillis(m_endTime - m_startTime));
  	}
  
  
  	private boolean decodeOptions(String[] args) throws Exception {
  		int argc = 0;
  		while(argc < args.length) {
  			String s = args[argc++];
  			if(s.startsWith("-")) {
  				if("-pf".equals(s) || "-poolfile".equals(s)) {
  					if(argc >= args.length)
  						throw new IllegalArgumentException("Missing file name after -poolfile");
  					m_poolFile = new File(args[argc++]);
  					if(!m_poolFile.exists() || !m_poolFile.isFile())
  						throw new IllegalArgumentException(m_poolFile + ": file not found");
  				} else if("-dump".equals(s)) {
  					m_runType = XType.DUMP;
  				} else if("-schema".equals(s)) {
  					if(argc >= args.length)
  						throw new IllegalArgumentException("Missing name after -schema");
  					m_runSchema = args[argc++];
  				} else if("-db".equals(s)) {
  					if(argc >= args.length)
  						throw new IllegalArgumentException("Missing db string after -db");
  					decodeDb(args[argc++]);
  				} else if("-dp".equals(s) || "-driver".equals(s)) {
  					if(argc >= args.length)
  						throw new IllegalArgumentException("Missing driver path after " + s);
  					m_driverPath = new File(args[argc++]);
  					if(!m_driverPath.exists() || !m_driverPath.isFile())
  						throw new IllegalArgumentException(m_driverPath + ": invalid path (not a file or does not exist)");
  				} else if("-maxwait".equals(s)) {
  					if(argc >= args.length)
  						throw new IllegalArgumentException("Missing numeric value (milliseconds) after -maxwait");
  					m_maxStatementDelay = Long.parseLong(args[argc++]);
  				} else if("-log".equals(s)) {
  					openLog();
  				} else if("-speedy".equals(s)) {
  					m_replayer = new SpeedyReplayer();
  				} else if(m_replayer != null) {
  					argc = m_replayer.decodeArgs(s, args, argc);
  					if(-1 == argc) {
  						usage("Unknown option: " + s);
  						return false;
  					}
  				} else {
  					usage("Unknown option: " + s);
  					return false;
  				}
  			} else {
  				if(m_inputFile == null) {
  					m_inputFile = new File(s);
  					if(!m_inputFile.exists() || !m_inputFile.isFile())
  						throw new Exception(m_inputFile + ": file does not exist or is not a file.");
  				} else if(m_poolId == null) {
  					m_poolId = s;
  				} else {
  					usage("Unexpected extra argument on command line");
  					return false;
  				}
  			}
  		}
  
  		if(m_inputFile == null) {
  			usage("Missing input file name");
  			return false;
  		}
  		if(m_poolId == null && m_dbHost == null) {
  			usage("Missing pool ID or database (-db) specification");
  			return false;
  		}
  		if(m_runType == null)
  			m_runType = XType.RUN;
  		if(m_replayer == null)
  			m_replayer = new TimeBasedReplayer();
  		return true;
  	}
  
  	private void decodeDb(String s) throws Exception {
  		int pos = s.indexOf('@');
  		if(pos != -1) {
  			String a = s.substring(0, pos);
  			String b = s.substring(pos + 1);
  
  			pos = a.indexOf(':');
  			if(pos != -1) {
  				m_dbUser = a.substring(0, pos);
  				m_dbPass = a.substring(pos + 1);
  
  				pos = b.indexOf('/');
  				if(pos != -1) {
  					m_dbHost = b.substring(0, pos);
  					m_dbSid = b.substring(pos + 1);
  					return;
  				}
  			}
  		}
  		throw new RuntimeException("Bad DB string: format is user:password@host/sid");
  	}
  
  	private void usage(String msg) {
  		System.out.println("Error: " + msg);
  		System.out.println("Usage: DbReplay [options] filename poolID");
  		System.out.println("Options are:\n" //
  			+ "-poolfile|-pf [filename]: The name of the pool.properties defining the database connection.\n" //
  			+ "-schema [name]: set the 'current schema' before starting the tests (useful to run test logged in as a different user). For instance when running as a user 'TEST' when tables in schema VIEWPOINT are needed\n"
  			//
  			+ "-db [userid:password@host/sid]: shorthand to connect to this specific database.\n" //
  			+ "-driver|-dp [path]: path to the Oracle driver .jar file, if not present on the classpath\n" //
  			+ "\n** replay options **\n" //
  			+ "-maxwait [milliseconds]: set the max time to wait between successive statements to a #of milliseconds. This ignores the real times that statements were sent to the database.\n"
  			+ "-log: create a log of statements in dbreplay.log\n" //
  			+ "-speedy: run using the 'speedy' replayer\n" //
  			+ "\nSpeedy executor options:\n"
  			+ "-perwait n: schedule this many SQL commands per 'maxwait' period. Example: -maxwait 1 -perwait 10 will try to execute 10 SQL statements every millisecond\n"
  		);
  	}
  
  	private void releaseAll() {
  		try {
  			if(m_log != null)
  				m_log.close();
  		} catch(Exception x) {
  			System.err.println("Cannot close log: " + x);
  		} finally {
  			m_log = null;
  		}
  
  		try {
  			if(m_bis != null)
  				m_bis.close();
  			m_bis = null;
  		} catch(Exception x) {
  			System.err.println("term: cannot close input file: " + x);
  		}
  
  		try {
  			terminateAll();
  		} catch(Exception x) {
  			x.printStackTrace();
  		}
  	}
  
  	synchronized public ConnectionPool getPool() {
  		return m_pool;
  	}
  
  	synchronized public String getRunSchema() {
  		return m_runSchema;
  	}
  
  	private void openSource() throws Exception {
  		m_bis = new BufferedInputStream(new FileInputStream(m_inputFile), 65536);
  	}
  
  	private void initialize() throws Exception {
  		if(m_dbHost != null) {
  			//-- Use command line invocation
  			String url = "jdbc:oracle:thin:@" + m_dbHost + ":1521:" + m_dbSid.toUpperCase();
  			m_pool = PoolManager.getInstance().definePool("db", "oracle.jdbc.driver.OracleDriver", url, m_dbUser, m_dbPass, m_driverPath == null ? null : m_driverPath.toString(), null);
  		} else {
  			if(m_poolFile == null)
  				m_pool = PoolManager.getInstance().definePool(m_poolId);
  			else
  				m_pool = PoolManager.getInstance().definePool(m_poolFile, m_poolId);
  		}
  
  		startExecutors();
  		startStatusReporter();
  		waitForReady();
  	}
  
  	private synchronized boolean isStopped() {
  		return m_stopped;
  	}
  
  
  	/*--------------------------------------------------------------*/
  	/*	CODING:	Accessing the data stream.							*/
  	/*--------------------------------------------------------------*/
  
  	private long m_fileOffset;
  
  	private int m_recordNumber;
  
  	/**
  	 *
  	 * @param is
  	 * @return
  	 * @throws Exception
  	 */
  	@Nullable
  	public String readString() throws Exception {
  		int len = readInt();
  		if(len < 0)
  			return null;
  		byte[] data = new byte[len];
  		int szrd = m_bis.read(data);
  		if(szrd != len)
  			throw new IOException("Unexpected EOF: got " + szrd + " bytes but needed " + len);
  		m_fileOffset += len;
  		return new String(data, "utf-8");
  	}
  
  
  	public long readLong() throws Exception {
  		long a = (readInt() & 0xffffffffl);
  		long b = (readInt() & 0xffffffffl);
  		return (a << 32) | b;
  	}
  
  	public int readInt() throws Exception {
  		int v = m_bis.read();
  		if(v == -1)
  