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 */ 017package org.apache.camel.management.mbean; 018 019import java.io.InputStream; 020import java.util.Stack; 021 022import javax.xml.parsers.DocumentBuilder; 023import javax.xml.parsers.DocumentBuilderFactory; 024import javax.xml.parsers.SAXParser; 025import javax.xml.parsers.SAXParserFactory; 026 027import org.w3c.dom.Document; 028import org.w3c.dom.Element; 029import org.w3c.dom.Node; 030 031import org.xml.sax.Attributes; 032import org.xml.sax.Locator; 033import org.xml.sax.SAXException; 034import org.xml.sax.helpers.DefaultHandler; 035 036import org.apache.camel.CamelContext; 037import org.apache.camel.api.management.mbean.ManagedProcessorMBean; 038import org.apache.camel.api.management.mbean.ManagedRouteMBean; 039 040/** 041 * An XML parser that uses SAX to enrich route stats in the route dump. 042 * <p/> 043 * The coverage details: 044 * <ul> 045 * <li>exchangesTotal - Total number of exchanges</li> 046 * <li>totalProcessingTime - Total processing time in millis</li> 047 * </ul> 048 * Is included as attributes on the route nodes. 049 */ 050public final class RouteCoverageXmlParser { 051 052 private RouteCoverageXmlParser() { 053 } 054 055 /** 056 * Parses the XML. 057 * 058 * @param camelContext the CamelContext 059 * @param is the XML content as an input stream 060 * @return the DOM model of the routes with coverage information stored as attributes 061 * @throws Exception is thrown if error parsing 062 */ 063 public static Document parseXml(final CamelContext camelContext, final InputStream is) throws Exception { 064 final SAXParserFactory factory = SAXParserFactory.newInstance(); 065 final SAXParser parser = factory.newSAXParser(); 066 final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); 067 final DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); 068 final Document doc = docBuilder.newDocument(); 069 070 final Stack<Element> elementStack = new Stack<>(); 071 final StringBuilder textBuffer = new StringBuilder(); 072 final DefaultHandler handler = new DefaultHandler() { 073 074 @Override 075 public void setDocumentLocator(final Locator locator) { 076 // noop 077 } 078 079 @Override 080 public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { 081 addTextIfNeeded(); 082 083 final Element el = doc.createElement(qName); 084 // add other elements 085 for (int i = 0; i < attributes.getLength(); i++) { 086 el.setAttribute(attributes.getQName(i), attributes.getValue(i)); 087 } 088 089 String id = el.getAttribute("id"); 090 if (id != null) { 091 try { 092 if ("route".equals(qName)) { 093 ManagedRouteMBean route = camelContext.getManagedRoute(id, ManagedRouteMBean.class); 094 if (route != null) { 095 long total = route.getExchangesTotal(); 096 el.setAttribute("exchangesTotal", "" + total); 097 long totalTime = route.getTotalProcessingTime(); 098 el.setAttribute("totalProcessingTime", "" + totalTime); 099 } 100 } else if ("from".equals(qName)) { 101 // grab statistics from the parent route as from would be the same 102 Element parent = elementStack.peek(); 103 if (parent != null) { 104 String routeId = parent.getAttribute("id"); 105 ManagedRouteMBean route = camelContext.getManagedRoute(routeId, ManagedRouteMBean.class); 106 if (route != null) { 107 long total = route.getExchangesTotal(); 108 el.setAttribute("exchangesTotal", "" + total); 109 long totalTime = route.getTotalProcessingTime(); 110 el.setAttribute("totalProcessingTime", "" + totalTime); 111 // from is index-0 112 el.setAttribute("index", "0"); 113 } 114 } 115 } else { 116 ManagedProcessorMBean processor = camelContext.getManagedProcessor(id, ManagedProcessorMBean.class); 117 if (processor != null) { 118 long total = processor.getExchangesTotal(); 119 el.setAttribute("exchangesTotal", "" + total); 120 long totalTime = processor.getTotalProcessingTime(); 121 el.setAttribute("totalProcessingTime", "" + totalTime); 122 int index = processor.getIndex(); 123 el.setAttribute("index", "" + index); 124 } 125 } 126 } catch (Exception e) { 127 // ignore 128 } 129 } 130 131 // we do not want customId in output of the EIPs 132 if (!"route".equals(qName)) { 133 el.removeAttribute("customId"); 134 } 135 136 elementStack.push(el); 137 } 138 139 @Override 140 public void endElement(final String uri, final String localName, final String qName) { 141 addTextIfNeeded(); 142 final Element closedEl = elementStack.pop(); 143 if (elementStack.isEmpty()) { 144 // is this the root element? 145 doc.appendChild(closedEl); 146 } else { 147 final Element parentEl = elementStack.peek(); 148 parentEl.appendChild(closedEl); 149 } 150 } 151 152 @Override 153 public void characters(final char ch[], final int start, final int length) throws SAXException { 154 textBuffer.append(ch, start, length); 155 } 156 157 /** 158 * outputs text accumulated under the current node 159 */ 160 private void addTextIfNeeded() { 161 if (textBuffer.length() > 0) { 162 final Element el = elementStack.peek(); 163 final Node textNode = doc.createTextNode(textBuffer.toString()); 164 el.appendChild(textNode); 165 textBuffer.delete(0, textBuffer.length()); 166 } 167 } 168 }; 169 parser.parse(is, handler); 170 171 return doc; 172 } 173}