From 53473c07b904dc97630d635dfac9d94c3c5c4f17 Mon Sep 17 00:00:00 2001 From: frank Date: Wed, 16 Jan 2013 16:27:21 -0800 Subject: [PATCH] CloudStack CLOUDSTACK-723 Enhanced baremetal servers support on Cisco UCS introduce an python etree like xml helper. Ok, this is not a new wheel. Frankly speaking, all Java XML API just suc**. there are two popular types of XML API in java, one class is for data binding, JAXB, XStream fall into this category. Another class is tree based, like JDOM, XOM ... for XML api call, data binding library is painful as you have to specify the schema that how xml stream converts to java object, which means you have to pre-define all schemas(xsd file for JAXB, java object for XStream ...). This is not productive, because you must add new schema when XML document grows. Tree based library shines in this case, for it's able to dynamically create an object tree from xml stream without any knowledge of its structure. However, all tree based XML API library fall into below convention: Element e = root.getChildElement("child1").getChildElement("child2").getChildElement("child3")...getChildElement("childN") anything wrong with it??? the sadness is if there is no "child2", you will get a NPE with above code, which means you have to judge before getting. And, why so verbose?? why not: Element e = root.child1.child2.child3...childN ??? Ok I am joking, it's impossible in Java the world knows Java is a static language. but you can actually do: Element e = root.get("child1.child2.child3"); or List e = root.getAsList("child1.child2.child3") this is known as XPath style(though XPATH use '/'), python etree has supported it. so I did this toy for my UCS xml api call, it's quite like etree which is easy to use, for example: XmlObject xo = XmlObjectParser.parseFromFile("~/components.xml.in"); List checkers = xo.getAsList("system-integrity-checker.checker"); then you get a list of XmlObject which represent each 'checker' element: XmlObject firstChecker = checkers.get(0); // firstChecker.get("name") == "ManagementServerNode" // firstChecker.get("class") == "com.cloud.cluster.ManagementServerNode" // firstChecker.getTag() == "checker" // firstChecker.getText() == "" if it's xxx, then getText() == "xxx" example 2: yout can do: XmlObject xo = XmlObjectParser.parseFromFile("~/components.xml.in"); XmlObject checker = xo.get("system-integrity-checker.checker"); then it returns a single object as we only have one "checker" in xml stream, or you still do List checkers = xo.getAsList("system-integrity-checker.checker"); it returns a list which only contains one element of "checker" if you do: XmlObject checker = xo.get("system-integrity-checker.checker.this_middle_element_doesnt_exist.some_element"); it returns a null without any exception, so you don't have to worry if a parent element is missing when getting a leaf element again it's not a new wheel, I just hate JAVA xml api --- .../exception/CloudRuntimeException.java | 6 + .../exception/RuntimeCloudException.java | 4 + .../com/cloud/utils/xmlobject/XmlObject.java | 117 ++++++++++++++++++ .../utils/xmlobject/XmlObjectParser.java | 107 ++++++++++++++++ .../cloud/utils/xmlobject/TestXmlObject.java | 29 +++++ 5 files changed, 263 insertions(+) mode change 100644 => 100755 utils/src/com/cloud/utils/exception/RuntimeCloudException.java create mode 100755 utils/src/com/cloud/utils/xmlobject/XmlObject.java create mode 100755 utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java create mode 100755 utils/test/com/cloud/utils/xmlobject/TestXmlObject.java diff --git a/utils/src/com/cloud/utils/exception/CloudRuntimeException.java b/utils/src/com/cloud/utils/exception/CloudRuntimeException.java index 78075947d3f..dabb715086f 100755 --- a/utils/src/com/cloud/utils/exception/CloudRuntimeException.java +++ b/utils/src/com/cloud/utils/exception/CloudRuntimeException.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.utils.exception; +import java.io.FileNotFoundException; + import com.cloud.utils.SerialVersionUID; /** @@ -36,4 +38,8 @@ public class CloudRuntimeException extends RuntimeCloudException { protected CloudRuntimeException() { super(); } + + public CloudRuntimeException(Throwable t) { + super(t); + } } diff --git a/utils/src/com/cloud/utils/exception/RuntimeCloudException.java b/utils/src/com/cloud/utils/exception/RuntimeCloudException.java old mode 100644 new mode 100755 index a2de5161596..422d66cfb17 --- a/utils/src/com/cloud/utils/exception/RuntimeCloudException.java +++ b/utils/src/com/cloud/utils/exception/RuntimeCloudException.java @@ -64,6 +64,10 @@ public class RuntimeCloudException extends RuntimeException { setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); } + public RuntimeCloudException(Throwable t) { + super(t); + } + public ArrayList getIdProxyList() { return idList; } diff --git a/utils/src/com/cloud/utils/xmlobject/XmlObject.java b/utils/src/com/cloud/utils/xmlobject/XmlObject.java new file mode 100755 index 00000000000..4ebf3718113 --- /dev/null +++ b/utils/src/com/cloud/utils/xmlobject/XmlObject.java @@ -0,0 +1,117 @@ +package com.cloud.utils.xmlobject; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.SAXException; + +import com.cloud.utils.exception.CloudRuntimeException; + +import edu.emory.mathcs.backport.java.util.Collections; + +public class XmlObject { + private Map elements = new HashMap(); + private String text; + private String tag; + + XmlObject() { + } + + XmlObject putElement(String key, Object e) { + Object old = elements.get(key); + if (old == null) { + System.out.println(String.format("no %s, add new", key)); + elements.put(key, e); + } else { + if (old instanceof List) { + System.out.println(String.format("already list %s, add", key)); + ((List)old).add(e); + } else { + System.out.println(String.format("not list list %s, add list", key)); + List lst = new ArrayList(); + lst.add(old); + lst.add(e); + elements.put(key, lst); + } + } + + return this; + } + + private Object recurGet(XmlObject obj, Iterator it) { + String key = it.next(); + Object e = obj.elements.get(key); + if (!it.hasNext()) { + return e; + } else { + if (!(e instanceof XmlObject)) { + throw new CloudRuntimeException(String.format("%s doesn't reference to a XmlObject", it.next())); + } + return recurGet((XmlObject) e, it); + } + } + + public T get(String elementStr) { + String[] strs = elementStr.split("\\."); + List lst = new ArrayList(strs.length); + Collections.addAll(lst, strs); + return (T)recurGet(this, lst.iterator()); + } + + public List getAsList(String elementStr) { + Object e = get(elementStr); + if (e instanceof List) { + return (List)e; + } + List lst = new ArrayList(1); + lst.add(e); + return lst; + } + + public String getText() { + return text; + } + + void setText(String text) { + this.text = text; + } + + public String getTag() { + return tag; + } + + void setTag(String tag) { + this.tag = tag; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("<" + tag); + for (Map.Entry e : elements.entrySet()) { + String key = e.getKey(); + Object value = e.getValue(); + if (!(value instanceof String)) { + continue; + } + sb.append(String.format(" %s=\"%s\"", key, value.toString())); + } + + if (text == null || "".equals(text.trim())) { + sb.append(" />"); + } else { + sb.append(">").append(text).append(String.format("", tag)); + } + return sb.toString(); + } +} diff --git a/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java b/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java new file mode 100755 index 00000000000..68a822f5429 --- /dev/null +++ b/utils/src/com/cloud/utils/xmlobject/XmlObjectParser.java @@ -0,0 +1,107 @@ +package com.cloud.utils.xmlobject; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Stack; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import com.cloud.utils.exception.CloudRuntimeException; + +public class XmlObjectParser { + final private InputStream is; + + private class XmlHandler extends DefaultHandler { + private Stack stack; + private String currentValue; + private XmlObject root; + + XmlHandler() { + stack = new Stack(); + } + + @Override + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + //System.out.println(String.format("startElement: namespaceURI:%s, localName:%s, qName:%s", namespaceURI, localName, qName)); + currentValue = null; + XmlObject obj = new XmlObject(); + for (int i=0; i lst = xo.get("management-server.adapters"); + for (XmlObject x : lst) { + List lst1 = x.getAsList("adapter"); + for (XmlObject y : lst1) { + p(y.toString()); + } + } + } + +}