OraMon 1.1 med JmxMon
Showing
12 changed files
with
200 additions
and
16 deletions
... | @@ -2,6 +2,7 @@ | ... | @@ -2,6 +2,7 @@ |
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 combineaccessrules="false" kind="src" path="/LILOM Library"/> |
5 | <classpathentry combineaccessrules="false" kind="src" path="/LILJM Library"/> | ||
5 | <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"> | 6 | <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"> |
6 | <attributes> | 7 | <attributes> |
7 | <attribute name="owner.project.facets" value="java"/> | 8 | <attribute name="owner.project.facets" value="java"/> |
... | @@ -12,6 +13,5 @@ | ... | @@ -12,6 +13,5 @@ |
12 | <classpathentry kind="lib" path="lib/javax.servlet.jar"/> | 13 | <classpathentry kind="lib" path="lib/javax.servlet.jar"/> |
13 | <classpathentry kind="lib" path="lib/javax.servlet.jsp.jar"/> | 14 | <classpathentry kind="lib" path="lib/javax.servlet.jsp.jar"/> |
14 | <classpathentry kind="lib" path="lib/javax.servlet.jsp.jstl.jar"/> | 15 | <classpathentry kind="lib" path="lib/javax.servlet.jsp.jstl.jar"/> |
15 | <classpathentry combineaccessrules="false" kind="src" path="/LILJM Library"/> | ||
16 | <classpathentry kind="output" path="build/classes"/> | 16 | <classpathentry kind="output" path="build/classes"/> |
17 | </classpath> | 17 | </classpath> | ... | ... |
1 | <%@page import="se.lil.om.Registry"%> | ||
2 | <%@page import="se.lil.om.OraMon"%> | 1 | <%@page import="se.lil.om.OraMon"%> |
3 | <%@page import="java.util.ArrayList"%> | 2 | <%@page import="se.lil.jm.JmxMon"%> |
4 | <%@page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> | 3 | <%@page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> |
5 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> | 4 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
6 | <html> | 5 | <html> |
... | @@ -17,7 +16,7 @@ String errMsg = ""; | ... | @@ -17,7 +16,7 @@ String errMsg = ""; |
17 | if(request.getParameter("action") != null) { | 16 | if(request.getParameter("action") != null) { |
18 | if(request.getParameter("action").equals("add")) { | 17 | if(request.getParameter("action").equals("add")) { |
19 | if(request.getParameter("data") != null && request.getParameter("data").length() != 0) { | 18 | if(request.getParameter("data") != null && request.getParameter("data").length() != 0) { |
20 | Registry.getList().add(new OraMon()); | 19 | //se.lil.om.Registry.getList().add(new OraMon()); |
21 | } else { | 20 | } else { |
22 | error = true; | 21 | error = true; |
23 | errMsg = "Data is empty. Nothing added."; | 22 | errMsg = "Data is empty. Nothing added."; |
... | @@ -26,34 +25,36 @@ if(request.getParameter("action") != null) { | ... | @@ -26,34 +25,36 @@ if(request.getParameter("action") != null) { |
26 | } | 25 | } |
27 | %> | 26 | %> |
28 | 27 | ||
29 | <% ArrayList<OraMon> monList = Registry.getList(); %> | ||
30 | |||
31 | <% if(error) { %> | 28 | <% if(error) { %> |
32 | <div style="background: Salmon; padding: 10px;">Error: <%= errMsg %></div> | 29 | <div style="background: Salmon; padding: 10px;">Error: <%= errMsg %></div> |
33 | <% } %> | 30 | <% } %> |
34 | 31 | ||
35 | <h1>OraMon Web 1.0</h1> | 32 | <h1>OraMon Web 1.1 with JmxMon</h1> |
36 | 33 | ||
37 | <h2>Status</h2> | 34 | <h2>Status Oracle Monitors</h2> |
38 | 35 | ||
39 | <p>Number of monitors: <%= monList.size() %></p> | 36 | <p>Number of monitors: <%= se.lil.om.Registry.getList().size() %></p> |
40 | 37 | ||
41 | <div style="background-color: LightSteelBlue; padding: 10px;"> | 38 | <div style="background-color: LightSteelBlue; padding: 10px;"> |
42 | <table> | 39 | <table> |
43 | <tr> | 40 | <tr> |
41 | <th>Name</th> | ||
44 | <th>Connection String</th> | 42 | <th>Connection String</th> |
45 | <th>Data Calls</th> | 43 | <th>Data Calls</th> |
46 | <th>Success</th> | 44 | <th>Success</th> |
47 | <th>Failed</th> | 45 | <th>Failed</th> |
46 | <th>Last Exception</th> | ||
48 | <th>Age</th> | 47 | <th>Age</th> |
49 | <th>RT</th> | 48 | <th>RT</th> |
50 | </tr> | 49 | </tr> |
51 | <% for (OraMon mon : Registry.getList()) { %> | 50 | <% for (OraMon mon : se.lil.om.Registry.getList()) { %> |
52 | <tr> | 51 | <tr> |
52 | <td><%= mon.getDBName() %></td> | ||
53 | <td><%= mon.getConString() %></td> | 53 | <td><%= mon.getConString() %></td> |
54 | <td><%= mon.getDataCalled() %></td> | 54 | <td><%= mon.getDataCalled() %></td> |
55 | <td><%= mon.getDataSucceeded() %></td> | 55 | <td><%= mon.getDataSucceeded() %></td> |
56 | <td><%= mon.getDataFailed() %></td> | 56 | <td><%= mon.getDataFailed() %></td> |
57 | <td><%= mon.getLastEx() %></td> | ||
57 | <td><%= mon.getAgeTs() %>s</td> | 58 | <td><%= mon.getAgeTs() %>s</td> |
58 | <td><%= mon.getLastRTms() %>ms</td> | 59 | <td><%= mon.getLastRTms() %>ms</td> |
59 | </tr> | 60 | </tr> |
... | @@ -61,7 +62,38 @@ if(request.getParameter("action") != null) { | ... | @@ -61,7 +62,38 @@ if(request.getParameter("action") != null) { |
61 | </table> | 62 | </table> |
62 | </div> | 63 | </div> |
63 | 64 | ||
65 | <h2>Status JMX Monitors</h2> | ||
66 | |||
67 | <p>Number of monitors: <%= se.lil.jm.Registry.getList().size() %></p> | ||
64 | 68 | ||
69 | <div style="background-color: LightSteelBlue; padding: 10px;"> | ||
70 | <table> | ||
71 | <tr> | ||
72 | <th>Name</th> | ||
73 | <th>Connection String</th> | ||
74 | <th>Data Calls</th> | ||
75 | <th>Success</th> | ||
76 | <th>Failed</th> | ||
77 | <th>Last Exception</th> | ||
78 | <th>Age</th> | ||
79 | <th>RT</th> | ||
80 | </tr> | ||
81 | <% for (JmxMon mon : se.lil.jm.Registry.getList()) { %> | ||
82 | <tr> | ||
83 | <td><%= mon.getServerName() %></td> | ||
84 | <td><%= mon.getConString() %></td> | ||
85 | <td><%= mon.getDataCalled() %></td> | ||
86 | <td><%= mon.getDataSucceeded() %></td> | ||
87 | <td><%= mon.getDataFailed() %></td> | ||
88 | <td><%= mon.getLastEx() %></td> | ||
89 | <td><%= mon.getAgeTs() %>s</td> | ||
90 | <td><%= mon.getLastRTms() %>ms</td> | ||
91 | </tr> | ||
92 | <% } %> | ||
93 | </table> | ||
94 | </div> | ||
95 | |||
96 | <!-- | ||
65 | <h2>Administration</h2> | 97 | <h2>Administration</h2> |
66 | 98 | ||
67 | <div title="Add Monitor" style="background: WhiteSmoke; padding: 10px;"> | 99 | <div title="Add Monitor" style="background: WhiteSmoke; padding: 10px;"> |
... | @@ -72,5 +104,7 @@ if(request.getParameter("action") != null) { | ... | @@ -72,5 +104,7 @@ if(request.getParameter("action") != null) { |
72 | <input type="submit" name="action" value="add"> | 104 | <input type="submit" name="action" value="add"> |
73 | </form> | 105 | </form> |
74 | </div> | 106 | </div> |
107 | --> | ||
108 | |||
75 | </body> | 109 | </body> |
76 | </html> | 110 | </html> |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
... | @@ -83,7 +83,13 @@ public class JmxMonRESTgetData extends HttpServlet { | ... | @@ -83,7 +83,13 @@ public class JmxMonRESTgetData extends HttpServlet { |
83 | 83 | ||
84 | } catch (Throwable e) { | 84 | } catch (Throwable e) { |
85 | response.setStatus(500); | 85 | response.setStatus(500); |
86 | response.getWriter().println("{\"error\":true,\"msg\":\""+e.toString().replace("\n"," ").replace("\"","\\\"").trim()+"\"}"); | 86 | Throwable lastCause = e.getCause(); |
87 | Throwable thisCause = e; | ||
88 | while(lastCause != null) { thisCause = lastCause; lastCause = thisCause.getCause(); } | ||
89 | //response.getWriter().println("{\"error\":true,\"msg\":\""+e.toString().replace("\n"," ").replace("\"","\\\"").trim()+"\"}"); | ||
90 | String rst = "{\"error\":true,\"msg\":\"" + e.toString() + ". Caused by: " + thisCause.toString() + "\"}"; | ||
91 | rst.replace("\n"," ").replace("\"","\\\"").trim(); | ||
92 | response.getWriter().println(rst); | ||
87 | e.printStackTrace(); | 93 | e.printStackTrace(); |
88 | } | 94 | } |
89 | } | 95 | } | ... | ... |
JavaMonWeb/src/JmxMonRESTgetMetrics.java
0 → 100644
1 | |||
2 | |||
3 | import java.io.IOException; | ||
4 | |||
5 | import javax.management.remote.JMXServiceURL; | ||
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.jm.JmxMon; | ||
13 | import se.lil.jm.Registry; | ||
14 | |||
15 | /** | ||
16 | * Servlet implementation class JmxMonRESTgetMetrics | ||
17 | */ | ||
18 | @WebServlet(description = "Gets JMX Monitors metrics", urlPatterns = { "/JmxMonREST/getMetrics" }) | ||
19 | public class JmxMonRESTgetMetrics extends HttpServlet { | ||
20 | private static final long serialVersionUID = 1L; | ||
21 | |||
22 | /** | ||
23 | * @see HttpServlet#HttpServlet() | ||
24 | */ | ||
25 | public JmxMonRESTgetMetrics() { | ||
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 | JmxMon mon = null; | ||
38 | |||
39 | if(request.getParameterMap().containsKey("connectionString")) { | ||
40 | // Check that we have a valid connection string | ||
41 | JMXServiceURL url; | ||
42 | try { | ||
43 | url = new JMXServiceURL(request.getParameter("connectionString")); | ||
44 | } catch (Exception e) { | ||
45 | // Error, we need a correct connection string | ||
46 | response.setStatus(400); | ||
47 | response.getWriter().println("{\"error\":true,\"msg\":\"connectionString is not a correct JMX Service URL\"}"); | ||
48 | return; | ||
49 | } | ||
50 | |||
51 | // Try to find the monitor in our list | ||
52 | for (JmxMon item : Registry.getList()) { | ||
53 | if(item.getConString().equals(url.toString())) { | ||
54 | mon = item; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | // If not found return error | ||
59 | if(mon == null) { | ||
60 | // Not found, create it | ||
61 | response.setStatus(HttpServletResponse.SC_NOT_FOUND); | ||
62 | response.getWriter().println("{\"error\":true,\"msg\":\"Can not find a monitor with connection string "+url.toString()+"\"}"); | ||
63 | return; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | // Loop over all monitors and output a Json array with the default set of metrics | ||
68 | sb.append("{\"error\":false,\"nvarray\":"); | ||
69 | sb.append("["); | ||
70 | |||
71 | sb.append(",{\"name\":\"Number of processors #\",\"value\":" + mon.getNumberOfCPUs() + "}"); | ||
72 | sb.append(",{\"name\":\"Process CPU Used %\",\"value\":" + mon.getCPUPercent() + "}"); | ||
73 | sb.append(",{\"name\":\"Process Threads #\",\"value\":" + mon.getThreads() + "}"); | ||
74 | sb.append(",{\"name\":\"Process Loaded Classes #\",\"value\":" + mon.getLoadedClassCount() + "}"); | ||
75 | sb.append(",{\"name\":\"Process File Descriptors %\",\"value\":" + mon.getSystemFileDescriptorsPercentUsed() + "}"); | ||
76 | sb.append(",{\"name\":\"Process Heap Committed MB\",\"value\":" + mon.getHeapCommittedMB() + "}"); | ||
77 | sb.append(",{\"name\":\"Process Heap Used MB\",\"value\":" + mon.getHeapUsedMB() + "}"); | ||
78 | sb.append(",{\"name\":\"Process Heap Used %\",\"value\":" + mon.getHeapUsedPercent() + "}"); | ||
79 | sb.append(",{\"name\":\"OldGen Used MB\",\"value\":" + mon.getOldGenUsedMB() + "}"); | ||
80 | sb.append(",{\"name\":\"OldGen Used %\",\"value\":" + mon.getOldGenUsedPercent() + "}"); | ||
81 | sb.append(",{\"name\":\"OldGen After Last GC MB\",\"value\":" + mon.getOldGenAfterGCMB() + "}"); | ||
82 | sb.append(",{\"name\":\"PermGen After Last GC MB\",\"value\":" + mon.getPermGenAfterGCMB() + "}"); | ||
83 | sb.append(",{\"name\":\"GC Time (Overhead) %\",\"value\":" + mon.getGCOverhead() + "}"); | ||
84 | sb.append(",{\"name\":\"System Load (per cpu)\",\"value\":" + mon.getAverageLoadPerCpu() + "}"); | ||
85 | sb.append(",{\"name\":\"System Load (total)\",\"value\":" + mon.getAverageLoad() + "}"); | ||
86 | sb.append(",{\"name\":\"System Memory %\",\"value\":" + mon.getSystemMemoryPercentUsed() + "}"); | ||
87 | sb.append(",{\"name\":\"System Swap %\",\"value\":" + mon.getSystemSwapPercentUsed() + "}"); | ||
88 | |||
89 | sb.append("]}"); | ||
90 | |||
91 | response.getWriter().println(sb.toString()); | ||
92 | |||
93 | } catch (Throwable e) { | ||
94 | response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); | ||
95 | response.getWriter().println("{\"error\":true,\"msg\":\""+e.toString().replace("\n"," ").trim()+"\"}"); | ||
96 | e.printStackTrace(); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | } |
... | @@ -237,11 +237,11 @@ public class JmxMon { | ... | @@ -237,11 +237,11 @@ public class JmxMon { |
237 | } | 237 | } |
238 | 238 | ||
239 | public long getAgeTs() { | 239 | public long getAgeTs() { |
240 | if(lastFetchTSns > 0) return (System.nanoTime() - lastFetchTSns)/(1000*1000*1000); | 240 | return (System.nanoTime() - lastFetchTSns)/(1000*1000*1000); |
241 | else return 0; | ||
242 | } | 241 | } |
243 | 242 | ||
244 | Lock lock = new ReentrantLock(); | 243 | Lock lock = new ReentrantLock(); |
244 | Throwable lastEx = null; | ||
245 | 245 | ||
246 | public boolean getData() throws Throwable { | 246 | public boolean getData() throws Throwable { |
247 | // Start thread safe | 247 | // Start thread safe |
... | @@ -260,8 +260,32 @@ public class JmxMon { | ... | @@ -260,8 +260,32 @@ public class JmxMon { |
260 | try { | 260 | try { |
261 | long startTSns = System.nanoTime(); | 261 | long startTSns = System.nanoTime(); |
262 | getDataCalls++; | 262 | getDataCalls++; |
263 | // Check that we have a valid connection | ||
264 | if (jmxc == null || mbsc == null) open(); | ||
265 | // Check if connection is valid | ||
266 | boolean stale = false; | ||
267 | try { | ||
268 | mbsc.getDefaultDomain(); | ||
269 | } catch (Exception e) { | ||
270 | stale = true; | ||
271 | } | ||
272 | if(stale) { | ||
273 | // Try reconnect | ||
274 | try { | ||
275 | jmxc.close(); | ||
276 | } catch (Exception e) {} | ||
277 | jmxc = null; | ||
278 | mbsc = null; | ||
279 | open(); | ||
280 | mbsc.getDefaultDomain(); // This will throw an exception if we cant communicate with the server and abort this call | ||
281 | } | ||
263 | // Do the update of data | 282 | // Do the update of data |
264 | numCpus = (Integer)getAttributeValue("java.lang:type=OperatingSystem", "AvailableProcessors"); | 283 | Object val = getAttributeValue("java.lang:type=OperatingSystem", "AvailableProcessors"); |
284 | if(val == null) { | ||
285 | // Something is wrong with the connection, we cant get number of cpus. Abort | ||
286 | throw new Exception("Bad JMX Connection: AvailableProcessors not found in java.lang:type=OperatingSystem MBean. JMX Connection String: " + this.url.toString()); | ||
287 | } | ||
288 | numCpus = (Integer)val; | ||
265 | fetchUpdateAttribute("java.lang:type=OperatingSystem", "ProcessCpuTime"); | 289 | fetchUpdateAttribute("java.lang:type=OperatingSystem", "ProcessCpuTime"); |
266 | fetchUpdateAttribute("java.lang:type=OperatingSystem", "OpenFileDescriptorCount"); | 290 | fetchUpdateAttribute("java.lang:type=OperatingSystem", "OpenFileDescriptorCount"); |
267 | fetchUpdateAttribute("java.lang:type=OperatingSystem", "MaxFileDescriptorCount"); | 291 | fetchUpdateAttribute("java.lang:type=OperatingSystem", "MaxFileDescriptorCount"); |
... | @@ -299,6 +323,10 @@ public class JmxMon { | ... | @@ -299,6 +323,10 @@ public class JmxMon { |
299 | } | 323 | } |
300 | return true; | 324 | return true; |
301 | } catch (Throwable e) { | 325 | } catch (Throwable e) { |
326 | Throwable lastCause = e.getCause(); | ||
327 | Throwable thisCause = e; | ||
328 | while(lastCause != null) { thisCause = lastCause; lastCause = thisCause.getCause(); } | ||
329 | lastEx = thisCause; | ||
302 | throw (e); | 330 | throw (e); |
303 | } finally { | 331 | } finally { |
304 | lock.unlock(); | 332 | lock.unlock(); |
... | @@ -306,6 +334,10 @@ public class JmxMon { | ... | @@ -306,6 +334,10 @@ public class JmxMon { |
306 | // End thread safe | 334 | // End thread safe |
307 | } | 335 | } |
308 | 336 | ||
337 | public Throwable getLastEx() { | ||
338 | return lastEx; | ||
339 | } | ||
340 | |||
309 | //NUM_CPUS | 341 | //NUM_CPUS |
310 | int numCpus = 0; | 342 | int numCpus = 0; |
311 | public long getNumberOfCPUs() { | 343 | public long getNumberOfCPUs() { | ... | ... |
... | @@ -26,7 +26,7 @@ public class TestRunner { | ... | @@ -26,7 +26,7 @@ public class TestRunner { |
26 | //JmxMon mon1 = new JmxMon("service:jmx:rmi:///jndi/iiop://u02878.ht.kap.rsv.se:17040/weblogic.management.mbeanservers.runtime"); // 10.3.6.0.161018.2 PS | 26 | //JmxMon mon1 = new JmxMon("service:jmx:rmi:///jndi/iiop://u02878.ht.kap.rsv.se:17040/weblogic.management.mbeanservers.runtime"); // 10.3.6.0.161018.2 PS |
27 | //JmxMon mon1 = new JmxMon("service:jmx:rmi:///jndi/iiop://u01891.ef.kap.rsv.se:17020/weblogic.management.mbeanservers.runtime"); // 10.3.6.0.161018.2 CMS | 27 | //JmxMon mon1 = new JmxMon("service:jmx:rmi:///jndi/iiop://u01891.ef.kap.rsv.se:17020/weblogic.management.mbeanservers.runtime"); // 10.3.6.0.161018.2 CMS |
28 | //JmxMon mon1 = new JmxMon("service:jmx:rmi:///jndi/iiop://u30450:34502/weblogic.management.mbeanservers.runtime"); // CMS | 28 | //JmxMon mon1 = new JmxMon("service:jmx:rmi:///jndi/iiop://u30450:34502/weblogic.management.mbeanservers.runtime"); // CMS |
29 | mon1.open(); | 29 | //mon1.open(); |
30 | //mon2.open(); | 30 | //mon2.open(); |
31 | jmxList.add(mon1); | 31 | jmxList.add(mon1); |
32 | //jmxList.add(mon2); | 32 | //jmxList.add(mon2); | ... | ... |
... | @@ -93,7 +93,7 @@ public class OraMon { | ... | @@ -93,7 +93,7 @@ public class OraMon { |
93 | } | 93 | } |
94 | } | 94 | } |
95 | 95 | ||
96 | public String getDBName() throws Throwable { | 96 | public String getDBName() { |
97 | return this.dbName; | 97 | return this.dbName; |
98 | } | 98 | } |
99 | 99 | ||
... | @@ -178,6 +178,7 @@ public class OraMon { | ... | @@ -178,6 +178,7 @@ public class OraMon { |
178 | } | 178 | } |
179 | 179 | ||
180 | Lock lock = new ReentrantLock(); | 180 | Lock lock = new ReentrantLock(); |
181 | Throwable lastEx = null; | ||
181 | 182 | ||
182 | public boolean getData() throws Throwable { | 183 | public boolean getData() throws Throwable { |
183 | // Start thread safe | 184 | // Start thread safe |
... | @@ -248,12 +249,20 @@ public class OraMon { | ... | @@ -248,12 +249,20 @@ public class OraMon { |
248 | lastRTns = lastFetchTSns - startTSns; | 249 | lastRTns = lastFetchTSns - startTSns; |
249 | return true; | 250 | return true; |
250 | } catch (Throwable e) { | 251 | } catch (Throwable e) { |
252 | Throwable lastCause = e.getCause(); | ||
253 | Throwable thisCause = e; | ||
254 | while(lastCause != null) { thisCause = lastCause; lastCause = thisCause.getCause(); } | ||
255 | lastEx = thisCause; | ||
251 | throw (e); | 256 | throw (e); |
252 | } finally { | 257 | } finally { |
253 | lock.unlock(); | 258 | lock.unlock(); |
254 | } | 259 | } |
255 | // End thread safe | 260 | // End thread safe |
256 | } | 261 | } |
262 | |||
263 | public Throwable getLastEx() { | ||
264 | return lastEx; | ||
265 | } | ||
257 | 266 | ||
258 | public OraMon() { | 267 | public OraMon() { |
259 | // TODO Auto-generated constructor stub | 268 | // TODO Auto-generated constructor stub | ... | ... |
-
Please register or sign in to post a comment