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.Properties; 025 import java.util.Set; 026 import java.util.TreeMap; 027 028 import javax.annotation.PostConstruct; 029 import javax.annotation.Resource; 030 import javax.annotation.security.PermitAll; 031 import javax.annotation.security.RolesAllowed; 032 import javax.ejb.Local; 033 import javax.ejb.Remote; 034 import javax.ejb.Stateless; 035 import javax.ejb.Timeout; 036 import javax.ejb.Timer; 037 import javax.ejb.TimerService; 038 import javax.management.Attribute; 039 import javax.management.MBeanServer; 040 import javax.management.MBeanServerFactory; 041 import javax.management.ObjectName; 042 import javax.management.j2ee.Management; 043 import javax.management.j2ee.ManagementHome; 044 import javax.management.j2ee.statistics.CountStatistic; 045 import javax.management.j2ee.statistics.RangeStatistic; 046 import javax.management.j2ee.statistics.Statistic; 047 import javax.management.j2ee.statistics.Stats; 048 import javax.management.j2ee.statistics.TimeStatistic; 049 import javax.naming.Context; 050 import javax.naming.InitialContext; 051 import javax.sql.DataSource; 052 053 import org.apache.commons.logging.Log; 054 import org.apache.commons.logging.LogFactory; 055 import org.apache.geronimo.monitoring.snapshot.SnapshotConfigXMLBuilder; 056 import org.apache.geronimo.monitoring.snapshot.SnapshotDBHelper; 057 import org.apache.geronimo.monitoring.snapshot.SnapshotProcessor; 058 059 /** 060 * This is a Stateful Session Bean that will be the bottleneck for the communication 061 * between the management node and the data in the server node. 062 */ 063 @Stateless(name="ejb/mgmt/MRC") 064 @Remote(MasterRemoteControlRemote.class) 065 @Local(MasterRemoteControlLocal.class) 066 @PermitAll 067 public class MasterRemoteControl { 068 private static Log log = LogFactory.getLog(MasterRemoteControl.class); 069 070 // constants 071 private static final String GERONIMO_DEFAULT_DOMAIN = "geronimo"; 072 private static final Long DEFAULT_DURATION = new Long(300000); 073 private static final int DEFAULT_RETENTION = 30; // 30 days 074 private static final String DURATION = "duration"; 075 private static final String RETENTION = "retention"; 076 077 // mbean server to talk to other components 078 private static MBeanServer mbServer = null; 079 080 // mangement ejb - use this to do the monitoring 081 private static Management mejb = null; 082 083 // credentials for snapshot processor 084 private static String username = null; 085 private static String password = null; 086 private static int port = -1; 087 088 // inject Data Sources 089 @Resource(name="jdbc/ActiveDS") private DataSource activeDS; 090 @Resource(name="jdbc/ArchiveDS") private DataSource archiveDS; 091 092 // inject a TimerService 093 @Resource private TimerService timer; 094 095 private SnapshotDBHelper snapshotDBHelper; 096 097 public MasterRemoteControl() { 098 099 } 100 101 @PostConstruct 102 private void init() { 103 // set up SnaphotDBHelper with the necessary data sources 104 // Note: do not put this in the constructor...datasources are not injected by then 105 snapshotDBHelper = new SnapshotDBHelper(activeDS, archiveDS); 106 } 107 108 /** 109 * Retrieves and instance of the MEJB and starts the snapshot process 110 */ 111 @RolesAllowed("mejbuser") 112 public void setUpMEJB(String username, String password) { 113 // instantiate the MEJB, which will be our gateway to communicate to MBeans 114 try { 115 Properties p = new Properties(); 116 p.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory"); 117 InitialContext ctx = new InitialContext(p); 118 119 ManagementHome mejbHome = (ManagementHome)ctx.lookup("ejb/mgmt/MEJBRemoteHome"); 120 mejb = mejbHome.create(); 121 122 // save credentials 123 this.username = username; 124 this.password = password; 125 this.port = port; 126 127 } catch(Exception e) { 128 log.error(e.getMessage(), e); 129 } 130 } 131 132 /** 133 * Looks up the JSR-77 statistics associated with this object name. 134 * 135 * @param objectName 136 * @return HashMap 137 * @throws Exception 138 */ 139 @RolesAllowed("mejbuser") 140 public static HashMap<String, Long> getStats(String objectName) throws Exception { 141 HashMap<String, Long> statsMap = new HashMap<String, Long>(); 142 Stats stats = (Stats)mejb.getAttribute(new ObjectName(objectName), "stats"); 143 String[] sttsName = stats.getStatisticNames(); 144 Statistic[] stts = stats.getStatistics(); 145 for(int i = 0; i < sttsName.length; i++) { 146 Statistic aStat = stats.getStatistic(sttsName[i]); 147 if(aStat instanceof RangeStatistic) { 148 Long current = new Long(((RangeStatistic)aStat).getCurrent()); 149 Long high = new Long(((RangeStatistic)aStat).getHighWaterMark()); 150 Long low = new Long(((RangeStatistic)aStat).getLowWaterMark()); 151 statsMap.put(stts[i].getName() + " Current", current); 152 statsMap.put(stts[i].getName() + " Max", high); 153 statsMap.put(stts[i].getName() + " Min", low); 154 } else if(aStat instanceof CountStatistic) { 155 Long current = new Long(((CountStatistic)aStat).getCount()); 156 statsMap.put(stts[i].getName(), current); 157 } else if(aStat instanceof TimeStatistic) { 158 Long count = new Long(((TimeStatistic)aStat).getCount()); 159 Long max = new Long(((TimeStatistic)aStat).getMaxTime()); 160 Long min = new Long(((TimeStatistic)aStat).getMinTime()); 161 Long total = new Long(((TimeStatistic)aStat).getTotalTime()); 162 statsMap.put(stts[i].getName() + " Count", count); 163 statsMap.put(stts[i].getName() + " MaxTime", max); 164 statsMap.put(stts[i].getName() + " MinTime", min); 165 statsMap.put(stts[i].getName() + " TotalTime", total); 166 } else { 167 // for the time being, only numbers should be returned 168 } 169 } 170 return statsMap; 171 } 172 173 /** 174 * Changes the objectName's attrName's value to attrValue 175 * 176 * @param objectName 177 * @param attrName 178 * @param attrValue 179 * @throws Exception 180 */ 181 @RolesAllowed("mejbadmin") 182 public void setAttribute(String objectName, String attrName, Object attrValue) throws Exception { 183 Attribute attr = new Attribute(attrName, attrValue); 184 mejb.setAttribute(new ObjectName(objectName), attr); 185 } 186 187 // This method is called by the EJB container upon Timer expiration. 188 @Timeout 189 @PermitAll 190 public void handleTimeout(Timer theTimer) { 191 SnapshotProcessor.takeSnapshot(this.username, this.password); 192 193 // get the duration of theTimer 194 long duration = Long.parseLong((String)theTimer.getInfo()); 195 // if the duration is different than the one in the snapshot-config.xml 196 // we need to get rid of this timer and start a new one with the 197 // correct duration. 198 if(duration != getSnapshotDuration().longValue()) { 199 Collection<Timer> timers = timer.getTimers(); 200 for(Iterator<Timer> it = timers.iterator(); it.hasNext(); ) { 201 // cancel all timers 202 it.next().cancel(); 203 } 204 // start a new one 205 long newDuration = getSnapshotDuration().longValue(); 206 timer.createTimer(newDuration, newDuration, "" + newDuration); 207 } 208 } 209 210 /** 211 * Begins the snapshot process given the time interval between snapshots 212 * 213 * Precondition: 214 * interval is given in milli seconds 215 * 216 * @param interval 217 */ 218 @RolesAllowed("mejbuser") 219 public boolean startSnapshot(Long interval) { 220 // get the saved/default retention period 221 String retentionStr = null; 222 try { 223 retentionStr = SnapshotConfigXMLBuilder.getAttributeValue("retention"); 224 } catch(Exception e){ 225 // happens when there is not an instance of "retention" in the config 226 // which is okay. 227 } 228 int retention; 229 if(retentionStr == null) { 230 retention = DEFAULT_RETENTION; 231 } else { 232 retention = Integer.parseInt(retentionStr); 233 } 234 return startSnapshot(interval, retention); 235 } 236 237 @RolesAllowed("mejbuser") 238 public boolean startSnapshot(Long interval, int retention) { 239 Collection<Timer> timers = timer.getTimers(); 240 if(timers.size() == 0) { 241 saveDuration(interval.longValue()); 242 saveRetention(retention); 243 timer.createTimer(0, interval.longValue(), "" + interval.longValue()); 244 log.info("Created timer successfully."); 245 return true; 246 } else { 247 log.warn("There is already a snapshot timer running..."); 248 return false; 249 } 250 } 251 252 /** 253 * Stops the snapshot thread 254 */ 255 @RolesAllowed("mejbuser") 256 public boolean stopSnapshot() { 257 Collection<Timer> timers = timer.getTimers(); 258 // stop all timers 259 boolean cancelled = false; 260 for(Iterator<Timer> it = timers.iterator(); it.hasNext(); ) { 261 Timer t = it.next(); 262 t.cancel(); 263 cancelled = true; 264 log.info("Stopped snapshot timer..."); 265 } 266 return cancelled; 267 } 268 269 /** 270 * Fetches the data stored from the snapshot thread and returns 271 * it in a ArrayList with each element being a HashMap of the attribute 272 * mapping to the statistic. All stats will be the average of 273 * 1 - n, n+1 - 2n, ..., cn+1 - c(n+1) 274 * 275 * Grabs 'numberOfSnapshots' snapshots. Grabs one snapshot per 276 * 'everyNthsnapshot' 277 * 278 * @param numberOfSnapshot 279 * @param everyNthSnapshot 280 * @return ArrayList 281 */ 282 @RolesAllowed("mejbuser") 283 public ArrayList<HashMap<String, HashMap<String, Object>>> fetchSnapshotData(Integer numberOfSnapshot, Integer everyNthSnapshot) { 284 return (ArrayList<HashMap<String, HashMap<String, Object>>>)snapshotDBHelper.fetchData(numberOfSnapshot, everyNthSnapshot); 285 } 286 287 /** 288 * Fetches the max amount for each statistic stored from the snapshot thread 289 * and returns it in a HashMap 290 * 291 * @param numberOfSnapshot 292 * @return HashMap 293 */ 294 @RolesAllowed("mejbuser") 295 public HashMap<String, HashMap<String, Long>> fetchMaxSnapshotData(Integer numberOfSnapshot) { 296 return (HashMap<String, HashMap<String, Long>>)snapshotDBHelper.fetchMaxSnapshotData(numberOfSnapshot); 297 } 298 299 /** 300 * Fetches the min amount for each statistic stored from the snapshot thread 301 * and returns it in a HashMap 302 * 303 * @param numberOfSnapshot 304 * @return HashMap 305 */ 306 @RolesAllowed("mejbuser") 307 public HashMap<String, HashMap<String, Long>> fetchMinSnapshotData(Integer numberOfSnapshot) { 308 return (HashMap<String, HashMap<String, Long>>)snapshotDBHelper.fetchMinSnapshotData(numberOfSnapshot); 309 } 310 311 /** 312 * Gets the elapsed time in milliseconds between each snapshot. 313 * 314 * @return Long 315 */ 316 @RolesAllowed("mejbuser") 317 public Long getSnapshotDuration() { 318 // return what is stored in the snapshot-config.xml or default value 319 try { 320 String returnedDuration = SnapshotConfigXMLBuilder.getAttributeValue( DURATION ); 321 return Long.parseLong( returnedDuration ); 322 } catch(Exception e) { 323 return DEFAULT_DURATION; // the default 324 } 325 } 326 327 /** 328 * Sets the elapsed time in milliseconds between each snapshot. 329 * The duration will be read in each time the handleTimeout() 330 * is called. So the change will be seen when the next 331 * handleTimeout() is called. 332 * 333 * @param snapshotDuration 334 */ 335 @RolesAllowed("mejbuser") 336 public void setSnapshotDuration(Long snapshotDuration) { 337 saveDuration(snapshotDuration); 338 } 339 340 @RolesAllowed("mejbuser") 341 public void setSnapshotRetention(int retention) { 342 saveRetention(retention); 343 } 344 345 @RolesAllowed("mejbuser") 346 public String getSnapshotRetention() { 347 try { 348 return SnapshotConfigXMLBuilder.getAttributeValue( RETENTION ); 349 } catch(Exception e) { 350 return "" + DEFAULT_RETENTION; // the default 351 } 352 } 353 354 @RolesAllowed("mejbuser") 355 public Long getSnapshotCount() { 356 return snapshotDBHelper.getSnapshotCount(); 357 } 358 359 /** 360 * Fetches all mbean names that provide JSR-77 statistics 361 * 362 * @return A set containing all mbean names of mbeans that provide 363 * statistics 364 */ 365 @RolesAllowed("mejbuser") 366 public Set<String> getStatisticsProviderMBeanNames() { 367 return MBeanHelper.getStatsProvidersMBeans( getAllMBeanNames() ); 368 } 369 370 /** 371 * Fetches all mbean names 372 * 373 * @return A set containing all mbean names 374 */ 375 @RolesAllowed("mejbuser") 376 public Set<String> getAllMBeanNames() { 377 try { 378 Set<ObjectName> names = (Set<ObjectName>)mejb.queryNames(null, null); 379 Set<String> strNames = new HashSet<String>(); 380 for(Iterator<ObjectName> it = names.iterator(); it.hasNext(); ) { 381 strNames.add(it.next().getCanonicalName()); 382 } 383 return strNames; 384 } catch(Exception e) { 385 log.error(e.getMessage(), e); 386 return new HashSet<String>(); 387 } 388 } 389 390 private void saveDuration(long duration) { 391 SnapshotConfigXMLBuilder.saveDuration(duration); 392 } 393 394 private void saveRetention(int retention) { 395 SnapshotConfigXMLBuilder.saveRetention(retention); 396 } 397 398 /** 399 * Adds a record of the mbean via its name to take snapshots of. As a result 400 * the mbeanName will be written to snapshot-config.xml 401 * 402 * @param mbeanName 403 */ 404 @RolesAllowed("mejbuser") 405 public boolean addMBeanForSnapshot(String mbeanName) { 406 return SnapshotConfigXMLBuilder.addMBeanName(mbeanName); 407 } 408 409 /** 410 * Removes a record of the mbean via its name to take snapshots of. As a result 411 * the mbeanName will be removed from snapshot-config.xml 412 * 413 * @param mbeanName 414 */ 415 @RolesAllowed("mejbuser") 416 public boolean removeMBeanForSnapshot(String mbeanName) { 417 return SnapshotConfigXMLBuilder.removeMBeanName(mbeanName); 418 } 419 420 /** 421 * @return A map: mbeanName --> ArrayList of statistic attributes for that mbean 422 */ 423 @RolesAllowed("mejbuser") 424 public HashMap<String, ArrayList<String>> getAllSnapshotStatAttributes() { 425 HashMap<String, ArrayList<String>> snapshotAttributes = new HashMap<String, ArrayList<String>>(); 426 Set<String> mbeans = getTrackedMBeans(); 427 // for each mbean name 428 for(Iterator<String> it = mbeans.iterator(); it.hasNext(); ) { 429 ArrayList<String> mbeanStatsList = new ArrayList<String>(); 430 String mbeanName = it.next(); 431 try { 432 Stats stats = (Stats)mejb.getAttribute(new ObjectName(mbeanName), "stats"); 433 String[] sttsName = stats.getStatisticNames(); 434 Statistic[] stts = stats.getStatistics(); 435 for(int i = 0; i < sttsName.length; i++) { 436 Statistic aStat = stats.getStatistic(sttsName[i]); 437 if(aStat instanceof RangeStatistic) { 438 mbeanStatsList.add(stts[i].getName() + " Current"); 439 mbeanStatsList.add(stts[i].getName() + " Max"); 440 mbeanStatsList.add(stts[i].getName() + " Min"); 441 } else if(aStat instanceof CountStatistic) { 442 mbeanStatsList.add(stts[i].getName()); 443 } else if(aStat instanceof TimeStatistic) { 444 mbeanStatsList.add(stts[i].getName() + " Count"); 445 mbeanStatsList.add(stts[i].getName() + " MaxTime"); 446 mbeanStatsList.add(stts[i].getName() + " MinTime"); 447 mbeanStatsList.add(stts[i].getName() + " TotalTime"); 448 } else { 449 // for the time being, only numbers should be returned 450 } 451 } 452 } catch (Exception e) { 453 log.error(e.getMessage(), e); 454 } 455 // save attributes to the returning list 456 snapshotAttributes.put(mbeanName, mbeanStatsList); 457 } 458 return snapshotAttributes; 459 } 460 461 /** 462 * @return Returns true if snapshot is running. 463 */ 464 @RolesAllowed("mejbuser") 465 public boolean isSnapshotRunning() { 466 Collection<Timer> timers = timer.getTimers(); 467 // if there are timers there is something running to collect snapshots 468 if(timers.size() > 0) { 469 return true; 470 } else { 471 return false; 472 } 473 } 474 475 /** 476 * @param name - object name of the mbean 477 * @param operationName - method within the class 478 * @param params - parameters for the method 479 * @param signature - types for the parameters 480 * @return Invokes the method of a class defined. 481 */ 482 @RolesAllowed("mejbadmin") 483 public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) throws Exception { 484 return mejb.invoke(name, operationName, params, signature); 485 } 486 487 /** 488 * @param mbeanName 489 * @param statsName 490 * @param numberOfSnapshots 491 * @param everyNthSnapshot 492 * @return HashMap which maps from a snapshot_time --> value of the mbean.statsName at that time 493 */ 494 @RolesAllowed("mejbuser") 495 public TreeMap<Long, Long> getSpecificStatistics( String mbeanName, 496 String statsName, 497 int numberOfSnapshots, 498 int everyNthSnapshot, 499 boolean showArchived) { 500 return snapshotDBHelper.getSpecificStatistics(mbeanName, statsName, numberOfSnapshots, everyNthSnapshot, showArchived); 501 } 502 503 /** 504 * @return A set of all mbeans being tracked from the db 505 */ 506 @RolesAllowed("mejbuser") 507 public Set<String> getTrackedMBeans() { 508 ArrayList<String> mbeans = (ArrayList<String>)SnapshotConfigXMLBuilder.getMBeanNames(); 509 Set<String> set = new HashSet<String>(); 510 for(int i = 0; i < mbeans.size(); i++) { 511 set.add(mbeans.get(i)); 512 } 513 return set; 514 } 515 }