1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.geronimo.monitoring.jmx; 18 19 import java.util.ArrayList; 20 import java.util.HashMap; 21 import java.util.HashSet; 22 import java.util.Iterator; 23 import java.util.Set; 24 import java.util.TreeMap; 25 26 import javax.management.Attribute; 27 import javax.management.MBeanServer; 28 import javax.management.MBeanServerFactory; 29 import javax.management.ObjectName; 30 import javax.management.j2ee.statistics.RangeStatistic; 31 import javax.management.j2ee.statistics.Stats; 32 import javax.management.j2ee.statistics.CountStatistic; 33 import javax.management.j2ee.statistics.Statistic; 34 import javax.management.j2ee.statistics.TimeStatistic; 35 import javax.naming.InitialContext; 36 import javax.sql.DataSource; 37 38 import org.slf4j.Logger; 39 import org.slf4j.LoggerFactory; 40 41 42 import org.apache.geronimo.gbean.GBeanInfo; 43 import org.apache.geronimo.gbean.GBeanInfoBuilder; 44 import org.apache.geronimo.gbean.GBeanLifecycle; 45 46 import org.apache.geronimo.monitoring.MBeanHelper; 47 import org.apache.geronimo.monitoring.MonitorConstants; 48 49 import org.apache.geronimo.monitoring.jmx.snapshot.SnapshotThread; 50 import org.apache.geronimo.monitoring.snapshot.SnapshotConfigXMLBuilder; 51 import org.apache.geronimo.monitoring.snapshot.SnapshotDBHelper; 52 53 /** 54 * This is the GBean that will be the bottleneck for the communication 55 * between the management node and the data in the server node. 56 */ 57 public class MasterRemoteControlJMX implements GBeanLifecycle { 58 private static final Logger log = LoggerFactory.getLogger(MasterRemoteControlJMX.class); 59 60 // mbean server to talk to other components 61 private static MBeanServer mbServer = null; 62 63 // threads 64 private static SnapshotThread snapshotThread = null; 65 66 // datasources 67 private DataSource activeDS; 68 private DataSource archiveDS; 69 70 private SnapshotDBHelper snapshotDBHelper; 71 72 public MasterRemoteControlJMX() { 73 // retrieve the mbean server 74 ArrayList mbServerList = MBeanServerFactory.findMBeanServer(null); 75 if(mbServerList.size() >= 1) { 76 mbServer = (MBeanServer) mbServerList.get(0); 77 for(int i = 0; i < mbServerList.size(); i++) { 78 String domain = ((MBeanServer)mbServerList.get(i)).getDefaultDomain(); 79 if(domain.equals( MonitorConstants.GERONIMO_DEFAULT_DOMAIN )) { 80 mbServer = (MBeanServer)mbServerList.get(i); 81 break; 82 } 83 } 84 } 85 // ensure that the mbServer has something in it 86 if(mbServer == null) { 87 mbServer = MBeanServerFactory.createMBeanServer( MonitorConstants.GERONIMO_DEFAULT_DOMAIN ); 88 } 89 90 // set up SnaphotDBHelper with the necessary data sources 91 // Note: do not put this in the constructor...datasources are not injected by then 92 try { 93 InitialContext ic = new InitialContext(); 94 activeDS = (DataSource)ic.lookup("jca:/org.apache.geronimo.plugins.monitoring/agent-ds/JCAManagedConnectionFactory/jdbc/ActiveDS"); 95 archiveDS = (DataSource)ic.lookup("jca:/org.apache.geronimo.plugins.monitoring/agent-ds/JCAManagedConnectionFactory/jdbc/ArchiveDS"); 96 } catch(Exception e) { 97 log.error(e.getMessage()); 98 } 99 snapshotDBHelper = new SnapshotDBHelper(activeDS, archiveDS); 100 } 101 102 /** 103 * Looks up the JSR-77 statistics associated with this object name. 104 * 105 * @param objectName 106 * @return HashMap 107 * @throws Exception 108 */ 109 public static HashMap getStats(String objectName) throws Exception { 110 HashMap statsMap = new HashMap(); 111 Stats stats = (Stats)mbServer.getAttribute(new ObjectName(objectName), "stats"); 112 String[] sttsName = stats.getStatisticNames(); 113 Statistic[] stts = stats.getStatistics(); 114 for(int i = 0; i < sttsName.length; i++) { 115 Statistic aStat = stats.getStatistic(sttsName[i]); 116 if(aStat instanceof RangeStatistic) { 117 Long current = new Long(((RangeStatistic)aStat).getCurrent()); 118 Long high = new Long(((RangeStatistic)aStat).getHighWaterMark()); 119 Long low = new Long(((RangeStatistic)aStat).getLowWaterMark()); 120 statsMap.put(stts[i].getName() + " Current", current); 121 statsMap.put(stts[i].getName() + " Max", high); 122 statsMap.put(stts[i].getName() + " Min", low); 123 } else if(aStat instanceof CountStatistic) { 124 Long current = new Long(((CountStatistic)aStat).getCount()); 125 statsMap.put(stts[i].getName(), current); 126 } else if(aStat instanceof TimeStatistic) { 127 Long current = new Long(((TimeStatistic)aStat).getCount()); 128 Long max = new Long(((TimeStatistic)aStat).getMaxTime()); 129 Long min = new Long(((TimeStatistic)aStat).getMinTime()); 130 Long total = new Long(((TimeStatistic)aStat).getTotalTime()); 131 statsMap.put(stts[i].getName() + " CurrentTime", current); 132 statsMap.put(stts[i].getName() + " MaxTime", max); 133 statsMap.put(stts[i].getName() + " MinTime", min); 134 statsMap.put(stts[i].getName() + " TotalTime", total); 135 } else { 136 // this should never happen 137 throw new Exception(); 138 } 139 } 140 return statsMap; 141 } 142 143 /** 144 * Changes the objectName's attrName's value to attrValue 145 * 146 * @param objectName 147 * @param attrName 148 * @param attrValue 149 * @throws Exception 150 */ 151 public void setAttribute(String objectName, String attrName, Object attrValue) throws Exception { 152 Attribute attr = new Attribute(attrName, attrValue); 153 mbServer.setAttribute(new ObjectName(objectName), attr); 154 } 155 156 /** 157 * Stops the snapshot thread 158 */ 159 public boolean stopSnapshot() { 160 if(snapshotThread != null) { 161 if(snapshotThread.getSnapshotDuration() != Long.MAX_VALUE) { 162 saveDuration(snapshotThread.getSnapshotDuration()); 163 snapshotThread.setSnapshotDuration(Long.MAX_VALUE); 164 log.info("Snapshot thread stopped."); 165 return true; 166 } else { 167 return false; 168 } 169 } else { 170 log.error("There is not a snapshot thread running. Stopping aborted."); 171 return false; 172 } 173 } 174 175 /** 176 * Fetches the data stored from the snapshot thread and returns 177 * it in a ArrayList with each element being a HashMap of the attribute 178 * mapping to the statistic. All stats will be the average of 179 * 1 - n, n+1 - 2n, ..., cn+1 - c(n+1) 180 * 181 * Grabs 'numberOfSnapshots' snapshots. Grabs one snapshot per 182 * 'everyNthsnapshot' 183 * 184 * @param numberOfSnapshot 185 * @param everyNthSnapshot 186 * @return ArrayList 187 */ 188 public ArrayList<HashMap<String, HashMap<String, Object>>> fetchSnapshotData(Integer numberOfSnapshot, Integer everyNthSnapshot) { 189 return snapshotDBHelper.fetchData(numberOfSnapshot, everyNthSnapshot); 190 } 191 192 /** 193 * Fetches the max amount for each statistic stored from the snapshot thread 194 * and returns it in a HashMap 195 * 196 * @param numberOfSnapshot 197 * @return HashMap 198 */ 199 public HashMap<String, HashMap<String, Long>> fetchMaxSnapshotData(Integer numberOfSnapshot) { 200 return snapshotDBHelper.fetchMaxSnapshotData(numberOfSnapshot); 201 } 202 203 /** 204 * Fetches the min amount for each statistic stored from the snapshot thread 205 * and returns it in a HashMap 206 * 207 * @param numberOfSnapshot 208 * @return HashMap 209 */ 210 public HashMap<String, HashMap<String, Long>> fetchMinSnapshotData(Integer numberOfSnapshot) { 211 return snapshotDBHelper.fetchMinSnapshotData(numberOfSnapshot); 212 } 213 214 /** 215 * Gets the elapsed time in milliseconds between each snapshot. 216 * 217 * @return Long 218 */ 219 public Long getSnapshotDuration() { 220 try { 221 return Long.parseLong(SnapshotConfigXMLBuilder.getAttributeValue( MonitorConstants.DURATION )); 222 } catch(Exception e) { 223 return new Long( MonitorConstants.DEFAULT_DURATION ); 224 } 225 } 226 227 /** 228 * Sets the elapsed time in milliseconds between each snapshot. 229 * 230 * @param snapshotDuration 231 */ 232 public void setSnapshotDuration(Long snapshotDuration) { 233 if(snapshotThread != null) { 234 snapshotThread.setSnapshotDuration(snapshotDuration.longValue()); 235 saveDuration(snapshotThread.getSnapshotDuration()); 236 } else { 237 log.warn("There is not a snapshot thread instantiated."); 238 } 239 } 240 241 public void setSnapshotRetention(Integer retention) { 242 saveRetention(retention.intValue()); 243 } 244 245 /** 246 * Begins the snapshot process given the time interval between snapshots 247 * 248 * Precondition: 249 * interval is given in milli seconds 250 * 251 * @param interval 252 */ 253 public boolean startSnapshot(Long interval) { 254 // get the saved/default retention period 255 String retentionStr = null; 256 try { 257 retentionStr = SnapshotConfigXMLBuilder.getAttributeValue( MonitorConstants.RETENTION ); 258 } catch(Exception e){ 259 // happens when there is not an instance of "retention" in the config 260 // which is okay. 261 } 262 int retention; 263 if(retentionStr == null) { 264 retention = MonitorConstants.DEFAULT_RETENTION; 265 } else { 266 retention = Integer.parseInt(retentionStr); 267 } 268 return startSnapshot(interval, new Integer(retention)); 269 } 270 271 /** 272 * Begins the snapshot process given the time interval between snapshots 273 * 274 * Precondition: 275 * interval is given in milli seconds 276 * 277 * @param interval 278 */ 279 public boolean startSnapshot(Long interval, Integer retention) { 280 if((snapshotThread == null || (snapshotThread != null && (snapshotThread.SnapshotStatus() == 0))) && interval.longValue() > 0) { 281 saveDuration(interval.longValue()); 282 saveRetention(retention.intValue()); 283 snapshotThread = new SnapshotThread(interval.longValue(), mbServer); 284 snapshotThread.start(); 285 log.info("Snapshot thread successfully created."); 286 return true; 287 } else { 288 log.warn("There is already a snapshot thread running."); 289 return false; 290 } 291 } 292 293 public Long getSnapshotCount() { 294 return snapshotDBHelper.getSnapshotCount(); 295 } 296 297 /** 298 * Fetches all mbean names that provide JSR-77 statistics 299 * 300 * @return A set containing all mbean names of mbeans that provide 301 * statistics 302 */ 303 public Set<String> getStatisticsProviderMBeanNames() { 304 return (Set<String>)MBeanHelper.getStatsProvidersMBeans( getAllMBeanNames() ); 305 } 306 307 /** 308 * Fetches all mbean names 309 * 310 * @return A set containing all mbean names 311 */ 312 public Set<String> getAllMBeanNames() { 313 try { 314 Set<ObjectName> names = (Set<ObjectName>)mbServer.queryNames(null, null); 315 Set<String> strNames = new HashSet<String>(); 316 for(Iterator<ObjectName> it = names.iterator(); it.hasNext(); ) { 317 strNames.add(it.next().getCanonicalName()); 318 } 319 return strNames; 320 } catch(Exception e) { 321 log.error(e.getMessage(), e); 322 return new HashSet<String>(); 323 } 324 } 325 326 public void doFail() { 327 doStop(); 328 } 329 330 /** 331 * Executes when the GBean starts up. Also starts the snapshot thread. 332 */ 333 public void doStart() { 334 335 } 336 337 /** 338 * Executes when the GBean stops. Also stops the snapshot thread. 339 */ 340 public void doStop() { 341 if(SnapshotStatus() == 1) { 342 stopSnapshot(); 343 } 344 } 345 346 private void saveDuration(long duration) { 347 SnapshotConfigXMLBuilder.saveDuration(duration); 348 } 349 350 private void saveRetention(int retention) { 351 SnapshotConfigXMLBuilder.saveRetention(retention); 352 } 353 354 /** 355 * Adds a record of the mbean via its name to take snapshots of. As a result 356 * the mbeanName will be written to snapshot-config.xml 357 * 358 * @param mbeanName 359 */ 360 public boolean addMBeanForSnapshot(String mbeanName) { 361 return SnapshotConfigXMLBuilder.addMBeanName(mbeanName); 362 } 363 364 /** 365 * Removes a record of the mbean via its name to take snapshots of. As a result 366 * the mbeanName will be removed from snapshot-config.xml 367 * 368 * @param mbeanName 369 */ 370 public boolean removeMBeanForSnapshot(String mbeanName) { 371 return SnapshotConfigXMLBuilder.removeMBeanName(mbeanName); 372 } 373 374 /** 375 * @return A map: mbeanName --> ArrayList of statistic attributes for that mbean 376 */ 377 public HashMap<String, ArrayList<String>> getAllSnapshotStatAttributes() { 378 HashMap<String, ArrayList<String>> snapshotAttributes = new HashMap<String, ArrayList<String>>(); 379 Set<String> mbeans = getTrackedMBeans(); 380 // for each mbean name 381 for(Iterator<String> it = mbeans.iterator(); it.hasNext(); ) { 382 ArrayList<String> mbeanStatsList = new ArrayList<String>(); 383 String mbeanName = it.next(); 384 try { 385 Stats stats = (Stats)mbServer.getAttribute(new ObjectName(mbeanName), "stats"); 386 String[] sttsName = stats.getStatisticNames(); 387 Statistic[] stts = stats.getStatistics(); 388 for(int i = 0; i < sttsName.length; i++) { 389 Statistic aStat = stats.getStatistic(sttsName[i]); 390 if(aStat instanceof RangeStatistic) { 391 mbeanStatsList.add(stts[i].getName() + " Current"); 392 mbeanStatsList.add(stts[i].getName() + " Max"); 393 mbeanStatsList.add(stts[i].getName() + " Min"); 394 } else if(aStat instanceof CountStatistic) { 395 mbeanStatsList.add(stts[i].getName()); 396 } else if(aStat instanceof TimeStatistic) { 397 mbeanStatsList.add(stts[i].getName() + " CurrentTime"); 398 mbeanStatsList.add(stts[i].getName() + " MaxTime"); 399 mbeanStatsList.add(stts[i].getName() + " MinTime"); 400 mbeanStatsList.add(stts[i].getName() + " TotalTime"); 401 } else { 402 // for the time being, only numbers should be returned 403 } 404 } 405 } catch (Exception e) { 406 log.error(e.getMessage(), e); 407 } 408 // save attributes to the returning list 409 snapshotAttributes.put(mbeanName, mbeanStatsList); 410 } 411 return snapshotAttributes; 412 } 413 414 public Set<String> getTrackedMBeans() { 415 ArrayList<String> mbeans = (ArrayList<String>)SnapshotConfigXMLBuilder.getMBeanNames(); 416 Set<String> set = new HashSet<String>(); 417 for(int i = 0; i < mbeans.size(); i++) { 418 set.add(mbeans.get(i)); 419 } 420 return set; 421 } 422 423 public Integer getSnapshotRetention() { 424 try { 425 return new Integer(SnapshotConfigXMLBuilder.getAttributeValue( MonitorConstants.RETENTION )); 426 } catch(Exception e) { 427 return new Integer(MonitorConstants.DEFAULT_RETENTION); // the default 428 } 429 } 430 431 /** 432 * @param name - object name of the mbean 433 * @param operationName - method within the class 434 * @param params - parameters for the method 435 * @param signature - types for the parameters 436 * @return Invokes the method of a class defined. 437 */ 438 public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) throws Exception { 439 return mbServer.invoke(name, operationName, params, signature); 440 } 441 442 /** 443 * @param mbeanName 444 * @param statsName 445 * @param numberOfSnapshots 446 * @param everyNthSnapshot 447 * @return HashMap which maps from a snapshot_time --> value of the mbean.statsName at that time 448 */ 449 public TreeMap<Long, Long> getSpecificStatistics( String mbeanName, 450 String statsName, 451 Integer numberOfSnapshots, 452 Integer everyNthSnapshot, 453 Boolean showArchived) { 454 return snapshotDBHelper.getSpecificStatistics(mbeanName, statsName, numberOfSnapshots.intValue(), everyNthSnapshot.intValue(), showArchived); 455 } 456 457 /** 458 * @return Returns true if snapshot is running. 459 */ 460 public Integer SnapshotStatus() { 461 // TODO: check if the snapshot thread is running 462 if(snapshotThread == null) { 463 return 0; 464 } else { 465 return snapshotThread.SnapshotStatus(); 466 } 467 } 468 469 public static final GBeanInfo GBEAN_INFO; 470 471 static { 472 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic("MasterRemoteControlJMX", MasterRemoteControlJMX.class); 473 infoFactory.addOperation("getStats", new Class[] {String.class}, "HashMap"); 474 infoFactory.addOperation("setAttribute", new Class[] {String.class, String.class, Object.class}, "void"); 475 infoFactory.addOperation("startSnapshot", new Class[] {Long.class}, "Boolean"); 476 infoFactory.addOperation("startSnapshot", new Class[] {Long.class, Integer.class}, "Boolean"); 477 infoFactory.addOperation("stopSnapshot", new Class[] {}, "Boolean"); 478 infoFactory.addOperation("fetchSnapshotData", new Class[] {Integer.class, Integer.class}, "ArrayList"); 479 infoFactory.addOperation("fetchMaxSnapshotData", new Class[] {Integer.class}, "HashMap"); 480 infoFactory.addOperation("fetchMinSnapshotData", new Class[] {Integer.class}, "HashMap"); 481 infoFactory.addOperation("getSnapshotDuration", new Class[] {}, "Long"); 482 infoFactory.addOperation("getSnapshotCount", new Class[] {}, "Long"); 483 infoFactory.addOperation("setSnapshotDuration", new Class[] {Long.class}, "void"); 484 infoFactory.addOperation("getStatisticsProviderMBeanNames", new Class[] {}, "Set"); 485 infoFactory.addOperation("getAllMBeanNames", new Class[] {}, "Set"); 486 infoFactory.addOperation("getAllSnapshotStatAttributes", new Class[] {}, "HashMap"); 487 infoFactory.addOperation("addMBeanForSnapshot", new Class[] {String.class}, "void"); 488 infoFactory.addOperation("removeMBeanForSnapshot", new Class[] {String.class}, "void"); 489 infoFactory.addOperation("getSnapshotRetention", new Class[] {}, "Integer"); 490 infoFactory.addOperation("setSnapshotRetention", new Class[] {Integer.class}, "void"); 491 infoFactory.addOperation("SnapshotStatus", new Class[] {}, "Integer"); 492 infoFactory.addOperation("getSpecificStatistics", new Class[] {String.class, String.class, Integer.class, Integer.class, Boolean.class}, "TreeMap"); 493 infoFactory.addOperation("getTrackedMBeans", new Class[] {}, "Set"); 494 infoFactory.setConstructor(new String[] {}); 495 GBEAN_INFO = infoFactory.getBeanInfo(); 496 } 497 498 public static GBeanInfo getGBeanInfo() { 499 return GBEAN_INFO; 500 } 501 }