OraMon.java 9.73 KB
package se.lil.om;

import java.sql.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class OraMon {
	Connection conn = null;
	String conString = "jdbc:oracle:thin:@//hostname:1521/SID";
	String conUser = "system";
	String conPass = "passw0rd";
	String dbName = null;
	
	Boolean isPDB = false;
	Boolean isCDB = false;
	Boolean hasContainers = null;
	OraMon containerDB = null;
	
	private Hashtable<String, OraMon> myPDBs = null;
	
	public static final int SYSSTAT = 1;
	public static final int OSSTAT = 2;
	
	Collector colSysStat = new Collector();
	Collector colOsStat = new Collector();
	
	int getDataCalls = 0;
	int getDataSucess = 0;
	
	public int getDataCalled() {
		return getDataCalls;
	}
	
	public int getDataSucceeded() {
		return getDataSucess;
	}
	
	public int getDataFailed() {
		return getDataCalls - getDataSucess;
	}
	
	public String getConString() {
		return this.conString;
	}
	
	public String getServiceName() {
		if(this.conString != null) {
			int last = this.conString.lastIndexOf('/');
			if(last >=0)
				return this.conString.substring(last +1);
			else {
				last = this.conString.lastIndexOf(':');
				return this.conString.substring(last +1);
			}
		} else {
			return null;
		}
	}
	
	//NUM_CPUS
	int numCpus = 0;
	public long getNumberOfCPUs() throws Throwable {
		return numCpus;
	}
	
	//DB CPU
	LongDelta cpuTime = new LongDelta(true);
	LongDelta niwTime = new LongDelta(true);
	public double getCPUPercent(boolean excludeNonIdleWaitTime) throws Throwable {
		double perSec = cpuTime.getPerSecondValue();
		if(excludeNonIdleWaitTime) {
			double newPerSec = perSec - (niwTime.getPerSecondValue() * 1000);
			if(newPerSec > 0) perSec = newPerSec; else perSec = 0;
		}
		double totSec = numCpus * 1000 * 1000;
		double percent = (perSec / totSec) * 100;
		if(percent > 100D) percent=100D;
		if(percent < 0D) percent=0D;
		return percent;
	}
	public double getCPUPercent() throws Throwable {
		return getCPUPercent(false);
	}
	public double getCPUAvailableSeconds() throws Throwable {
		return cpuTime.getSeconds()*numCpus;
	}
	public long getCPURawValue() throws Throwable {
		return cpuTime.getCurrentValue();
	}
	public long getCPURawDiff() {
		if(cpuTime != null && cpuTime.lastValue != null)
			return cpuTime.curValue - cpuTime.lastValue;
		else
			return 0;
	}
	
	public double getCPUTimePerSecond() throws Throwable {
		return cpuTime.getPerSecondValue();
	}
	
	public double getPerSecondValue(String name) throws Throwable {
		return getPerSecondValue(name, SYSSTAT);
	}
	
	public double getPerSecondValue(String name, int table) throws Throwable {
		switch (table) {
		case OSSTAT:
			return colOsStat.getPerSecValue(name);
		case SYSSTAT:
			return colSysStat.getPerSecValue(name);
		default:
			return colSysStat.getPerSecValue(name);
		}
	}
	
	public String getDBName() {
		return this.dbName;
	}
	
	public double getOsBusyPercent() throws Throwable {
		double idle = getPerSecondValue("IDLE_TIME", OSSTAT);
		double busy = getPerSecondValue("BUSY_TIME", OSSTAT);
		if(idle != 0 && busy != 0)
			return 100*(busy/(busy+idle));
		else {
			if(busy == 0)
				return 0;
			else
				return 100;
		}
	}
	
	// OS LOAD
	DoubleDelta osLoad = new DoubleDelta();
	
	public double getOsLoad() throws Throwable {
		return osLoad.getCurrentValue();
	}
	
	public double getOsLoadPerCPU() throws Throwable {
		double num = this.numCpus;
		double load = getOsLoad();
		return load / num;
	}
	
	public double getBufferCacheHitRatioPercent() throws Throwable {
		double conGetsCache = getPerSecondValue("consistent gets from cache");
		double dbBlocksCache = getPerSecondValue("db block gets from cache");
		double physReadsCache = getPerSecondValue("physical reads cache");
		if (conGetsCache + dbBlocksCache + physReadsCache == 0) {
			return 100;
		}
		if (conGetsCache + dbBlocksCache == 0 && physReadsCache > 0) {
			return 0;
		}
		double buffCacheHitRatio = 1 - (physReadsCache/(conGetsCache + dbBlocksCache));
		if(buffCacheHitRatio > 0)
			return buffCacheHitRatio * 100;
		else
			return 0;
		
	}
	
	public double getCacheHitRatioPercent() throws Throwable {
		double conGets = getPerSecondValue("consistent gets");
		double dbBlocks = getPerSecondValue("db block gets");
		double physReads = getPerSecondValue("physical reads");
		if (conGets + dbBlocks + physReads == 0) {
			return 100;
		}
		if (conGets + dbBlocks == 0 && physReads > 0) {
			return 0;
		}
		double cacheHitRatio = 1 - (physReads/(conGets + dbBlocks));
		if(cacheHitRatio > 0)
			return cacheHitRatio * 100;
		else
			return 0;
	}
	
	public double getLogicalReadsPerSecond() throws Throwable {
		return getPerSecondValue("consistent gets") + getPerSecondValue("db block gets");
	}
	
	long lastRTns = 0;
	public long getLastRTms() {
		return lastRTns / (1000 * 1000);
	}
	
	long lastFetchTSns = 0;
	
	public long getAgeTs() {
		return (System.nanoTime() - lastFetchTSns)/(1000*1000*1000);
	}
	
	public boolean getData() throws Throwable {
		return getData(0);
	}
	
	Lock lock = new ReentrantLock();
	Throwable lastEx = null;
	
 	public boolean getData(long ageTs) throws Throwable {
 		//Check age
 		if ((System.nanoTime() - lastFetchTSns) < (ageTs * 1000 * 1000 * 1000)) return false;
 		//Bail out if we are a PDB and ask the CDB OraMon object to update us
 		if(isPDB) {
 			if(containerDB != null) return containerDB.getData(ageTs);
 			// If we get here its strange. We know we are a PDB but we have no CDB object.
 			// Lets see if we ever end up here ;)
 		}
 		// Start thread safe
 		// Try to get a lock
 		boolean haveLock = lock.tryLock();
		if( haveLock == false) {
			// We could not get the lock, someone else is updating. Wait for it to complete by waiting for a lock, then unlock and return.
			try {
				lock.lock();
			} finally {
				lock.unlock();
			}
			return false;
		}
		// We do have the lock. Do the rest in a try catch everything and if we catch anything, re throw the catch but always release the lock.
		try {
	 		long startTSns = System.nanoTime();
	 		getDataCalls++;
	 		if(conn == null) open();
	 		if(conn.isClosed()) open();
	 		boolean stale = true;
	 		try {
	 			if (conn.isValid(20)) {
	 				stale = false;
	 			}
	 		} catch (SQLException e) {
	 			stale = true;
	 		}
	 		if(stale) {
	 			open();
	 		}
			Statement stmt = conn.createStatement();
			ResultSet rset = null;
			
			//Get the database name once // Currently always gets the CDB name
			if(this.dbName == null) {
				rset = stmt.executeQuery("select value from V$SYSTEM_PARAMETER where name = 'db_name'");
				rset.next();
				this.dbName = rset.getString(1);
				rset.close(); 
			}
			
			// Check if we can have containers
			if(hasContainers == null) {
				// We don't know, check if the CONTAINERS view exists in the database
				rset = stmt.executeQuery("select count(*) from SYS.ALL_VIEWS where VIEW_NAME = 'V_$CONTAINERS'");
				if(rset.next()) {
					if(rset.getInt(0) == 1) {
						// We found the containers view. Enable container handling
						hasContainers = true;
						if(getServiceName().equals(this.dbName)) isCDB = true;
					}
				}
			}
			
			// If we have containers, but we are not the CDB, then we are a PDB. 
			// At this point we should close the connections, and ask the CDB OraMon object to getData() instead, and update us.
			if(hasContainers && isCDB == false) {
				
			}
			
			//Get the entire V_$OSSTAT table from DB
			colOsStat.update(stmt.executeQuery("select systimestamp, stat_name, value from V$OSSTAT"));
			
			//Set numCpus
			this.numCpus = (int) colOsStat.getCurrentValue("NUM_CPUS");
			
			// Get OS Load
			osLoad.update(stmt.executeQuery("select systimestamp, value from V$OSSTAT where stat_name='LOAD'"));
			
			
			// SYSSTAT and SYS_TIME_MODEL are specific for PDB and CDB/Instance is the total.
			
			// Get DB CPU use time
			rset = stmt.executeQuery("select systimestamp, s.value as NIWT, t.value as DBCPU from V$SYSSTAT s, V$SYS_TIME_MODEL t where s.name='non-idle wait time' and t.STAT_NAME='DB CPU'");
			rset.next();
			niwTime.update(rset.getLong(2), rset.getTimestamp(1));
			cpuTime.update(rset.getLong(3), rset.getTimestamp(1));
			rset.close();
	
			//Get the entire V_$SYSSTAT table from DB
			colSysStat.update(stmt.executeQuery("select systimestamp, name, value from V$SYSSTAT"));
			
			stmt.close(); 
			getDataSucess++;
			lastFetchTSns = System.nanoTime();
			lastRTns = lastFetchTSns - startTSns;
			return true;
		} catch (Throwable e) {
			Throwable lastCause = e.getCause();
			Throwable thisCause = e;
			while(lastCause != null) { thisCause = lastCause; lastCause = thisCause.getCause(); }
			lastEx = thisCause;
			throw (e);
		} finally {
			lock.unlock();
		}
		// End thread safe
	}
 	
	public Throwable getLastEx() {
		return lastEx;
	}
	
	public OraMon() {
		// TODO Auto-generated constructor stub
	}
	
	public OraMon(String con, String user, String pass) {
		this.conString = con;
		this.conUser = user;
		this.conPass = pass;
	}
	
	public void open() throws Throwable {
		Class.forName ("oracle.jdbc.OracleDriver");
		Properties jdbcProps = new Properties();
		jdbcProps.put("user", conUser);
		jdbcProps.put("password", conPass);
		jdbcProps.put("v$session.program","LIL Oracle Monitor");
		this.conn = DriverManager.getConnection(conString, jdbcProps);
		
	}
	
	public void open(String con, String user, String pass) throws Throwable {
		Class.forName ("oracle.jdbc.OracleDriver");
		if(con != null) this.conString = con;
		if(user != null) this.conUser = user;
		if(pass != null) this.conPass = pass;
		Properties jdbcProps = new Properties();
		jdbcProps.put("user", conUser);
		jdbcProps.put("password", conPass);
		jdbcProps.put("v$session.program","LIL Oracle Monitor");
		this.conn = DriverManager.getConnection(conString, jdbcProps);
	}
	
	public void close() throws Throwable {
		if(conn != null) conn.close();
	}
}