001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.geronimo.monitoring; 018 019 import java.util.ArrayList; 020 import java.util.Collection; 021 import java.util.HashMap; 022 import java.util.HashSet; 023 import java.util.Iterator; 024 import java.util.Set; 025 import java.util.TreeMap; 026 027 import javax.management.Attribute; 028 import javax.management.MBeanServer; 029 import javax.management.MBeanServerFactory; 030 import javax.management.ObjectName; 031 import javax.management.j2ee.Management; 032 import javax.management.j2ee.ManagementHome; 033 import javax.management.j2ee.statistics.BoundedRangeStatistic; 034 import javax.management.j2ee.statistics.RangeStatistic; 035 import javax.management.j2ee.statistics.Stats; 036 import javax.management.j2ee.statistics.CountStatistic; 037 import javax.management.j2ee.statistics.Statistic; 038 import javax.management.j2ee.statistics.TimeStatistic; 039 import javax.naming.Context; 040 import javax.naming.InitialContext; 041 import javax.naming.NamingException; 042 import javax.sql.DataSource; 043 044 import org.apache.commons.logging.Log; 045 import org.apache.commons.logging.LogFactory; 046 047 048 import org.apache.geronimo.connector.outbound.ManagedConnectionFactoryWrapper; 049 import org.apache.geronimo.gbean.AbstractName; 050 import org.apache.geronimo.gbean.GBeanInfo; 051 import org.apache.geronimo.gbean.GBeanInfoBuilder; 052 import org.apache.geronimo.gbean.GBeanLifecycle; 053 054 import org.apache.geronimo.monitoring.MBeanHelper; 055 056 import org.apache.xbean.naming.context.WritableContext.NestedWritableContext; 057 058 import org.apache.geronimo.monitoring.snapshot.SnapshotThread; 059 import org.apache.geronimo.monitoring.snapshot.SnapshotConfigXMLBuilder; 060 import org.apache.geronimo.monitoring.snapshot.SnapshotDBHelper; 061 062 /** 063 * This is the GBean that will be the bottleneck for the communication 064 * between the management node and the data in the server node. 065 */ 066 public class MasterRemoteControlJMX implements GBeanLifecycle { 067 private static Log log = LogFactory.getLog(MasterRemoteControlJMX.class); 068 // constants 069 private static final String GERONIMO_DEFAULT_DOMAIN = "geronimo"; 070 private static final Long DEFAULT_DURATION = new Long(300000); 071 private static final int DEFAULT_RETENTION = 30; 072 private static final String RETENTION = "retention"; 073 private static final String DURATION = "duration"; 074 075 // mbean server to talk to other components 076 private static MBeanServer mbServer = null; 077 078 // threads 079 private static SnapshotThread snapshotThread = null; 080 081 // datasources 082 private DataSource activeDS; 083 private DataSource archiveDS; 084 085 private SnapshotDBHelper snapshotDBHelper; 086 087 public MasterRemoteControlJMX() { 088 // retrieve the mbean server 089 ArrayList mbServerList = MBeanServerFactory.findMBeanServer(null); 090 if(mbServerList.size() >= 1) { 091 mbServer = (MBeanServer) mbServerList.get(0); 092 for(int i = 0; i < mbServerList.size(); i++) { 093 String domain = ((MBeanServer)mbServerList.get(i)).getDefaultDomain(); 094 if(domain.equals(GERONIMO_DEFAULT_DOMAIN)) { 095 mbServer = (MBeanServer)mbServerList.get(i); 096 break; 097 } 098 } 099 } 100 // ensure that the mbServer has something in it 101 if(mbServer == null) { 102 mbServer = MBeanServerFactory.createMBeanServer(GERONIMO_DEFAULT_DOMAIN); 103 } 104 105 // set up SnaphotDBHelper with the necessary data sources 106 // Note: do not put this in the constructor...datasources are not injected by then 107 try { 108 InitialContext ic = new InitialContext(); 109 activeDS = (DataSource)ic.lookup("jca:/org.apache.geronimo.plugins/agent-ds/JCAManagedConnectionFactory/jdbc/ActiveDS"); 110 archiveDS = (DataSource)ic.lookup("jca:/org.apache.geronimo.plugins/agent-ds/JCAManagedConnectionFactory/jdbc/ArchiveDS"); 111 // activeDS = (DataSource)((NestedWritableContext)ic.lookup("jms:conn")).lookup("jdbc/ActiveDS"); 112 // archiveDS = (DataSource)((NestedWritableContext)ic.lookup("jms:conn")).lookup("jdbc/ArchiveDS"); 113 } catch(Exception e) { 114 log.error(e.getMessage()); 115 e.printStackTrace(); 116 } 117 snapshotDBHelper = new SnapshotDBHelper(activeDS, archiveDS); 118 } 119 120 /** 121 * Looks up the JSR-77 statistics associated with this object name. 122 * 123 * @param objectName 124 * @return HashMap 125 * @throws Exception 126 */ 127 public static HashMap getStats(String objectName) throws Exception { 128 HashMap statsMap = new HashMap(); 129 Stats stats = (Stats)mbServer.getAttribute(new ObjectName(objectName), "stats"); 130 String[] sttsName = stats.getStatisticNames(); 131 Statistic[] stts = stats.getStatistics(); 132 for(int i = 0; i < sttsName.length; i++) { 133 Statistic aStat = stats.getStatistic(sttsName[i]); 134 if(aStat instanceof RangeStatistic) { 135 Long current = new Long(((RangeStatistic)aStat).getCurrent()); 136 Long high = new Long(((RangeStatistic)aStat).getHighWaterMark()); 137 Long low = new Long(((RangeStatistic)aStat).getLowWaterMark()); 138 statsMap.put(stts[i].getName() + " Current", current); 139 statsMap.put(stts[i].getName() + " Max", high); 140 statsMap.put(stts[i].getName() + " Min", low); 141 } else if(aStat instanceof CountStatistic) { 142 Long current = new Long(((CountStatistic)aStat).getCount()); 143 statsMap.put(stts[i].getName(), current); 144 } else if(aStat instanceof TimeStatistic) { 145 Long current = new Long(((TimeStatistic)aStat).getCount()); 146 Long max = new Long(((TimeStatistic)aStat).getMaxTime()); 147 Long min = new Long(((TimeStatistic)aStat).getMinTime()); 148 Long total = new Long(((TimeStatistic)aStat).getTotalTime()); 149 statsMap.put(stts[i].getName() + " CurrentTime", current); 150 statsMap.put(stts[i].getName() + " MaxTime", max); 151 statsMap.put(stts[i].getName() + " MinTime", min); 152 statsMap.put(stts[i].getName() + " TotalTime", total); 153 } else { 154 // this should never happen 155 throw new Exception(); 156 } 157 } 158 return statsMap; 159 } 160 161 /** 162 * Changes the objectName's attrName's value to attrValue 163 * 164 * @param objectName 165 * @param attrName 166 * @param attrValue 167 * @throws Exception 168 */ 169 public void setAttribute(String objectName, String attrName, Object attrValue) throws Exception { 170 Attribute attr = new Attribute(attrName, attrValue); 171 mbServer.setAttribute(new ObjectName(objectName), attr); 172 } 173 174 /** 175 * Stops the snapshot thread 176 */ 177 public boolean stopSnapshot() { 178 if(snapshotThread != null) { 179 if(snapshotThread.getSnapshotDuration() != Long.MAX_VALUE) { 180 saveDuration(snapshotThread.getSnapshotDuration()); 181 snapshotThread.setSnapshotDuration(Long.MAX_VALUE); 182 log.info("Snapshot thread stopped."); 183 return true; 184 } else { 185 return false; 186 } 187 } else { 188 log.error("There is not a snapshot thread running. Stopping aborted."); 189 return false; 190 } 191 } 192 193 /** 194 * Fetches the data stored from the snapshot thread and returns 195 * it in a ArrayList with each element being a HashMap of the attribute 196 * mapping to the statistic. All stats will be the average of 197 * 1 - n, n+1 - 2n, ..., cn+1 - c(n+1) 198 * 199 * Grabs 'numberOfSnapshots' snapshots. Grabs one snapshot per 200 * 'everyNthsnapshot' 201 * 202 * @param numberOfSnapshot 203 * @param everyNthSnapshot 204 * @return ArrayList 205 */ 206 public ArrayList<HashMap<String, HashMap<String, Object>>> fetchSnapshotData(Integer numberOfSnapshot, Integer everyNthSnapshot) { 207 return (ArrayList<HashMap<String, HashMap<String, Object>>>)snapshotDBHelper.fetchData(numberOfSnapshot, everyNthSnapshot); 208 } 209 210 /** 211 * Fetches the max amount for each statistic stored from the snapshot thread 212 * and returns it in a HashMap 213 * 214 * @param numberOfSnapshot 215 * @return HashMap 216 */ 217 public HashMap<String, HashMap<String, Long>> fetchMaxSnapshotData(Integer numberOfSnapshot) { 218 return (HashMap<String, HashMap<String, Long>>)snapshotDBHelper.fetchMaxSnapshotData(numberOfSnapshot); 219 } 220 221 /** 222 * Fetches the min amount for each statistic stored from the snapshot thread 223 * and returns it in a HashMap 224 * 225 * @param numberOfSnapshot 226 * @return HashMap 227 */ 228 public HashMap<String, HashMap<String, Long>> fetchMinSnapshotData(Integer numberOfSnapshot) { 229 return (HashMap<String, HashMap<String, Long>>)snapshotDBHelper.fetchMinSnapshotData(numberOfSnapshot); 230 } 231 232 /** 233 * Gets the elapsed time in milliseconds between each snapshot. 234 * 235 * @return Long 236 */ 237 public Long getSnapshotDuration() { 238 try { 239 return Long.parseLong(SnapshotConfigXMLBuilder.getAttributeValue(DURATION)); 240 } catch(Exception e) { 241 return new Long(DEFAULT_DURATION); 242 } 243 } 244 245 /** 246 * Sets the elapsed time in milliseconds between each snapshot. 247 * 248 * @param snapshotDuration 249 */ 250 public void setSnapshotDuration(Long snapshotDuration) { 251 if(snapshotThread != null) { 252 snapshotThread.setSnapshotDuration(snapshotDuration.longValue()); 253 saveDuration(snapshotThread.getSnapshotDuration()); 254 } else { 255 log.warn("There is not a snapshot thread instantiated."); 256 } 257 } 258 259 public void setSnapshotRetention(Integer retention) { 260 saveRetention(retention.intValue()); 261 } 262 263 /** 264 * Begins the snapshot process given the time interval between snapshots 265 * 266 * Precondition: 267 * interval is given in milli seconds 268 * 269 * @param interval 270 */ 271 public boolean startSnapshot(Long interval) { 272 // get the saved/default retention period 273 String retentionStr = null; 274 try { 275 retentionStr = SnapshotConfigXMLBuilder.getAttributeValue(RETENTION); 276 } catch(Exception e){ 277 // happens when there is not an instance of "retention" in the config 278 // which is okay. 279 } 280 int retention; 281 if(retentionStr == null) { 282 retention = DEFAULT_RETENTION; 283 } else { 284 retention = Integer.parseInt(retentionStr); 285 } 286 return startSnapshot(interval, new Integer(retention)); 287 } 288 289 /** 290 * Begins the snapshot process given the time interval between snapshots 291 * 292 * Precondition: 293 * interval is given in milli seconds 294 * 295 * @param interval 296 */ 297 public boolean startSnapshot(Long interval, Integer retention) { 298 if((snapshotThread == null || (snapshotThread != null && (snapshotThread.SnapshotStatus() == 0))) && interval.longValue() > 0) { 299 saveDuration(interval.longValue()); 300 saveRetention(retention.intValue()); 301 snapshotThread = new SnapshotThread(interval.longValue(), mbServer); 302 snapshotThread.start(); 303 log.info("Snapshot thread successfully created."); 304 return true; 305 } else { 306 log.warn("There is already a snapshot thread running."); 307 return false; 308 } 309 } 310 311 public Long getSnapshotCount() { 312 return snapshotDBHelper.getSnapshotCount(); 313 } 314 315 /** 316 * Fetches all mbean names that provide JSR-77 statistics 317 * 318 * @return A set containing all mbean names of mbeans that provide 319 * statistics 320 */ 321 public Set<String> getStatisticsProviderMBeanNames() { 322 return (Set<String>)MBeanHelper.getStatsProvidersMBeans( getAllMBeanNames() ); 323 } 324 325 /** 326 * Fetches all mbean names 327 * 328 * @return A set containing all mbean names 329 */ 330 public Set<String> getAllMBeanNames() { 331 try { 332 Set<ObjectName> names = (Set<ObjectName>)mbServer.queryNames(null, null); 333 Set<String> strNames = new HashSet<String>(); 334 for(Iterator<ObjectName> it = names.iterator(); it.hasNext(); ) { 335 strNames.add(it.next().getCanonicalName()); 336 } 337 return strNames; 338 } catch(Exception e) { 339 log.error(e.getMessage(), e); 340 return new HashSet<String>(); 341 } 342 } 343 344 public void doFail() { 345 doStop(); 346 } 347 348 /** 349 * Executes when the GBean starts up. Also starts the snapshot thread. 350 */ 351 public void doStart() { 352 353 } 354 355 /** 356 * Executes when the GBean stops. Also stops the snapshot thread. 357 */ 358 public void doStop() { 359 if(SnapshotStatus() == 1) { 360 stopSnapshot(); 361 } 362 } 363 364 private void saveDuration(long duration) { 365 SnapshotConfigXMLBuilder.saveDuration(duration); 366 } 367 368 private void saveRetention(int retention) { 369 SnapshotConfigXMLBuilder.saveRetention(retention); 370 } 371 372 /** 373 * Adds a record of the mbean via its name to take snapshots of. As a result 374 * the mbeanName will be written to snapshot-config.xml 375 * 376 * @param mbeanName 377 */ 378 public boolean addMBeanForSnapshot(String mbeanName) { 379 return SnapshotConfigXMLBuilder.addMBeanName(mbeanName); 380 } 381 382 /** 383 * Removes a record of the mbean via its name to take snapshots of. As a result 384 * the mbeanName will be removed from snapshot-config.xml 385 * 386 * @param mbeanName 387 */ 388 public boolean removeMBeanForSnapshot(String mbeanName) { 389 return SnapshotConfigXMLBuilder.removeMBeanName(mbeanName); 390 } 391 392 /** 393 * @return A map: mbeanName --> ArrayList of statistic attributes for that mbean 394 */ 395 public HashMap<String, ArrayList<String>> getAllSnapshotStatAttributes() { 396 HashMap<String, ArrayList<String>> snapshotAttributes = new HashMap<String, ArrayList<String>>(); 397 Set<String> mbeans = getTrackedMBeans(); 398 // for each mbean name 399 for(Iterator<String> it = mbeans.iterator(); it.hasNext(); ) { 400 ArrayList<String> mbeanStatsList = new ArrayList<String>(); 401 String mbeanName = it.next(); 402 try { 403 Stats stats = (Stats)mbServer.getAttribute(new ObjectName(mbeanName), "stats"); 404 String[] sttsName = stats.getStatisticNames(); 405 Statistic[] stts = stats.getStatistics(); 406 for(int i = 0; i < sttsName.length; i++) { 407 Statistic aStat = stats.getStatistic(sttsName[i]); 408 if(aStat instanceof RangeStatistic) { 409 mbeanStatsList.add(stts[i].getName() + " Current"); 410 mbeanStatsList.add(stts[i].getName() + " Max"); 411 mbeanStatsList.add(stts[i].getName() + " Min"); 412 } else if(aStat instanceof CountStatistic) { 413 mbeanStatsList.add(stts[i].getName()); 414 } else if(aStat instanceof TimeStatistic) { 415 mbeanStatsList.add(stts[i].getName() + " Count"); 416 mbeanStatsList.add(stts[i].getName() + " MaxTime"); 417 mbeanStatsList.add(stts[i].getName() + " MinTime"); 418 mbeanStatsList.add(stts[i].getName() + " TotalTime"); 419 } else { 420 // for the time being, only numbers should be returned 421 } 422 } 423 } catch (Exception e) { 424 log.error(e.getMessage(), e); 425 } 426 // save attributes to the returning list 427 snapshotAttributes.put(mbeanName, mbeanStatsList); 428 } 429 return snapshotAttributes; 430 } 431 432 public Set<String> getTrackedMBeans() { 433 ArrayList<String> mbeans = (ArrayList<String>)SnapshotConfigXMLBuilder.getMBeanNames(); 434 Set<String> set = new HashSet<String>(); 435 for(int i = 0; i < mbeans.size(); i++) { 436 set.add(mbeans.get(i)); 437 } 438 return set; 439 } 440 441 public Integer getSnapshotRetention() { 442 try { 443 return new Integer(SnapshotConfigXMLBuilder.getAttributeValue( RETENTION )); 444 } catch(Exception e) { 445 return new Integer(DEFAULT_RETENTION); // the default 446 } 447 } 448 449 /** 450 * @param name - object name of the mbean 451 * @param operationName - method within the class 452 * @param params - parameters for the method 453 * @param signature - types for the parameters 454 * @return Invokes the method of a class defined. 455 */ 456 public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) throws Exception { 457 return mbServer.invoke(name, operationName, params, signature); 458 } 459 460 /** 461 * @param mbeanName 462 * @param statsName 463 * @param numberOfSnapshots 464 * @param everyNthSnapshot 465 * @return HashMap which maps from a snapshot_time --> value of the mbean.statsName at that time 466 */ 467 public TreeMap<Long, Long> getSpecificStatistics( String mbeanName, 468 String statsName, 469 Integer numberOfSnapshots, 470 Integer everyNthSnapshot, 471 Boolean showArchived) { 472 return snapshotDBHelper.getSpecificStatistics(mbeanName, statsName, numberOfSnapshots.intValue(), everyNthSnapshot.intValue(), showArchived); 473 } 474 475 /** 476 * @return Returns true if snapshot is running. 477 */ 478 public Integer SnapshotStatus() { 479 // TODO: check if the snapshot thread is running 480 if(snapshotThread == null) { 481 return 0; 482 } else { 483 return snapshotThread.SnapshotStatus(); 484 } 485 } 486 487 public static final GBeanInfo GBEAN_INFO; 488 489 static { 490 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic("MasterRemoteControlJMX", MasterRemoteControlJMX.class); 491 infoFactory.addOperation("getStats", new Class[] {String.class}, "HashMap"); 492 infoFactory.addOperation("setAttribute", new Class[] {String.class, String.class, Object.class}, "void"); 493 infoFactory.addOperation("startSnapshot", new Class[] {Long.class}, "Boolean"); 494 infoFactory.addOperation("startSnapshot", new Class[] {Long.class, Integer.class}, "Boolean"); 495 infoFactory.addOperation("stopSnapshot", new Class[] {}, "Boolean"); 496 infoFactory.addOperation("fetchSnapshotData", new Class[] {Integer.class, Integer.class}, "ArrayList"); 497 infoFactory.addOperation("fetchMaxSnapshotData", new Class[] {Integer.class}, "HashMap"); 498 infoFactory.addOperation("fetchMinSnapshotData", new Class[] {Integer.class}, "HashMap"); 499 infoFactory.addOperation("getSnapshotDuration", new Class[] {}, "Long"); 500 infoFactory.addOperation("getSnapshotCount", new Class[] {}, "Long"); 501 infoFactory.addOperation("setSnapshotDuration", new Class[] {Long.class}, "void"); 502 infoFactory.addOperation("getStatisticsProviderMBeanNames", new Class[] {}, "Set"); 503 infoFactory.addOperation("getAllMBeanNames", new Class[] {}, "Set"); 504 infoFactory.addOperation("getAllSnapshotStatAttributes", new Class[] {}, "HashMap"); 505 infoFactory.addOperation("addMBeanForSnapshot", new Class[] {String.class}, "void"); 506 infoFactory.addOperation("removeMBeanForSnapshot", new Class[] {String.class}, "void"); 507 infoFactory.addOperation("getSnapshotRetention", new Class[] {}, "Integer"); 508 infoFactory.addOperation("setSnapshotRetention", new Class[] {Integer.class}, "void"); 509 infoFactory.addOperation("SnapshotStatus", new Class[] {}, "Integer"); 510 infoFactory.addOperation("getSpecificStatistics", new Class[] {String.class, String.class, Integer.class, Integer.class, Boolean.class}, "TreeMap"); 511 infoFactory.addOperation("getTrackedMBeans", new Class[] {}, "Set"); 512 infoFactory.setConstructor(new String[] {}); 513 GBEAN_INFO = infoFactory.getBeanInfo(); 514 } 515 516 public static GBeanInfo getGBeanInfo() { 517 return GBEAN_INFO; 518 } 519 }