| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. You may obtain a |
| * copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package org.apache.geode.internal.statistics; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.util.ArrayList; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.Text; |
| import org.xml.sax.EntityResolver; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXNotRecognizedException; |
| import org.xml.sax.SAXParseException; |
| |
| import org.apache.geode.GemFireConfigException; |
| import org.apache.geode.StatisticDescriptor; |
| import org.apache.geode.StatisticsType; |
| import org.apache.geode.StatisticsTypeFactory; |
| import org.apache.geode.internal.Assert; |
| import org.apache.geode.internal.ClassPathLoader; |
| |
| // @todo davidw Use a SAX parser instead of DOM |
| /** |
| * This is an internal helper class for dealing with the SessionFactory XML configuration files. |
| */ |
| public class StatisticsTypeXml implements EntityResolver, ErrorHandler { |
| |
| /** The name of the DTD file */ |
| static final String DTD = "statisticsType.dtd"; |
| |
| static final String systemId = "http://www.gemstone.com/dtd/" + DTD; |
| static final String publicId = "-//GemStone Systems, Inc.//GemFire StatisticsType//EN"; |
| |
| ///////////////////// Interface methods /////////////////////// |
| |
| /** |
| * Given a publicId, attempts to resolve it to a DTD. Returns an <code>InputSource</code> for the |
| * DTD. |
| */ |
| @Override |
| public InputSource resolveEntity(String publicId, String systemId) throws SAXException { |
| |
| // Figure out the location for the publicId. Be tolerant of other |
| // versions of the dtd |
| if (publicId.equals(StatisticsTypeXml.publicId) || systemId.equals(StatisticsTypeXml.systemId) |
| || systemId.endsWith(DTD)) { |
| |
| // Public ID for system config DTD |
| String location = "/org/apache/geode/" + DTD; |
| InputStream stream = ClassPathLoader.getLatest().getResourceAsStream(getClass(), location); |
| if (stream != null) { |
| return new InputSource(stream); |
| |
| } else { |
| throw new SAXNotRecognizedException( |
| String.format("DTD not found: %s", location)); |
| } |
| |
| } else { |
| throw new SAXNotRecognizedException( |
| String.format("Invalid public ID: ' %s '", publicId)); |
| } |
| } |
| |
| @Override |
| public void warning(SAXParseException exception) throws SAXException { |
| // We don't want to thrown an exception. We want to log it!! |
| // FIXME |
| // String s = "SAX warning while working with XML"; |
| } |
| |
| @Override |
| public void error(SAXParseException exception) throws SAXException { |
| throw new GemFireConfigException( |
| "SAX error while working with XML", |
| exception); |
| } |
| |
| @Override |
| public void fatalError(SAXParseException exception) throws SAXException { |
| throw new GemFireConfigException( |
| "SAX fatal error while working with XML", |
| exception); |
| } |
| |
| ////////////////////// Parsing XML File //////////////////////// |
| |
| /** |
| * Parses the contents of XML data and from it creates one or more <code>StatisticsType</code> |
| * instances. |
| */ |
| public StatisticsType[] read(Reader reader, StatisticsTypeFactory statFactory) { |
| DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
| // factory.setValidating(validate); |
| |
| DocumentBuilder parser = null; |
| try { |
| parser = factory.newDocumentBuilder(); |
| |
| } catch (ParserConfigurationException ex) { |
| throw new GemFireConfigException( |
| "Failed parsing XML", ex); |
| } |
| |
| parser.setErrorHandler(this); |
| parser.setEntityResolver(this); |
| Document doc; |
| try { |
| doc = parser.parse(new InputSource(reader)); |
| } catch (SAXException se) { |
| throw new GemFireConfigException( |
| "Failed parsing XML", se); |
| } catch (IOException io) { |
| throw new GemFireConfigException( |
| "Failed reading XML data", io); |
| } |
| |
| if (doc == null) { |
| throw new GemFireConfigException( |
| "Failed reading XML data; no document"); |
| } |
| Element root = doc.getDocumentElement(); |
| if (root == null) { |
| throw new GemFireConfigException( |
| "Failed reading XML data; no root element"); |
| } |
| return extractStatistics(root, statFactory); |
| } |
| |
| /* |
| * <!ELEMENT statistics (type)+> |
| */ |
| private StatisticsType[] extractStatistics(Element root, StatisticsTypeFactory statFactory) { |
| Assert.assertTrue(root.getTagName().equals("statistics")); |
| |
| ArrayList types = new ArrayList(); |
| NodeList typeNodes = root.getElementsByTagName("type"); |
| for (int i = 0; i < typeNodes.getLength(); i++) { |
| Element typeNode = (Element) typeNodes.item(i); |
| types.add(extractType(typeNode, statFactory)); |
| } |
| return (StatisticsType[]) types.toArray(new StatisticsType[0]); |
| } |
| |
| /** |
| * <!ELEMENT type (description?, (stat)+)> <!ATTLIST type name CDATA #REQUIRED> |
| */ |
| private StatisticsType extractType(Element typeNode, StatisticsTypeFactory statFactory) { |
| Assert.assertTrue(typeNode.getTagName().equals("type")); |
| Assert.assertTrue(typeNode.hasAttribute("name")); |
| |
| final String typeName = typeNode.getAttribute("name"); |
| ArrayList stats = new ArrayList(); |
| NodeList statNodes = typeNode.getElementsByTagName("stat"); |
| for (int i = 0; i < statNodes.getLength(); i++) { |
| Element statNode = (Element) statNodes.item(i); |
| stats.add(extractStat(statNode, statFactory)); |
| } |
| StatisticDescriptor[] descriptors = |
| (StatisticDescriptor[]) stats.toArray(new StatisticDescriptor[0]); |
| String description = ""; |
| { |
| NodeList descriptionNodes = typeNode.getElementsByTagName("description"); |
| if (descriptionNodes.getLength() > 0) { |
| // descriptionNodes will contain the both our description, if it exists, |
| // and any nested stat descriptions. Ours will always be first |
| Element descriptionNode = (Element) descriptionNodes.item(0); |
| // but make sure the first one belongs to our node |
| if (descriptionNode.getParentNode().getNodeName().equals(typeNode.getNodeName())) { |
| description = extractDescription(descriptionNode); |
| } |
| } |
| } |
| |
| return statFactory.createType(typeName, description, descriptors); |
| } |
| |
| private static final int INT_STORAGE = 0; |
| private static final int LONG_STORAGE = 1; |
| private static final int DOUBLE_STORAGE = 2; |
| |
| /** |
| * <!ELEMENT stat (description?, unit?)> <!ATTLIST stat name CDATA #REQUIRED counter (true | |
| * false) #IMPLIED largerBetter (true | false) #IMPLIED storage (int | long | double) #IMPLIED > |
| */ |
| private StatisticDescriptor extractStat(Element statNode, StatisticsTypeFactory statFactory) { |
| Assert.assertTrue(statNode.getTagName().equals("stat")); |
| Assert.assertTrue(statNode.hasAttribute("name")); |
| |
| final String statName = statNode.getAttribute("name"); |
| String description = ""; |
| String unit = ""; |
| boolean isCounter = true; |
| boolean largerBetter; |
| int storage = INT_STORAGE; |
| |
| if (statNode.hasAttribute("counter")) { |
| String value = statNode.getAttribute("counter"); |
| Assert.assertTrue(value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")); |
| isCounter = Boolean.valueOf(value).booleanValue(); |
| } |
| largerBetter = isCounter; // default |
| if (statNode.hasAttribute("largerBetter")) { |
| String value = statNode.getAttribute("largerBetter"); |
| Assert.assertTrue(value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")); |
| largerBetter = Boolean.valueOf(value).booleanValue(); |
| } |
| if (statNode.hasAttribute("storage")) { |
| String value = statNode.getAttribute("storage"); |
| if (value.equalsIgnoreCase("int")) { |
| storage = INT_STORAGE; |
| } else if (value.equalsIgnoreCase("long")) { |
| storage = LONG_STORAGE; |
| } else { |
| Assert.assertTrue(value.equalsIgnoreCase("double")); |
| storage = DOUBLE_STORAGE; |
| } |
| } |
| { |
| NodeList descriptionNodes = statNode.getElementsByTagName("description"); |
| Assert.assertTrue(descriptionNodes.getLength() <= 1); |
| if (descriptionNodes.getLength() == 1) { |
| Element descriptionNode = (Element) descriptionNodes.item(0); |
| description = extractDescription(descriptionNode); |
| } |
| } |
| |
| { |
| NodeList unitNodes = statNode.getElementsByTagName("unit"); |
| Assert.assertTrue(unitNodes.getLength() <= 1); |
| if (unitNodes.getLength() == 1) { |
| Element unitNode = (Element) unitNodes.item(0); |
| unit = extractUnit(unitNode); |
| } |
| } |
| if (isCounter) { |
| switch (storage) { |
| case INT_STORAGE: |
| return statFactory.createIntCounter(statName, description, unit, largerBetter); |
| case LONG_STORAGE: |
| return statFactory.createLongCounter(statName, description, unit, largerBetter); |
| case DOUBLE_STORAGE: |
| return statFactory.createDoubleCounter(statName, description, unit, largerBetter); |
| default: |
| throw new RuntimeException(String.format("unexpected storage type %s", |
| Integer.valueOf(storage))); |
| } |
| } else { |
| switch (storage) { |
| case INT_STORAGE: |
| return statFactory.createIntGauge(statName, description, unit, largerBetter); |
| case LONG_STORAGE: |
| return statFactory.createLongGauge(statName, description, unit, largerBetter); |
| case DOUBLE_STORAGE: |
| return statFactory.createDoubleGauge(statName, description, unit, largerBetter); |
| default: |
| throw new RuntimeException(String.format("unexpected storage type %s", |
| Integer.valueOf(storage))); |
| } |
| } |
| } |
| |
| /** |
| * <!ELEMENT description (#PCDATA)> |
| */ |
| private String extractDescription(Element descriptionNode) { |
| Assert.assertTrue(descriptionNode.getTagName().equals("description")); |
| return extractText(descriptionNode); |
| } |
| |
| /** |
| * <!ELEMENT unit (#PCDATA)> |
| */ |
| private String extractUnit(Element unitNode) { |
| Assert.assertTrue(unitNode.getTagName().equals("unit")); |
| return extractText(unitNode); |
| } |
| |
| private String extractText(Element element) { |
| Text text = (Text) element.getFirstChild(); |
| return ((text == null ? "" : text.getData())); |
| } |
| } |