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    }