User: hiram
Date: 00/12/12 16:34:05
Added: src/java/org/spydermq/xml XElement.java
XElementConsumer.java XElementException.java
XElementProducer.java XElementTester.java
Log:
The spyderMQ server now loads it's configuration via an XML file.
The XML file allows us to configure the server for multiple Invocation layers.
Revision Changes Path
1.1 spyderMQ/src/java/org/spydermq/xml/XElement.java
Index: XElement.java
===================================================================
/*
* spyderMQ, the OpenSource JMS implementation
*
* Distributable under GPL license.
* See terms of license at gnu.org.
*/
package org.spydermq.xml;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Hashtable;
import org.xml.sax.*;
import java.util.Iterator;
/**
* XElement provides an interface to an XML element.
*
* An XElement represents an XML element which contains:<br>
* <ul>
* <li> Name (required)
* <li> Attributes (optional)
* <li> character data (optional)
* <li> other elements (optional)
* </ul>
*
* <p>It is important to understand the diffrence between an "field"
* XElement and a non "field" XElement. If an XElement does not contain
* any sub elements, it is considered a "field" XElement.
*
* The <code>getField(String)</code> and <code>getValue()</code>
* functions will throw an XElementException if they are used on
* non "attribute" objects. This give you a little bit type checking
* (You'll get an exception if you try to access the character data of
* an element that has sub elements).
*
* <p>If XElement is not an field, then it contains other XElements
* and optionaly some text. The text data can be accessed with
* the <code>getText()</code> method and the sub elements with the
* <code>iterator()</code> or with <code>getElementXXX()</code> fuctions.
*
* Since XML and thus XElements provide a tree type data structure, traversing
* the tree to access leaf data can be cumbersom if you have a 'deep' tree.
* For example, you may have to do:
* <code>
* element.getElement("tree").getElement("branch").getElement("leaf")
* </code>
* access a XElement 3 levels deep in the tree.
*
* To access deep elements easier, XElements lets you use 'reletive' names
* to access deep elements.
* Using reletive names, you could access the same element in previous
* example doing:
* <code>
* element.getElement("tree/branch/leaf")
* </code>
*
* When using relative names, keep in mind that "." will get the current
* XElement, and ".." will get parent XElement. Very similar to how URLs work.
*
* @author Hiram Chirino ([EMAIL PROTECTED])
*
* @version $Revision: 1.1 $
*/
public class XElement {
private XElement parent = null;
private String name = null;
private Hashtable metadata = new Hashtable();
private Vector contents = new Vector();
private static final String nl = System.getProperty("line.separator");
/**
* Constructs an empty object.
* @param objectName the tag or element name that this object represents.
*/
public XElement(String objectName) {
if (objectName == null)
throw new NullPointerException();
name = objectName;
contents.addElement(new StringBuffer());
}
/**
* Constructs an XElement with it's parent and metatags set.
* @param objectName the tag or element name that this object represents.
* @param AttributeList list containing metatags to set.
*/
public XElement(String objectName, AttributeList atts) {
if (objectName == null)
throw new NullPointerException();
if (atts == null)
throw new NullPointerException();
name = objectName;
contents.addElement(new StringBuffer());
for (int i = 0; i < atts.getLength(); i++)
metadata.put(atts.getName(i), atts.getValue(i));
}
/**
* Adds and appends string data to the objects text.
* @param data.
*/
public void add(String data) {
((StringBuffer) contents.elementAt(0)).append(data);
}
private static String findAndReplace(String value, String searchStr, String
replaceStr) {
StringBuffer buffer = new StringBuffer(value.length());
while (value.length() > 0) {
int pos = value.indexOf(searchStr);
if (pos != -1) {
buffer.append(value.substring(0, pos));
buffer.append(replaceStr);
if (pos + searchStr.length() < value.length())
value = value.substring(pos +
searchStr.length());
else
value = "";
} else {
buffer.append(value);
value = "";
}
}
return buffer.toString();
}
/**
* Returns the value of a meta data value.
*
* @param objectName The name of the metadata item
* @returns the value of the metadata item, or "" if the item has not been set.
*/
public String getAttribute(String key) {
String t = (String) metadata.get(key);
if (t == null)
return "";
return t;
}
/**
* Returns the element name (tag name) of this XElement
*
* @returns
*/
public java.lang.String getName() {
return name;
}
/**
* Get the parent of this object, or the object the contains this one.
*
* @returns null if this object is not contained by any other XElement.
*/
public XElement getParent() {
return parent;
}
/**
* Gets the TRIMMED character data that was within this object.
* This differs from getValue() in that:
* <UL><LI>this fuction will work on attribute and non attribute XElements
* <LI>it will trim both ends of the character data before returning it.
* </UL>
* @returns the character data contained within this object.
*/
public String getText() {
return contents.elementAt(0).toString().trim();
}
/**
* Gets the character data that was within this object.
* This fuction can only be used on objects that are attributes.
* @returns the character data contained within this object.
* @throws XElementException if the object was not an attribute object
*/
public String getValue() throws XElementException {
if (!isField())
throw new XElementException("" + getName() + " is not an
attribute object");
return contents.elementAt(0).toString();
}
private static String metaValueEncode(String value) {
value = findAndReplace(value, "&", "&");
value = findAndReplace(value, "\"", """);
value = findAndReplace(value, "'", "'");
return utf8Encode(value);
}
/**
* Sets/Adds a metatag value
* Only metatags whose value is not empty will display when the
* <code>toString()</code> methods is called.
* @param key the name of the metatag
* @param value the value to set the metatag to.
*/
public void setAttribute(String key, String value) {
metadata.put(key, value);
}
/**
* Sets the object name
* @param newName
*/
public void setName(String newName) {
name = newName;
}
/**
* Gets the character data that was within this object.
* This fuction can only be used on objects that are attributes.
* @returns the character data contained within this object.
* @throws XElementException if the object was not an attribute object
*/
public void setValue(String value) throws XElementException {
if (!isField())
throw new XElementException("" + getName() + " is not an
attribute object");
contents.setElementAt(new StringBuffer(value), 0);
}
/**
* Serializes this object into a string.
*/
public String toString() {
return toString(0, true);
}
private static String utf8Encode(String value) {
try {
char buff[] = new char[value.length()];
value.getChars(0, buff.length, buff, 0);
sun.io.CharToByteUTF8 conv = new sun.io.CharToByteUTF8();
byte b[] = conv.convertAll(buff);
return new String(b);
} catch (sun.io.MalformedInputException e) {
return null;
}
}
private static String valueEncode(String value) {
value = findAndReplace(value, "&", "&");
value = findAndReplace(value, "<", "<");
value = findAndReplace(value, ">", ">");
return utf8Encode(value);
}
/**
* Adds an XElement to the set of XElements that are
* contained by this object.
* @param subObject
*/
public void addElement(XElement subObject) {
contents.addElement(subObject);
subObject.parent = this;
}
/**
* Adds an XElement to the set of XElements that are
* contained by this object.
* @param subObject
*/
public void addField(String key, String value) {
XElement subObject = new XElement(key);
subObject.add(value);
addElement(subObject);
}
/**
* Tests to see if this object contains the specified object.
*
* @param objectName The name of the object.
* @returns true if the object exits.
*/
public boolean containsElement(String objectName) {
try {
getElement(objectName);
return true;
} catch (XElementException e) {
return false;
}
}
/**
* Tests to see if this object contains the specified attribute object.
*
* @param objectName The name of the attribute object.
* @returns true if the attribute exits.
*/
public boolean containsField(String objectName) {
try {
XElement obj = getElement(objectName);
return obj.isField();
} catch (XElementException e) {
return false;
}
}
/**
* Returns the first object contained in this object named relativeName.
*
* @param relativeName The name of the object to find
* @returns the XElement named relativeName
* @throws XElementException if the object could not be found.
*/
public XElement getElement(String relativeName) throws XElementException {
if (relativeName == null)
throw new NullPointerException();
String names[] = {null, relativeName};
// Does the name have a "/" in it?
String split[] = splitFront(relativeName, "/");
if( split != null ) {
// was it an absolute name? (started with a '/')
if( split[0].length() == 0 ) {
// we are the parent
if( parent == null )
split[0] = null;
// Let my parent handle the request.
else
return parent.getElement(relativeName);
}
// did we have a trailing / in the name?
if( split[1].length() == 0 ) {
// For the case of "/",
if( split[0].equals(null) )
return this;
//otherwise it is an error
// to leave a trailing /, for example tree/leaf/
throw new XElementException("Invalid name (trailing
'/') : "+relativeName);
}
names = split;
}
if( names[0] == null ) {
for (int i = 1; i < contents.size(); i++) {
XElement o = (XElement) contents.elementAt(i);
if (relativeName.equals(o.getName())) {
return o;
}
}
} else {
if( names[0].equals(".") ) {
return getElement(names[1]);
} else if( names[0].equals("..") ) {
if( parent != null )
return parent.getElement(names[1]);
else
throw new XElementException("Invalid name
("+getName()+" has no parent) : "+relativeName);
} else {
for (int i = 1; i < contents.size(); i++) {
XElement o = (XElement) contents.elementAt(i);
if (relativeName.equals(o.getName())) {
return o.getElement(names[1]);
}
}
}
}
throw new XElementException("Invalid name ("+getName()+ " does not
contain the name) : " + relativeName);
}
/**
* Returns all the contained objects with the specified name.
*
* @param relativeName The name of the objects
* @returns whose name is relativeName;
*/
public Iterator getElementsNamed(String relativeName) {
Vector t = new Vector();
if (relativeName != null) {
String split[] = splitBack(relativeName, "/");
if( split != null ) {
try {
return
getElement(split[0]).getElementsNamed(split[1]);
} catch (XElementException e) {
return t.iterator(); // return an empty
enumeration
}
}
}
for (int i = 1; i < contents.size(); i++) {
XElement o = (XElement) contents.elementAt(i);
if (relativeName == null || relativeName.equals(o.getName())) {
t.addElement(o);
}
}
return t.iterator();
}
/**
* Gets the value of a contained attribute object.
*
* @param objectName The name of the attribute object.
* @returns the value of the attribute object.
* @throws XElementException if the object does not exist or if its not an
attribute object.
*/
public String getField(String objectName) throws XElementException {
return getElement(objectName).getValue();
}
/**
* Returns true if the object is an attribute object.
* An object is an attribute object if it does not contain any other
* objects.
* @returns true if the object is an attribute object.
*/
public boolean isField() {
return contents.size() == 1;
}
/**
* @returns an Enumeration of all the XElement conatained within this object.
*/
public Iterator iterator() {
return getElementsNamed(null);
}
/**
* Sets/Adds a attribute
* @param key the name of the attribute element
* @param value the value to set the attribute to.
*/
public void setField(String key, String value) throws XElementException {
getElement(key).setValue(value);
}
static private String[] splitBack(String string, String splitMarker) {
if (string == null || splitMarker==null )
throw new NullPointerException();
String front;
String back;
int pos = string.lastIndexOf(splitMarker);
if (pos == -1)
return null;
int l = splitMarker.length();
front = string.substring(0, pos);
if ( pos+l >= string.length() ) {
back = "";
} else {
back = string.substring(pos+l);
}
String rc[] = { front, back };
return rc;
}
static private String[] splitFront(String string, String splitMarker) {
if (string == null || splitMarker==null )
throw new NullPointerException();
String front;
String back;
int pos = string.indexOf(splitMarker);
if (pos == -1)
return null;
int l = splitMarker.length();
front = string.substring(0, pos);
if ( pos+l >= string.length() ) {
back = "";
} else {
back = string.substring(pos+l);
}
String rc[] = { front, back };
return rc;
}
/**
* Serializes this object into a string.
*
* @param nestingLevel how many tabs to prepend to output
*/
public String toString(int nestingLevel, boolean indent) {
try {
StringBuffer indentation = new StringBuffer();
StringBuffer rc = new StringBuffer();
if( indent ) {
for (int i = 0; i < nestingLevel; i++) {
indentation.append('\t');
}
}
rc.append(indentation.toString());
rc.append("<");
rc.append(getName());
Enumeration enum = metadata.keys();
while (enum.hasMoreElements()) {
String key = (String) enum.nextElement();
String value = (String) metadata.get(key);
rc.append(' ');
rc.append(key);
rc.append("=\"");
rc.append(metaValueEncode(value));
rc.append('"');
}
if (isField()) {
if (getValue().length() == 0) {
rc.append("/>");
rc.append(nl);
} else {
rc.append('>');
rc.append(valueEncode(getValue()));
rc.append("</");
rc.append(getName());
rc.append('>');
rc.append(nl);
}
} else {
rc.append('>');
rc.append(nl);
String text = getText();
if (text.length() > 0) {
rc.append(indentation.toString() + "\t");
rc.append(getText());
rc.append(nl);
}
for (int i = 1; i < contents.size(); i++) {
Object o = contents.elementAt(i);
rc.append(((XElement) o).toString(nestingLevel
+ 1, indent));
}
rc.append(indentation.toString());
rc.append("</");
rc.append(getName());
rc.append('>');
rc.append(nl);
}
return rc.toString();
} catch (XElementException e) {
// This should not occur!
e.printStackTrace();
System.exit(1);
return "";
}
}
/**
* Serializes this object into a XML document String.
*/
public String toXML(boolean indent) {
return
"<?xml version=\"1.0\" encoding=\"UTF8\"?>"+nl+
toString(0, indent);
}
/**
* Constructs an empty object.
* @param objectName the tag or element name that this object represents.
*/
static public XElement createFrom(java.io.InputStream is) throws
XElementException, java.io.IOException {
class MyRecordConsumer implements XElementConsumer {
XElement root = null;
public void documentEndEvent() {
}
public void documentStartEvent() {
}
public void recordReadEvent(XElement o) {
root = o;
}
}
MyRecordConsumer consumer = new MyRecordConsumer();
XElementProducer producer = new XElementProducer(consumer);
try {
producer.parse(is);
if (consumer.root == null)
throw new XElementException("No root element");
return consumer.root;
} catch (java.io.IOException e) {
throw e;
} catch (Exception e) {
throw new XElementException("Parse Error: " + e);
}
}
/**
* Constructs an empty object.
* @param objectName the tag or element name that this object represents.
*/
static public XElement createFrom(java.net.URL url) throws XElementException,
java.io.IOException {
class MyRecordConsumer implements XElementConsumer {
XElement root = null;
public void documentEndEvent() {
}
public void documentStartEvent() {
}
public void recordReadEvent(XElement o) {
root = o;
}
}
MyRecordConsumer consumer = new MyRecordConsumer();
XElementProducer producer = new XElementProducer(consumer);
try {
producer.parse(url);
if (consumer.root == null)
throw new XElementException("No root element");
return consumer.root;
} catch (java.io.IOException e) {
throw e;
} catch (Exception e) {
throw new XElementException("Parse Error: " + e);
}
}
}
1.1 spyderMQ/src/java/org/spydermq/xml/XElementConsumer.java
Index: XElementConsumer.java
===================================================================
/*
* spyderMQ, the OpenSource JMS implementation
*
* Distributable under GPL license.
* See terms of license at gnu.org.
*/
package org.spydermq.xml;
/**
* XMLRecordConsumer Interface defines the method signatures used
* to notify the consumer object of parsing errors, document starts,
* record reads, and document ends.
*
* @author Hiram Chirino ([EMAIL PROTECTED])
*
* @version $Revision: 1.1 $
*/
public interface XElementConsumer {
/**
* Signals that the END of the XML document has been reached.
*/
public void documentEndEvent() throws Exception;
/**
* Signals that the START of the XML document has been reached.
*/
public void documentStartEvent() throws Exception;
/**
* Signals that a record object, an xml element, has been fully read in.
*/
public void recordReadEvent( XElement o ) throws Exception;
}
1.1 spyderMQ/src/java/org/spydermq/xml/XElementException.java
Index: XElementException.java
===================================================================
/*
* spyderMQ, the OpenSource JMS implementation
*
* Distributable under GPL license.
* See terms of license at gnu.org.
*/
package org.spydermq.xml;
/**
* XMLObjectException is used to signal errors occuring inside of an
* XMLObject.
*
* @author Hiram Chirino ([EMAIL PROTECTED])
*
* @version $Revision: 1.1 $
*/
public class XElementException extends Exception {
public XElementException() {
super();
}
public XElementException(String s) {
super(s);
}
}
1.1 spyderMQ/src/java/org/spydermq/xml/XElementProducer.java
Index: XElementProducer.java
===================================================================
/*
* spyderMQ, the OpenSource JMS implementation
*
* Distributable under GPL license.
* See terms of license at gnu.org.
*/
package org.spydermq.xml;
import java.util.*;
import org.xml.sax.*;
/**
* XElementProducer parses and provides an XElementConsumer XElement data.
*
* <p>An XElementProducer is a SAX based XML parser. This class provides
* a hybrid of the Event Based and DOM based
* XML parsing models. This is done by telling the XElementProducer
* which elements signal the start of a record, and when to generat an
* XElement.
*
* As the "record" XML elements are encountered, XElements
* are produced. These XElements create an in memory DOM tree
* of the element and all sub elements.
*
* After the "record" element has be fully read in through the use of
* SAX events, the "record" element, now in the form of an XElement,
* is passed to an XElementConsumer for processing.
*
* @author Hiram Chirino ([EMAIL PROTECTED])
*
* @version $Revision: 1.1 $
*/
public class XElementProducer {
private XElementConsumer consumer;
private Vector targetRecords = new Vector();
private Handler handler = new Handler();
private Exception thrownError = null;
//
// INNER CLASS : Handler
// Used to handle for SAX events.
//
// If we get an element whose name is in targetRecords vector then
// we start a new root XElement and set currentXElement to this object.
// currentXElement is always the top element that is being processed.
// (He's working like the top pointer of a stack)
//
// As sub elements are encountered, they will be added to the currentXElement
// and then they become the currentXElement.
//
// When the end of an element is encountered, then currentXElement
// is set to the parent of currentXElement.
//
// Exception processing is a little trick, read on:
// An XElementConsumer is allowed to throw any kind of exception
// when processing records. But since the SAX event based parser only allows
// you to throw SAXExceptions from it's event handler methods, this class
// uses the thrownError variable to store the thrown event. A SAXException
// is then generated to stop the SAXParser and the XElementProducer.parse()
// method checks to see if the thrownError variable was set, if so, then
// it throws the exception stored in thrownError.
//
class Handler extends HandlerBase {
private XElement currentXElement;
public void startDocument() throws SAXException {
try {
consumer.documentStartEvent();
} catch (Exception e) {
thrownError = e;
throw new SAXException(e.toString());
}
}
public void endDocument() throws SAXException {
try {
consumer.documentEndEvent();
} catch (Exception e) {
thrownError = e;
throw new SAXException(e.toString());
}
}
public void startElement(String elementName, AttributeList atts)
throws SAXException {
if (currentXElement != null) {
XElement o = new XElement(elementName, atts);
currentXElement.addElement(o);
currentXElement = o;
} else {
if (targetRecords.size() == 0) {
currentXElement = new XElement(elementName,
atts);
} else {
for (int i = 0; i < targetRecords.size(); i++)
{
if
(elementName.equals(targetRecords.elementAt(i))) {
currentXElement = new
XElement(elementName, atts);
break;
}
}
}
}
}
public void endElement(String elementName) throws SAXException {
if (currentXElement != null) {
// Sanity Check :
if (!elementName.equals(currentXElement.getName()))
throw new SAXException("XElement parsing sanitity
check failed");
XElement t = currentXElement;
currentXElement = currentXElement.getParent();
if (currentXElement == null)
try {
consumer.recordReadEvent(t);
} catch (Exception e) {
thrownError = e;
throw new SAXException(e.toString());
}
}
}
public void characters(char[] chars, int start, int length) {
if (length == 0)
return;
if (currentXElement != null)
currentXElement.add(new String(chars, start, length));
}
}
/**
* The Constructor must be passed the consumer object.
*
* @param consumerObject the object that will process data produced from this
object.
*/
public XElementProducer(XElementConsumer consumerObject)
{
consumer = consumerObject;
}
/**
* Adds a name to the list of element names which when
* encountered, will produce an XElement record.
* @param name
*/
public void addElementRecord(String name)
{
targetRecords.addElement(name);
}
/**
* Clears all the previously added element record names.
*/
public void clearElementRecords()
{
targetRecords.removeAllElements();
}
/**
* Starts parsing a file.
*
* As "record" elements are encountered, record XElements are produced and
* given to the XElementConsumer to process.
*
* @param fileName the absolute or relative path to a file on the local file
system.
* @throws Exception includes IO errors, parse errors, or Exceptions thrown
from the RecordConsumer.
*/
public void parse(java.io.InputStream is) throws Exception
{
if (consumer == null)
throw new NullPointerException();
try
{
Parser parser = new com.sun.xml.parser.Parser();
parser.setDocumentHandler(handler);
if ( consumer instanceof org.xml.sax.ErrorHandler )
parser.setErrorHandler((org.xml.sax.ErrorHandler)consumer);
thrownError = null;
parser.parse(new InputSource(is));
}
catch (SAXException e)
{
if (thrownError != null)
throw thrownError;
else
throw e;
}
}
/**
* Starts parsing a file.
*
* As "record" elements are encountered, record XElements are produced and
* given to the XElementConsumer to process.
*
* @param fileName the absolute or relative path to a file on the local file
system.
* @throws Exception includes IO errors, parse errors, or Exceptions thrown
from the RecordConsumer.
*/
public void parse(java.net.URL url) throws Exception
{
if (consumer == null)
throw new NullPointerException();
try
{
Parser parser = new com.sun.xml.parser.Parser();
parser.setDocumentHandler(handler);
if ( consumer instanceof org.xml.sax.ErrorHandler )
parser.setErrorHandler((org.xml.sax.ErrorHandler)consumer);
thrownError = null;
parser.parse(url.toExternalForm());
}
catch (SAXException e)
{
if (thrownError != null)
throw thrownError;
else
throw e;
}
}
}
1.1 spyderMQ/src/java/org/spydermq/xml/XElementTester.java
Index: XElementTester.java
===================================================================
/*
* spyderMQ, the OpenSource JMS implementation
*
* Distributable under GPL license.
* See terms of license at gnu.org.
*/
package org.spydermq.xml;
/**
* This class is for a basic unit test of the XElement class
*
* @author Hiram Chirino ([EMAIL PROTECTED])
*
* @version $Revision: 1.1 $
*/
public class XElementTester {
/**
* Starts the application.
* @param args an array of command-line arguments
*/
public static void main(java.lang.String[] args) throws Exception {
XElement o = XElement.createFrom(new java.net.URL("file:" + new
java.io.File(args[0]).getAbsolutePath()) );
System.out.println("Result="+o);
}
}