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 }