Commit d4dac92e d4dac92e8288a20590aa79dc310879bfc9f7e014 by Christian Gerdes

Ändrat mappningen mellan servlets och uri så att de ligger under en

context root OraMonREST/ istället. Döpt om den till getData samt skapat
en som heter getMetrics. Lagt in thread safety på getData och testat med
10 paralella anrop utan att det nu går fel. getMetrics returnerar samma
set av data som javamon vu:n fast i json format. Ändrat OraMon.jsp så
den visar vettig status om monitorerna, som antal anrop, svarstid, ålder
på datat. getData tar nu även en age parameter som kontrollerar
cachning. Därmed kan man alltid anropa denna innan getMetrics utan att
det blir onödiga db anrop. Testat mot Oracle XE 11.2.
1 parent cfa10ffe
1 <?xml version="1.0" encoding="UTF-8"?> 1 <?xml version="1.0" encoding="UTF-8"?>
2 <classpath> 2 <classpath>
3 <classpathentry kind="src" path="src"/> 3 <classpathentry kind="src" path="src"/>
4 <classpathentry combineaccessrules="false" kind="src" path="/LILOM Library"/>
4 <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"> 5 <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
5 <attributes> 6 <attributes>
6 <attribute name="owner.project.facets" value="java"/> 7 <attribute name="owner.project.facets" value="java"/>
......
...@@ -37,13 +37,30 @@ if(request.getParameter("action") != null) { ...@@ -37,13 +37,30 @@ if(request.getParameter("action") != null) {
37 <h2>Status</h2> 37 <h2>Status</h2>
38 38
39 <p>Number of monitors: <%= monList.size() %></p> 39 <p>Number of monitors: <%= monList.size() %></p>
40 <% for (OraMon mon : Registry.getList()) { %> 40
41 <div style="background-color: LightSteelBlue; padding: 10px;"> 41 <div style="background-color: LightSteelBlue; padding: 10px;">
42 <table> 42 <table>
43 <tr><td>Connection String</td><td><%= mon.getConString() %></td></tr> 43 <tr>
44 <th>Connection String</th>
45 <th>Data Calls</th>
46 <th>Success</th>
47 <th>Failed</th>
48 <th>Age</th>
49 <th>RT</th>
50 </tr>
51 <% for (OraMon mon : Registry.getList()) { %>
52 <tr>
53 <td><%= mon.getConString() %></td>
54 <td><%= mon.getDataCalled() %></td>
55 <td><%= mon.getDataSucceeded() %></td>
56 <td><%= mon.getDataFailed() %></td>
57 <td><%= mon.getAgeTs() %>s</td>
58 <td><%= mon.getLastRTms() %>ms</td>
59 </tr>
60 <% } %>
44 </table> 61 </table>
45 </div> 62 </div>
46 <% } %> 63
47 64
48 <h2>Administration</h2> 65 <h2>Administration</h2>
49 66
......
1 /readme.txt 1 /OraMonRESTgetData.class
2 /OraMonREST.class 2 /OraMonRESTgetMetrics.class
......
1 1
2 2
3 import java.io.IOException; 3 import java.io.IOException;
4 import java.io.PrintWriter;
5 4
6 import javax.servlet.ServletException; 5 import javax.servlet.ServletException;
7 import javax.servlet.annotation.WebServlet; 6 import javax.servlet.annotation.WebServlet;
...@@ -9,19 +8,20 @@ import javax.servlet.http.HttpServlet; ...@@ -9,19 +8,20 @@ import javax.servlet.http.HttpServlet;
9 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse; 9 import javax.servlet.http.HttpServletResponse;
11 10
11 import se.lil.om.OraMon;
12 import se.lil.om.Registry; 12 import se.lil.om.Registry;
13 13
14 /** 14 /**
15 * Servlet implementation class OraMonREST 15 * Servlet implementation class OraMonREST
16 */ 16 */
17 @WebServlet(description = "REST API for OraMon", urlPatterns = { "/OraMonREST" }) 17 @WebServlet(description = "REST API for OraMon", urlPatterns = { "/OraMonREST/getData" })
18 public class OraMonREST extends HttpServlet { 18 public class OraMonRESTgetData extends HttpServlet {
19 private static final long serialVersionUID = 1L; 19 private static final long serialVersionUID = 1L;
20 20
21 /** 21 /**
22 * @see HttpServlet#HttpServlet() 22 * @see HttpServlet#HttpServlet()
23 */ 23 */
24 public OraMonREST() { 24 public OraMonRESTgetData() {
25 super(); 25 super();
26 // TODO Auto-generated constructor stub 26 // TODO Auto-generated constructor stub
27 } 27 }
...@@ -31,27 +31,62 @@ public class OraMonREST extends HttpServlet { ...@@ -31,27 +31,62 @@ public class OraMonREST extends HttpServlet {
31 */ 31 */
32 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 32 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
33 response.setContentType("application/json"); 33 response.setContentType("application/json");
34 StringBuffer sb = new StringBuffer(); 34 try {
35 if(request.getParameterMap().containsKey("connectionString")) { 35 StringBuffer sb = new StringBuffer();
36 // We have a connection string, find the monitor or create it and call getData() on the monitor 36 int age = 0;
37 // Check that we have a valid connection string 37 if(request.getParameterMap().containsKey("age")) {
38 String[] conStrParamArray = request.getParameter("connectionString").split(":"); 38 try {
39 if(conStrParamArray.length != 5) { 39 age = Integer.parseInt(request.getParameter("age"));
40 // Error, we need 3 parts in the con string 40 } catch (Exception e) {
41 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "connectionString needs to be in format host:port:sid:username:password"); 41 response.setStatus(400);
42 return; 42 response.getWriter().println("{\"error\":true,\"msg\":\"The specified age parameter is not a valid integer number\"}");
43 return;
44 }
43 } 45 }
44 String conStr = "jdbc:oracle:thin:@" + conStrParamArray[0] + ":" + conStrParamArray[1] + ":" + conStrParamArray[2];
45 String userStr = conStrParamArray[3];
46 String passStr = conStrParamArray[4];
47 46
48 } else { 47 if(request.getParameterMap().containsKey("connectionString")) {
49 // No input, just return the list of monitors 48 // We have a connection string, find the monitor or create it and call getData() on the monitor
50 sb.append("{\"count\":" + Registry.getList().size() + "}"); 49
50 // Check that we have a valid connection string
51 String[] conStrParamArray = request.getParameter("connectionString").split(":");
52 if(conStrParamArray.length != 5) {
53 // Error, we need 5 parts in the connection string
54 response.setStatus(400);
55 response.getWriter().println("{\"error\":true,\"msg\":\"connectionString needs to be in format host:port:sid:username:password\"}");
56 return;
57 }
58 String conStr = "jdbc:oracle:thin:@" + conStrParamArray[0] + ":" + conStrParamArray[1] + ":" + conStrParamArray[2];
59 String usrStr = conStrParamArray[3];
60 String pwdStr = conStrParamArray[4];
61
62 // Try to find the monitor in our list
63 OraMon monitor = Registry.findOrCreate(conStr, usrStr, pwdStr);
64
65 // Call getData()
66 boolean didUpdate = monitor.getData(age);
67 String dbName = monitor.getDBName();
68 if(didUpdate) {
69 sb.append("{\"error\":false,\"msg\":\"Sucessfully collected data on instance '" + dbName + "'\"");
70 sb.append(",\"ms\":"+monitor.getLastRTms()+"}");
71 } else {
72 response.setStatus(HttpServletResponse.SC_ACCEPTED);
73 sb.append("{\"error\":false");
74 sb.append(",\"msg\":\"Data does not need to be updated on instance '" + dbName + "' either because another thread did concurrently update the monitor and we just waited for it to complete, or because the age (if) specified was higher than the monitors data age.\"");
75 sb.append(",\"age\":"+monitor.getAgeTs()+"}");
76 }
77
78 } else {
79 // No input, just return the list of monitors
80 sb.append("{\"count\":" + Registry.getList().size() + "}");
81 }
82
83 response.getWriter().println(sb.toString());
84
85 } catch (Throwable e) {
86 response.setStatus(500);
87 response.getWriter().println("{\"error\":true,\"msg\":\""+e.toString().replace("\n"," ").trim()+"\"}");
88 e.printStackTrace();
51 } 89 }
52
53 PrintWriter out = response.getWriter();
54 out.println(sb.toString());
55 } 90 }
56 91
57 /** 92 /**
......
1
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5
6 import javax.servlet.ServletException;
7 import javax.servlet.annotation.WebServlet;
8 import javax.servlet.http.HttpServlet;
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11
12 import se.lil.om.OraMon;
13 import se.lil.om.Registry;
14
15 /**
16 * Servlet implementation class OraMonRESTgetMetrics
17 */
18 @WebServlet(description = "Gets a default set of metrics from an existing monitor", urlPatterns = { "/OraMonREST/getMetrics" })
19 public class OraMonRESTgetMetrics extends HttpServlet {
20 private static final long serialVersionUID = 1L;
21
22 /**
23 * @see HttpServlet#HttpServlet()
24 */
25 public OraMonRESTgetMetrics() {
26 super();
27 // TODO Auto-generated constructor stub
28 }
29
30 /**
31 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
32 */
33 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
34 response.setContentType("application/json");
35 try {
36 StringBuffer sb = new StringBuffer();
37 ArrayList<OraMon> list = Registry.getList();
38
39 if(request.getParameterMap().containsKey("connectionString")) {
40 // We have a connection string, find the monitor and report only on that monitor
41 // Check that we have a valid connection string
42 String[] conStrParamArray = request.getParameter("connectionString").split(":");
43 if(conStrParamArray.length < 3) {
44 // Error, we need at least 3 parts in the connection string
45 response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
46 response.getWriter().println("{\"error\":true,\"msg\":\"connectionString needs to be in format host:port:sid\"}");
47 return;
48 }
49 String conStr = "jdbc:oracle:thin:@" + conStrParamArray[0] + ":" + conStrParamArray[1] + ":" + conStrParamArray[2];
50
51 // Try to find the monitor in our list
52 OraMon monitor = null;
53 for (OraMon item : Registry.getList()) {
54 if(item.getConString().equals(conStr)) {
55 monitor = item;
56 }
57 }
58
59 // If not found return error
60 if(monitor == null) {
61 // Not found, create it
62 response.setStatus(HttpServletResponse.SC_NOT_FOUND);
63 response.getWriter().println("{\"error\":true,\"msg\":\"Can not find a monitor with connection string "+conStr+"\"}");
64 return;
65 }
66
67 // Set this monitor to the only one on the list
68 list = new ArrayList<OraMon>();
69 list.add(monitor);
70 }
71
72 // Loop over all monitors and output a Json array with the default set of metrics
73 sb.append("{\"error\":false,\"nvarray\":");
74 String aStr = "";
75 sb.append("[");
76 for (OraMon item : list) {
77 if (list.size() > 1) aStr = " " + item.getDBName();
78 sb.append("{\"name\":\"Cpus (#)" + aStr + "\",\"value\":" + item.getNumberOfCPUs() + "}");
79 sb.append(",{\"name\":\"Cpu Time (ms/s)" + aStr + "\",\"value\":" + item.getCPUTimePerSecond() + "}");
80 sb.append(",{\"name\":\"Cpu Usage (%)" + aStr + "\",\"value\":" + item.getCPUPercent() + "}");
81 sb.append(",{\"name\":\"Logical Reads (#/s)" + aStr + "\",\"value\":" + item.getLogicalReadsPerSecond() + "}");
82 sb.append(",{\"name\":\"Consistent Gets (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("consistent gets") + "}");
83 sb.append(",{\"name\":\"DB Block Gets (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("db block gets") + "}");
84 sb.append(",{\"name\":\"Cache Hit Ratio (%)" + aStr + "\",\"value\":" + item.getCacheHitRatioPercent() + "}");
85 sb.append(",{\"name\":\"Buffer Cache Hit Ratio (%)" + aStr + "\",\"value\":" + item.getBufferCacheHitRatioPercent() + "}");
86 sb.append(",{\"name\":\"DB Block Changes (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("db block changes") + "}");
87 sb.append(",{\"name\":\"Redo Size (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("redo size") + "}");
88 sb.append(",{\"name\":\"Physical Reads (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("physical reads") + "}");
89 sb.append(",{\"name\":\"Physical Writes (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("physical writes") + "}");
90 sb.append(",{\"name\":\"Redo Writes (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("redo writes") + "}");
91 sb.append(",{\"name\":\"Non-idle Wait Time (ms/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("non-idle wait time") + "}");
92 sb.append(",{\"name\":\"File I/O Wait Time (ms/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("file io wait time") + "}");
93 sb.append(",{\"name\":\"Executes (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("execute count") + "}");
94 sb.append(",{\"name\":\"User Calls (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("user calls") + "}");
95 sb.append(",{\"name\":\"User Commits (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("user commits") + "}");
96 sb.append(",{\"name\":\"User Rollbacks (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("user rollbacks") + "}");
97 sb.append(",{\"name\":\"Parse Count Total (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("parse count (total)") + "}");
98 sb.append(",{\"name\":\"Parse Count Hard (#/s)" + aStr + "\",\"value\":" + item.getPerSecondValue("parse count (hard)") + "}");
99 }
100 sb.append("]}");
101
102 response.getWriter().println(sb.toString());
103
104 } catch (Throwable e) {
105 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
106 response.getWriter().println("{\"error\":true,\"msg\":\""+e.toString().replace("\n"," ").trim()+"\"}");
107 e.printStackTrace();
108 }
109 }
110
111 }
1 /Collector.class
2 /LongDelta.class
3 /OraMon.class
4 /Registry.class
5 /TestRunner.class
1 package se.lil.om; 1 package se.lil.om;
2 2
3 import java.sql.*; 3 import java.sql.*;
4 import java.util.Properties;
5 import java.util.concurrent.locks.Lock;
6 import java.util.concurrent.locks.ReentrantLock;
4 7
5 public class OraMon { 8 public class OraMon {
6 Connection conn = null; 9 Connection conn = null;
...@@ -11,6 +14,21 @@ public class OraMon { ...@@ -11,6 +14,21 @@ public class OraMon {
11 14
12 Collector col = new Collector(); 15 Collector col = new Collector();
13 16
17 int getDataCalls = 0;
18 int getDataSucess = 0;
19
20 public int getDataCalled() {
21 return getDataCalls;
22 }
23
24 public int getDataSucceeded() {
25 return getDataSucess;
26 }
27
28 public int getDataFailed() {
29 return getDataCalls - getDataSucess;
30 }
31
14 public String getConString() { 32 public String getConString() {
15 return this.conString; 33 return this.conString;
16 } 34 }
...@@ -74,30 +92,86 @@ public class OraMon { ...@@ -74,30 +92,86 @@ public class OraMon {
74 return getPerSecondValue("consistent gets") + getPerSecondValue("db block gets"); 92 return getPerSecondValue("consistent gets") + getPerSecondValue("db block gets");
75 } 93 }
76 94
77 public void getData() throws Throwable { 95 long lastRTns = 0;
78 Statement stmt = conn.createStatement(); 96 public long getLastRTms() {
79 ResultSet rset = null; 97 return lastRTns / (1000 * 1000);
80 98 }
81 //Get the database name once 99
82 if(this.dbName == null) { 100 long lastFetchTSns = 0;
83 rset = stmt.executeQuery("select value from V$SYSTEM_PARAMETER where name = 'db_name'"); 101 public boolean getData(long ageTs) throws Throwable {
102 if ((System.nanoTime() - lastFetchTSns) > (ageTs * 1000 * 1000 * 1000)) return getData();
103 else return false;
104 }
105
106 public long getAgeTs() {
107 return (System.nanoTime() - lastFetchTSns)/(1000*1000*1000);
108 }
109
110 Lock lock = new ReentrantLock();
111
112 public boolean getData() throws Throwable {
113 // Start thread safe
114 // Try to get a lock
115 boolean haveLock = lock.tryLock();
116 if( haveLock == false) {
117 // We could not get the lock, someone else is updating. Wait for it to complete by waiting for a lock, then unlock and return.
118 try {
119 lock.lock();
120 } finally {
121 lock.unlock();
122 }
123 return false;
124 }
125 // 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.
126 try {
127 long startTSns = System.nanoTime();
128 getDataCalls++;
129 if(conn == null) open();
130 if(conn.isClosed()) open();
131 boolean stale = true;
132 try {
133 if (conn.isValid(20)) {
134 stale = false;
135 }
136 } catch (SQLException e) {
137 stale = true;
138 }
139 if(stale) {
140 open();
141 }
142 Statement stmt = conn.createStatement();
143 ResultSet rset = null;
144
145 //Get the database name once
146 if(this.dbName == null) {
147 rset = stmt.executeQuery("select value from V$SYSTEM_PARAMETER where name = 'db_name'");
148 rset.next();
149 this.dbName = rset.getString(1);
150 rset.close();
151 }
152
153 //Get values for CPU calculation
154 rset = stmt.executeQuery("select value from V$OSSTAT where STAT_NAME = 'NUM_CPUS'");
84 rset.next(); 155 rset.next();
85 this.dbName = rset.getString(1); 156 this.numCpus = rset.getInt(1);
86 rset.close(); 157 rset.close();
158
159 cpuTime.update(stmt.executeQuery("select systimestamp, value from V$SYS_TIME_MODEL where stat_name='DB CPU'"), true);
160
161 // Get the entire V_$SYSSTAT table from DB
162 col.update(stmt.executeQuery("select systimestamp, name, value from V$SYSSTAT"));
163
164 stmt.close();
165 getDataSucess++;
166 lastFetchTSns = System.nanoTime();
167 lastRTns = lastFetchTSns - startTSns;
168 return true;
169 } catch (Throwable e) {
170 throw (e);
171 } finally {
172 lock.unlock();
87 } 173 }
88 174 // End thread safe
89 //Get values for CPU calculation
90 rset = stmt.executeQuery("select value from V$OSSTAT where STAT_NAME = 'NUM_CPUS'");
91 rset.next();
92 this.numCpus = rset.getInt(1);
93 rset.close();
94
95 cpuTime.update(stmt.executeQuery("select systimestamp, value from V$SYS_TIME_MODEL where stat_name='DB CPU'"), true);
96
97 // Get the entire V_$SYSSTAT table from DB
98 col.update(stmt.executeQuery("select systimestamp, name, value from V$SYSSTAT"));
99
100 stmt.close();
101 } 175 }
102 176
103 public OraMon() { 177 public OraMon() {
...@@ -112,7 +186,12 @@ public class OraMon { ...@@ -112,7 +186,12 @@ public class OraMon {
112 186
113 public void open() throws Throwable { 187 public void open() throws Throwable {
114 Class.forName ("oracle.jdbc.OracleDriver"); 188 Class.forName ("oracle.jdbc.OracleDriver");
115 this.conn = DriverManager.getConnection(conString, conUser, conPass); 189 Properties jdbcProps = new Properties();
190 jdbcProps.put("user", conUser);
191 jdbcProps.put("password", conPass);
192 jdbcProps.put("v$session.program","LIL Oracle Monitor");
193 this.conn = DriverManager.getConnection(conString, jdbcProps);
194
116 } 195 }
117 196
118 public void open(String con, String user, String pass) throws Throwable { 197 public void open(String con, String user, String pass) throws Throwable {
...@@ -120,7 +199,11 @@ public class OraMon { ...@@ -120,7 +199,11 @@ public class OraMon {
120 if(con != null) this.conString = con; 199 if(con != null) this.conString = con;
121 if(user != null) this.conUser = user; 200 if(user != null) this.conUser = user;
122 if(pass != null) this.conPass = pass; 201 if(pass != null) this.conPass = pass;
123 this.conn = DriverManager.getConnection(conString, conUser, conPass); 202 Properties jdbcProps = new Properties();
203 jdbcProps.put("user", conUser);
204 jdbcProps.put("password", conPass);
205 jdbcProps.put("v$session.program","LIL Oracle Monitor");
206 this.conn = DriverManager.getConnection(conString, jdbcProps);
124 } 207 }
125 208
126 public void close() throws Throwable { 209 public void close() throws Throwable {
......
...@@ -4,11 +4,24 @@ import java.util.ArrayList; ...@@ -4,11 +4,24 @@ import java.util.ArrayList;
4 4
5 public class Registry { 5 public class Registry {
6 private static ArrayList<OraMon> oraList = null; 6 private static ArrayList<OraMon> oraList = null;
7 public static boolean test = true;
7 8
8 public static ArrayList<OraMon> getList() { 9 public static synchronized ArrayList<OraMon> getList() {
9 if (oraList == null) { 10 if (oraList == null) {
10 oraList = new ArrayList<OraMon>(); 11 oraList = new ArrayList<OraMon>();
11 } 12 }
12 return oraList; 13 return oraList;
13 } 14 }
15
16 public static synchronized OraMon findOrCreate(String conStr, String usrStr, String pwdStr) {
17 for (OraMon item : getList()) {
18 if(item.getConString().equals(conStr)) {
19 return item;
20 }
21 }
22 // Not found, create it
23 OraMon monitor = new OraMon(conStr, usrStr, pwdStr);
24 oraList.add(monitor);
25 return monitor;
26 }
14 } 27 }
......