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 }