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.servicemix.web.jmx;
018    
019    import org.apache.commons.logging.Log;
020    import org.apache.commons.logging.LogFactory;
021    
022    import javax.management.AttributeNotFoundException;
023    import javax.management.InstanceNotFoundException;
024    import javax.management.JMException;
025    import javax.management.MBeanAttributeInfo;
026    import javax.management.MBeanException;
027    import javax.management.MBeanInfo;
028    import javax.management.MBeanServer;
029    import javax.management.ObjectInstance;
030    import javax.management.ObjectName;
031    import javax.management.ReflectionException;
032    
033    import java.io.IOException;
034    import java.io.PrintWriter;
035    import java.util.ArrayList;
036    import java.util.Collection;
037    import java.util.HashSet;
038    import java.util.Hashtable;
039    import java.util.Iterator;
040    import java.util.List;
041    import java.util.Map;
042    import java.util.Set;
043    import java.util.TreeMap;
044    import java.util.TreeSet;
045    import java.util.Map.Entry;
046    
047    /**
048     * A useful class for turning JMX statistics into XML and XHTML
049     *
050     * @version $Revision: 356269 $
051     */
052    public class JMXWriter {
053        private static final Log log = LogFactory.getLog(JMXWriter.class);
054    
055        private PrintWriter writer;
056        private ManagementContext managementContext;
057        private String unknownValue = "Unknown";
058    
059        public JMXWriter(PrintWriter writer, ManagementContext context) {
060            this.writer = writer;
061            managementContext = context;
062        }
063    
064        public MBeanServer getMBeanServer() {
065            return managementContext.getMBeanServer();
066        }
067    
068        public ManagementContext getManagementContext() {
069            return managementContext;
070        }
071    
072        public void setManagementContext(ManagementContext managementContext) {
073            this.managementContext = managementContext;
074        }
075    
076        public void outputHtmlNamesByDomain(Collection names) throws IOException {
077            Map map = new TreeMap();
078            for (Iterator iter = names.iterator(); iter.hasNext();) {
079                ObjectName name = (ObjectName) iter.next();
080                String domain = name.getDomain();
081                List list = (List) map.get(domain);
082                if (list == null) {
083                    list = new ArrayList();
084                    map.put(domain, list);
085                }
086                list.add(name);
087            }
088            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
089                Map.Entry entry = (Map.Entry) iter.next();
090                String domain = (String) entry.getKey();
091                names = (List) entry.getValue();
092    
093                writer.print("<li>");
094                writer.print(domain);
095    
096                writer.print("<ul>");
097                outputHtmlNamesByProperty(names, "Type");
098                writer.print("</ul>");
099                writer.print("</li>");
100            }
101        }
102    
103        public void outputHtmlNamesByProperty(Collection names, String property) throws IOException {
104            Map map = new TreeMap();
105            for (Iterator iter = names.iterator(); iter.hasNext();) {
106                ObjectName name = (ObjectName) iter.next();
107                String propertyValue = name.getKeyProperty(property);
108                if (propertyValue == null) {
109                    propertyValue = unknownValue;
110                }
111                List list = (List) map.get(propertyValue);
112                if (list == null) {
113                    list = new ArrayList();
114                    map.put(propertyValue, list);
115                }
116                list.add(name);
117            }
118            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
119                Map.Entry entry = (Map.Entry) iter.next();
120                String propertyValue = (String) entry.getKey();
121                names = (List) entry.getValue();
122    
123                if (names.size() > 1) {
124                    writer.print("<li><a href='");
125                    printDetailURL(property, propertyValue);
126                    writer.print("'>");
127                    writer.print(propertyValue);
128                    writer.print("</a><ul>");
129    
130                    // outputHtmlNames(names);
131                    outputHtmlNamesSortedByShortName(names);
132                    writer.print("</ul>");
133                    writer.print("</li>");
134                }
135                else if (names.size() == 1) {
136                    ObjectName name = (ObjectName) ((List) names).get(0);
137                    outputHtmlName(name, propertyValue);
138                }
139            }
140        }
141    
142        public void outputHtmlNamesSortedByShortName(Collection names) throws IOException {
143            Map map = new TreeMap();
144            for (Iterator iter = names.iterator(); iter.hasNext();) {
145                ObjectName name = (ObjectName) iter.next();
146                String propertyValue = getShortName(name);
147                map.put(propertyValue, name);
148            }
149            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
150                Map.Entry entry = (Map.Entry) iter.next();
151                String description = (String) entry.getKey();
152                ObjectName name = (ObjectName) entry.getValue();
153                outputHtmlName(name, description);
154            }
155        }
156    
157        public void outputHtmlNames(Collection names) throws IOException {
158            for (Iterator iter = names.iterator(); iter.hasNext();) {
159                outputHtmlNames((ObjectName) iter.next());
160            }
161        }
162    
163        public void outputHtmlNames(ObjectName name) throws IOException {
164            outputHtmlName(name, getShortName(name));
165        }
166    
167        public void outputHtmlName(ObjectName name, String shortName) throws IOException {
168            writer.print("<li><a href='");
169            printDetailURL(name);
170    
171            /*
172             * Map properties = name.getKeyPropertyList(); for (Iterator iter =
173             * properties.entrySet().iterator(); iter.hasNext();) { Map.Entry entry =
174             * (Map.Entry) iter.next(); writer.print("<property name='");
175             * writer.print(entry.getKey()); writer.print("'>");
176             * writer.print(entry.getValue()); writer.println("</property>"); }
177             */
178    
179            writer.print("'>");
180            // writer.print(name.getCanonicalKeyPropertyListString());
181            writer.print(shortName);
182            writer.print("</a></li>");
183        }
184    
185        /**
186         * Returns a short descriptive name of the ObjectName without the domain
187         */
188        protected String getShortName(ObjectName name) {
189            // TODO Auto-generated method stub
190            String answer = name.toString();
191            int idx = answer.indexOf(':');
192            if (idx >= 0) {
193                return answer.substring(idx + 1);
194            }
195            return answer;
196        }
197    
198        public void outputNames(Collection names) throws IOException {
199            for (Iterator iter = names.iterator(); iter.hasNext();) {
200                outputNames((ObjectName) iter.next());
201            }
202        }
203    
204        public void outputNames(ObjectName name) throws IOException {
205            writer.print("<mbean name='");
206            writer.print(name.getCanonicalName());
207            writer.print("' domain='");
208            writer.print(name.getDomain());
209            writer.println("'>");
210            Map properties = name.getKeyPropertyList();
211            for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) {
212                Map.Entry entry = (Map.Entry) iter.next();
213                writer.print("<property name='");
214                writer.print(entry.getKey());
215                writer.print("'>");
216                writer.print(entry.getValue());
217                writer.println("</property>");
218            }
219            writer.println("</mbean>");
220        }
221    
222        public void outputDetail(Set names) throws IOException, JMException {
223    
224            for (Iterator iter = names.iterator(); iter.hasNext();) {
225                outputDetail((ObjectName) iter.next());
226            }
227        }
228    
229        public void outputDetail(ObjectName name) throws JMException, IOException {
230    
231            MBeanServer beanServer = getMBeanServer();
232            MBeanInfo beanInfo = beanServer.getMBeanInfo(name);
233            writer.print("<mbean name='");
234            writer.print(name.getCanonicalName());
235            writer.print("' domain='");
236            writer.print(name.getDomain());
237            writer.println("'>");
238    
239            MBeanAttributeInfo[] attributes = beanInfo.getAttributes();
240            for (int i = 0; i < attributes.length; i++) {
241                MBeanAttributeInfo info = attributes[i];
242                if (info.isReadable()) {
243                    String attributeName = info.getName();
244                    Object value = getAttributeValue(name, attributeName);
245                    if (value != null) {
246                        writer.print("<attribute name='");
247                        writer.print(attributeName);
248                        writer.print("' type='");
249                        writer.print(info.getType());
250                        writer.print("'>");
251                        printEncodedValue(value);
252                        writer.println("</attribute>");
253                    }
254                }
255            }
256    
257            writer.println("</mbean>");
258        }
259    
260        public void outputHtmlProperties(Set names) throws JMException, IOException {
261            if (names.size() <= 1) {
262            for (Iterator iter = names.iterator(); iter.hasNext();) {
263                outputHtmlProperties((ObjectName) iter.next());
264            }
265            }else {
266                outputHtmlPropertiesGrid(names);
267            }
268        }
269    
270        public void outputHtmlPropertiesGrid(Set names) throws JMException {
271            Set propertyNames = new TreeSet();
272            Map[] propertyNamesPerMBeanArray = new Map[names.size()];
273            int beanCounter = 0;
274            for (Iterator iter = names.iterator(); iter.hasNext();) {
275                ObjectName name = (ObjectName) iter.next();
276                Hashtable keyMap = name.getKeyPropertyList();
277                propertyNamesPerMBeanArray[beanCounter++] = keyMap;
278                propertyNames.addAll(keyMap.keySet());
279            }
280    
281            writer.println("<table>");
282            writer.println("<tr>");
283            writer.print("<th>Domain</th>");
284            for (Iterator iter = propertyNames.iterator(); iter.hasNext();) {
285                writer.print("<th>");
286                writer.print(iter.next());
287                writer.print("</th>");
288            }
289            writer.println();
290            writer.println("</tr>");
291    
292            beanCounter = 0;
293            for (Iterator iter = names.iterator(); iter.hasNext();) {
294                ObjectName name = (ObjectName) iter.next();
295    
296                writer.print("<tr><td class='domainName'>");
297                writer.print(name.getDomain());
298                writer.print("</td>");
299    
300                for (Iterator iter2 = propertyNames.iterator(); iter2.hasNext();) {
301                    String propertyName = (String) iter2.next();
302    
303                    if (propertyNamesPerMBeanArray[beanCounter].containsKey(propertyName)) {
304                        String value = name.getKeyProperty(propertyName);
305                        writer.print("<td class='");
306                        writer.print(propertyName);
307                        writer.print("'>");
308                        if (value != null) {
309                            printEncodedValue(value);
310                        }
311                        writer.print("</td>");
312                    }
313                }
314                writer.print("</tr>");
315                beanCounter++;
316            }
317            writer.println("</table>");
318        }
319    
320    
321        public void outputHtmlProperties(ObjectName name) throws JMException, IOException {
322            writer.println("<table>");
323            writer.println("<tr>");
324            writer.println("<th>Property</th><th>Value</th>");
325            writer.println("</tr>");
326    
327            writer.print("<tr><td>Domain</td><td>");
328            String domain = name.getDomain();
329            if (domain != null) {
330                printEncodedValue(domain);
331            }
332            writer.print("</td></tr>");
333    
334            Map map = name.getKeyPropertyList();
335            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
336                Map.Entry entry = (Entry) iter.next();
337                String attributeName = (String) entry.getKey();
338                String value = (String) entry.getValue();
339    
340                writer.print("<tr><td>");
341                writer.print(attributeName);
342                writer.print("</td><td>");
343                if (value != null) {
344                    printEncodedValue(value);
345                }
346                writer.print("</td></tr>");
347            }
348    
349            writer.println("</table>");
350        }
351    
352        public void outputHtmlAttributes(Set names) throws IOException, JMException {
353            if (names.size() <= 1) {
354                for (Iterator iter = names.iterator(); iter.hasNext();) {
355                    outputHtmlAttributes((ObjectName) iter.next());
356                }
357            }
358            else {
359                outputHtmlAttributeGrid(names);
360            }
361        }
362    
363        public void outputHtmlAttributeGrid(Set names) throws JMException {
364            MBeanServer beanServer = getMBeanServer();
365            Set attributeNames = new TreeSet();
366            Set[] attributeNamesPerMBeanArray = new Set[names.size()];
367            int beanCounter = 0;
368            for (Iterator iter = names.iterator(); iter.hasNext();) {
369                ObjectName name = (ObjectName) iter.next();
370                MBeanInfo beanInfo = beanServer.getMBeanInfo(name);
371                MBeanAttributeInfo[] attributes = beanInfo.getAttributes();
372    
373                Set availableNamesPerMBean = new HashSet();
374                attributeNamesPerMBeanArray[beanCounter++] = availableNamesPerMBean;
375                for (int i = 0; i < attributes.length; i++) {
376                    MBeanAttributeInfo info = attributes[i];
377                    if (info.isReadable()) {
378                        String attributeName = info.getName();
379                        availableNamesPerMBean.add(attributeName);
380                        attributeNames.add(attributeName);
381                    }
382                }
383            }
384    
385            writer.println("<table>");
386            writer.println("<tr>");
387            writer.print("<th>MBean</th>");
388            for (Iterator iter = attributeNames.iterator(); iter.hasNext();) {
389                writer.print("<th>");
390                writer.print(iter.next());
391                writer.print("</th>");
392            }
393            writer.println();
394            writer.println("</tr>");
395    
396            beanCounter = 0;
397            for (Iterator iter = names.iterator(); iter.hasNext();) {
398                ObjectName name = (ObjectName) iter.next();
399    
400                writer.print("<tr><td class='mbeanName'>");
401                writer.print(name);
402                writer.print("</td>");
403    
404                for (Iterator iter2 = attributeNames.iterator(); iter2.hasNext();) {
405                    String attributeName = (String) iter2.next();
406    
407                    if (attributeNamesPerMBeanArray[beanCounter].contains(attributeName)) {
408                        Object value = getAttributeValue(name, attributeName);
409                        writer.print("<td class='");
410                        writer.print(attributeName);
411                        writer.print("'>");
412                        if (value != null) {
413                            printEncodedValue(value);
414                        }
415                        writer.print("</td>");
416                    }
417                }
418                writer.print("</tr>");
419                beanCounter++;
420            }
421            writer.println("</table>");
422        }
423    
424        public void outputHtmlAttributes(ObjectName name) throws JMException, IOException {
425            MBeanServer beanServer = getMBeanServer();
426            MBeanInfo beanInfo = beanServer.getMBeanInfo(name);
427            writer.println("<table>");
428            writer.println("<tr>");
429            writer.println("<th>Attribute</th><th>Value</th><th>Type</th>");
430            writer.println("</tr>");
431    
432            MBeanAttributeInfo[] attributes = beanInfo.getAttributes();
433            for (int i = 0; i < attributes.length; i++) {
434                MBeanAttributeInfo info = attributes[i];
435                if (info.isReadable()) {
436                    String attributeName = info.getName();
437    
438                    Object value = getAttributeValue(name, attributeName);
439                    writer.print("<tr><td>");
440                    writer.print(attributeName);
441                    writer.print("</td><td>");
442                    if (value != null) {
443                        printEncodedValue(value);
444                    }
445                    writer.print("</td><td>");
446                    writer.print(info.getType());
447                    writer.print("</td></tr>");
448                }
449            }
450    
451            writer.println("</table>");
452        }
453    
454        public void outputMBeans(Collection names) throws IOException {
455            for (Iterator iter = names.iterator(); iter.hasNext();) {
456                outputMBeans((ObjectName) iter.next());
457            }
458        }
459    
460        public void outputMBeans(ObjectName name) throws IOException {
461            Map properties = name.getKeyPropertyList();
462            for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) {
463                Map.Entry entry = (Map.Entry) iter.next();
464                writer.print("<mbean name='");
465                writer.print(entry.getKey());
466                ObjectInstance objectInstance = (ObjectInstance) entry.getValue();
467                if (objectInstance != null) {
468                    writer.print("' className='");
469                    writer.print(objectInstance.getClassName());
470                }
471                writer.println("'/>");
472            }
473        }
474    
475        public void outputHeader() throws IOException {
476            writer.println("<?xml version='1.0'?>");
477            writer.println("<mbeans>");
478        }
479    
480        public void outputFooter() throws IOException {
481            writer.println("</mbeans>");
482        }
483    
484        /**
485         * Encodes the value as a String and ensures that there are no bad XML
486         * characters like < or > which are encoded.
487         */
488        public void printEncodedValue(Object value) {
489            if (value != null) {
490                String text = value.toString();
491                for (int i = 0, size = text.length(); i < size; i++) {
492                    char ch = text.charAt(i);
493                    switch (ch) {
494                    case '<':
495                        writer.print("&lt;");
496                        break;
497    
498                    case '>':
499                        writer.print("&gt;");
500                        break;
501    
502                    case '&':
503                        writer.print("&amp;");
504                        break;
505    
506                    // used in ObjectName
507                    case ',':
508                        writer.print("%2C");
509                        break;
510    
511                    case ':':
512                        writer.print("%3A");
513                        break;
514    
515                    case '=':
516                        writer.print("%3D");
517                        break;
518    
519                    default:
520                        writer.print(ch);
521                    }
522                }
523            }
524        }
525    
526        /**
527         * Prints a HTTP encoded ObjectName suitable for use inside URLs
528         */
529        public void printEncodedObjectName(ObjectName name) {
530            printEncodedValue(name);
531        }
532    
533        /**
534         * Outputs a URL to the detail JMX stats view
535         */
536        protected void printDetailURL(ObjectName name) {
537            writer.print("mbeanDetail.jsp?view=detail&style=html&name=");
538            printEncodedObjectName(name);
539        }
540    
541        /**
542         * Outputs a URL to the detail JMX stats view
543         */
544        protected void printDetailURL(String propertyName, String propertyValue) {
545            writer.print("mbeanDetail.jsp?view=detail&style=html&query=");
546            printEncodedValue("*:" + propertyName + "=" + propertyValue + ",*");
547        }
548    
549        protected Object getAttributeValue(ObjectName name, String attributeName) throws MBeanException {
550            MBeanServer beanServer = getMBeanServer();
551            Object value = null;
552            try {
553                value = beanServer.getAttribute(name, attributeName);
554            }
555            catch (AttributeNotFoundException e) {
556                log.warn("Caught: " + e, e);
557            }
558            catch (InstanceNotFoundException e) {
559                log.warn("Caught: " + e, e);
560            }
561            catch (ReflectionException e) {
562                log.warn("Caught: " + e, e);
563            }
564            return value;
565        }
566    
567    }