/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.jmx;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import static javax.xml.xpath.XPathConstants.NODE;
import static javax.xml.xpath.XPathConstants.NODESET;
/**
* <p>Title: XMLHelper</p>
* <p>Description: XML parsing helper</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.jmx.XMLHelper</code></p>
*/
public class XMLHelper {
static {
String xmlParser = System.getProperty("org.xml.sax.parser");
if(xmlParser == null) {
System.setProperty("org.xml.sax.parser", "com.sun.org.apache.xerces.internal.parsers.SAXParser");
}
}
/**
* Searches throgh the passed NamedNodeMap for an attribute and returns it if it is found.
* If it is not found, returns a null.
* @param nnm NamedNodeMap
* @param name String
* @return String
*/
public static String getAttributeValueByName(NamedNodeMap nnm, String name) {
for(int i = 0; i < nnm.getLength(); i++) {
Attr attr = (Attr)nnm.item(i);
if(attr.getName().equalsIgnoreCase(name)) {
return attr.getValue();
}
}
return null;
}
/**
* Returns the attribute value for the passed name in the passed node.
* @param node the node to get the attribute from
* @param name the name of the attribute
* @param defaultValue the value to return if the node did not contain the attribute
* @return The attribute value or the default value if it is not found.
*/
public static String getAttributeByName(Node node, String name, String defaultValue) {
try {
String val = getAttributeValueByName(node, name);
if(val!=null && !val.trim().isEmpty()) return val.trim();
} catch (Exception e) {}
return defaultValue;
}
/**
* Returns the long attribute value for the passed name in the passed node.
* @param node the node to get the attribute from
* @param name the name of the attribute
* @param defaultValue the value to return if the node did not contain the attribute
* @return The attribute value or the default value if it is not found.
*/
public static long getLongAttributeByName(Node node, String name, long defaultValue) {
String s = getAttributeByName(node, name, null);
if(s==null) return defaultValue;
try {
return Long.parseLong(s.trim());
} catch (Exception e) {
return defaultValue;
}
}
/**
* Returns the attribute value for the passed name in the passed node.
* @param node the node to get the attribute from
* @param name the name of the attribute
* @return The attribute value or null if it is not found.
*/
public static String getAttributeValueByName(Node node, String name) {
return getAttributeValueByName(node.getAttributes(), name);
}
/**
* Searches throgh the passed NamedNodeMap for an attribute. If it is found, it will try to convert it to a boolean.
* @param nnm NamedNodeMap
* @param name String
* @throws RuntimeException on any failure to parse a boolean
* @return boolean
*/
public static boolean getAttributeBooleanByName(NamedNodeMap nnm, String name) throws RuntimeException {
for(int i = 0; i < nnm.getLength(); i++) {
Attr attr = (Attr)nnm.item(i);
if(attr.getName().equalsIgnoreCase(name)) {
String tmp = attr.getValue().toLowerCase();
if(tmp.equalsIgnoreCase("true")) return true;
if(tmp.equalsIgnoreCase("false")) return false;
throw new RuntimeException("Attribute " + name + " value not boolean:" + tmp);
}
}
throw new RuntimeException("Attribute " + name + " not found.");
}
/**
* Returns the value of a named node attribute in the form of a boolean
* @param node The node to retrieve the attribute from
* @param name The name of the attribute
* @param defaultValue The default value if the attribute cannot be located or converted.
* @return true or false
*/
public static boolean getAttributeByName(Node node, String name, boolean defaultValue) {
if(node==null || name==null) return defaultValue;
try {
return getAttributeBooleanByName(node.getAttributes(), name);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Returns the value of a named node attribute in the form of an int
* @param node The node to retrieve the attribute from
* @param name The name of the attribute
* @param defaultValue The default value if the attribute cannot be located or converted.
* @return an int
*/
public static int getAttributeByName(Node node, String name, int defaultValue) {
if(node==null || name==null) return defaultValue;
try {
return new Double(getAttributeValueByName(node, name).trim()).intValue();
} catch (Exception e) {
return defaultValue;
}
}
/**
* Returns the value of a named node attribute in the form of a long
* @param node The node to retrieve the attribute from
* @param name The name of the attribute
* @param defaultValue The default value if the attribute cannot be located or converted.
* @return a long
*/
public static long getAttributeByName(Node node, String name, long defaultValue) {
if(node==null || name==null) return defaultValue;
try {
return new Double(getAttributeValueByName(node, name).trim()).longValue();
} catch (Exception e) {
return defaultValue;
}
}
/**
* Helper Method. Searches through the child nodes of a node and returns the first node with a matching name.
* Do we need this ?
* @param element Element
* @param name String
* @param caseSensitive boolean
* @return Node
*/
public static Node getChildNodeByName(Node element, String name, boolean caseSensitive) {
NodeList list = element.getChildNodes();
for(int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if(caseSensitive) {
if(node.getNodeName().equals(name)) return node;
} else {
if(node.getNodeName().equalsIgnoreCase(name)) return node;
}
}
return null;
}
/**
* Helper Method. Searches through the child nodes of an element and returns an array of the matching nodes.
* @param element Element
* @param name String
* @param caseSensitive boolean
* @return ArrayList
*/
public static List<Node> getChildNodesByName(Node element, String name, boolean caseSensitive) {
ArrayList<Node> nodes = new ArrayList<Node>();
NodeList list = element.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (caseSensitive) {
if (node.getNodeName().equals(name)) nodes.add(node);
}
else {
if (node.getNodeName().equalsIgnoreCase(name)) nodes.add(node);
}
}
return nodes;
}
/**
* Parses an input source and generates an XML document.
* @param is An input source to an XML source.
* @return An XML doucument.
*/
public static Document parseXML(InputSource is) {
try {
Document doc = null;
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
doc = documentBuilder.parse(is);
return doc;
} catch (Exception e) {
throw new RuntimeException("Failed to parse XML source", e);
}
}
/**
* Parses an input stream and generates an XML document.
* @param is An input stream to an XML source.
* @return An XML doucument.
*/
public static Document parseXML(InputStream is) {
return parseXML(new InputSource(is));
}
/**
* Parses a file and generates an XML document.
* @param file
* @return An XML doucument.
*/
public static Document parseXML(File file) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
return parseXML(fis);
} catch (Exception e) {
throw new RuntimeException("Failed to open XML file:" + file, e);
} finally {
try { fis.close(); } catch (Exception e) {}
}
}
/**
* Parses the input stream of a URL and generates an XML document.
* @param xmlUrl The URL of the XML document.
* @return The parsed document.
*/
public static Document parseXML(URL xmlUrl) {
InputStream is = null;
BufferedInputStream bis = null;
try {
is = xmlUrl.openConnection().getInputStream();
bis = new BufferedInputStream(is);
return parseXML(bis);
} catch (Exception e) {
throw new RuntimeException("Failed to read XML URL:" + xmlUrl, e);
} finally {
try {bis.close();} catch (Exception e) {}
try {is.close();} catch (Exception e) {}
}
}
/**
* Parses an XML string and generates an XML document.
* @param xml The XML to parse
* @return An XML doucument.
*/
public static Document parseXML(CharSequence xml) {
StringReader sr = new StringReader(xml.toString());
return parseXML(new InputSource(sr));
}
/**
* Renders an XML node to a string
* @param node The xml node to render
* @return the rendered string or null if it failed conversion
*/
public static String renderNode(Node node) {
if(node==null) return null;
try {
StringWriter writer = new StringWriter();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(node), new StreamResult(writer));
return writer.toString();
} catch (Throwable e) {
return null;
}
}
/**
* Writes an element out to a file.
* @param element The XML element to write out.
* @param fileName The file name to write to. Existing file is overwriten.
*/
public static void writeElement(Element element, String fileName) {
File file = new File(fileName);
file.delete();
DOMSource domSource = new DOMSource(element);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
StreamResult result = new StreamResult(fos);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(domSource, result);
fos.flush();
fos.close();
} catch (Exception e) {
throw new RuntimeException("Failed to write XML element to:" + fileName, e);
} finally {
try { fos.flush(); } catch (Exception e) {}
try { fos.close(); } catch (Exception e) {}
}
}
/**
* Locates the attribute defined by the XPath expression in the XML file and replaces it with the passed value.
* @param fileName The XML file to update.
* @param xPathExpression An XPath expression that locates the attribute to update.
* @param attributeName The name of the attribute to update.
* @param value The value to update the attribute to.
*/
public static void updateAttributeInXMLFile(String fileName, String xPathExpression, String attributeName, String value) {
try {
Document document = parseXML(new File(fileName));
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression xpathExpression = xpath.compile(xPathExpression);
Element element = (Element)xpathExpression.evaluate(document, NODE);
element.getAttributeNode(attributeName).setValue(value);
writeElement(document.getDocumentElement(), fileName);
} catch (Exception e) {
throw new RuntimeException("Failed to extract element from:" + fileName, e);
}
}
public static String getNodeTextValue(Node node) {
return node.getFirstChild().getNodeValue();
}
/**
* Locates the element defined by the XPath expression in the XML file and replaces the child text with the passed value.
* @param fileName The XML file to update.
* @param xPathExpression An XPath expression that locates the element to update.
* @param value The value to update the attribute to.
*/
public static void updateElementValueInXMLFile(String fileName, String xPathExpression, String value) {
try {
Document document = parseXML(new File(fileName));
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression xpathExpression = xpath.compile(xPathExpression);
Element element = (Element)xpathExpression.evaluate(document, NODE);
element.getFirstChild().setNodeValue(value);
writeElement(document.getDocumentElement(), fileName);
} catch (Exception e) {
throw new RuntimeException("Failed to extract element from:" + fileName, e);
}
}
public static String getAttribute(String fileName, String xPathExpression, String attributeName) {
try {
Document document = parseXML(new File(fileName));
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression xpathExpression = xpath.compile(xPathExpression);
Node node = (Node)xpathExpression.evaluate(document, NODE);
return getAttributeValueByName(node, attributeName);
} catch (Exception e) {
throw new RuntimeException("Failed to extract element from:" + fileName, e);
}
}
public static void main(String[] args) {
log("XMLHelper");
if(args.length < 1) return;
if(args[0].equalsIgnoreCase("updateAttributeInXMLFile")) {
if(args.length != 5) {
System.err.println("Invalid Argument Count. Usage: updateAttributeInXMLFile <fileName> <xpath> <attributeName> <newValue>");
}
updateAttributeInXMLFile(args[1], args[2], args[3], args[4]);
} else if(args[0].equalsIgnoreCase("updateElementValueInXMLFile")) {
if(args.length != 4) {
System.err.println("Invalid Argument Count. Usage: updateElementValueInXMLFile <fileName> <xpath> <newValue>");
}
updateElementValueInXMLFile(args[1], args[2], args[3]);
}
}
public static void log(Object message) {
System.out.println(message);
}
/**
* Uses the passed XPath expression to locate a set of nodes in the passed element.
* @param targetNode The node to search.
* @param expression The XPath expression.
* @return A list of located nodes.
*/
public static List<Node> xGetNodes(Node targetNode, String expression) {
List<Node> nodes = new ArrayList<Node>();
XPath xpath = null;
try {
xpath = XPathFactory.newInstance().newXPath();
XPathExpression xpathExpression = xpath.compile(expression);
NodeList nodeList = (NodeList)xpathExpression.evaluate(targetNode, NODESET);
if(nodeList!=null) {
for(int i = 0; i < nodeList.getLength(); i++) {
nodes.add(nodeList.item(i));
}
}
return nodes;
} catch (Exception e) {
throw new RuntimeException("XPath:Failed to locate the nodes:" + expression, e);
}
}
/**
* Uses the passed XPath expression to locate a single node in the passed element.
* @param targetNode The node to search.
* @param expression The XPath expression.
* @return The located node or null if one is not found.
*/
public static Node xGetNode(Node targetNode, String expression) {
Node node = null;
XPath xpath = null;
try {
xpath = XPathFactory.newInstance().newXPath();
XPathExpression xpathExpression = xpath.compile(expression);
node = (Node)xpathExpression.evaluate(targetNode, NODE);
return node;
} catch (Exception e) {
throw new RuntimeException("XPath:Failed to locate the node:" + expression, e);
}
}
/**
* Converts a node to a string.
* @param node The node to convert.
* @return A string representation of the node.
*/
public static String getStringFromNode(Node node) {
DOMSource domSource = new DOMSource(node);
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
StreamResult result = new StreamResult(baos);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(domSource, result);
baos.flush();
return new String(baos.toByteArray());
} catch (Exception e) {
throw new RuntimeException("Failed to stream node to string", e);
} finally {
try { baos.close(); } catch (Exception e) {}
}
}
}