aboutsummaryrefslogtreecommitdiff
path: root/src/com/kenai/jbosh/BodyParserXmlPull.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/kenai/jbosh/BodyParserXmlPull.java')
-rw-r--r--src/com/kenai/jbosh/BodyParserXmlPull.java165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/com/kenai/jbosh/BodyParserXmlPull.java b/src/com/kenai/jbosh/BodyParserXmlPull.java
new file mode 100644
index 0000000..5f23b06
--- /dev/null
+++ b/src/com/kenai/jbosh/BodyParserXmlPull.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2009 Mike Cumings
+ *
+ * Licensed 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 com.kenai.jbosh;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.lang.ref.SoftReference;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.XMLConstants;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+/**
+ * Implementation of the BodyParser interface which uses the XmlPullParser
+ * API. When available, this API provides an order of magnitude performance
+ * improvement over the default SAX parser implementation.
+ */
+final class BodyParserXmlPull implements BodyParser {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOG =
+ Logger.getLogger(BodyParserXmlPull.class.getName());
+
+ /**
+ * Thread local to contain a XmlPullParser instance for each thread that
+ * attempts to use one. This allows us to gain an order of magnitude of
+ * performance as a result of not constructing parsers for each
+ * invocation while retaining thread safety.
+ */
+ private static final ThreadLocal<SoftReference<XmlPullParser>> XPP_PARSER =
+ new ThreadLocal<SoftReference<XmlPullParser>>() {
+ @Override protected SoftReference<XmlPullParser> initialValue() {
+ return new SoftReference<XmlPullParser>(null);
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+ // BodyParser interface methods:
+
+ /**
+ * {@inheritDoc}
+ */
+ public BodyParserResults parse(final String xml) throws BOSHException {
+ BodyParserResults result = new BodyParserResults();
+ Exception thrown;
+ try {
+ XmlPullParser xpp = getXmlPullParser();
+
+ xpp.setInput(new StringReader(xml));
+ int eventType = xpp.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Start tag: " + xpp.getName());
+ }
+ } else {
+ eventType = xpp.next();
+ continue;
+ }
+
+ String prefix = xpp.getPrefix();
+ if (prefix == null) {
+ prefix = XMLConstants.DEFAULT_NS_PREFIX;
+ }
+ String uri = xpp.getNamespace();
+ String localName = xpp.getName();
+ QName name = new QName(uri, localName, prefix);
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Start element: ");
+ LOG.finest(" prefix: " + prefix);
+ LOG.finest(" URI: " + uri);
+ LOG.finest(" local: " + localName);
+ }
+
+ BodyQName bodyName = AbstractBody.getBodyQName();
+ if (!bodyName.equalsQName(name)) {
+ throw(new IllegalStateException(
+ "Root element was not '" + bodyName.getLocalPart()
+ + "' in the '" + bodyName.getNamespaceURI()
+ + "' namespace. (Was '" + localName
+ + "' in '" + uri + "')"));
+ }
+
+ for (int idx=0; idx < xpp.getAttributeCount(); idx++) {
+ String attrURI = xpp.getAttributeNamespace(idx);
+ if (attrURI.length() == 0) {
+ attrURI = xpp.getNamespace(null);
+ }
+ String attrPrefix = xpp.getAttributePrefix(idx);
+ if (attrPrefix == null) {
+ attrPrefix = XMLConstants.DEFAULT_NS_PREFIX;
+ }
+ String attrLN = xpp.getAttributeName(idx);
+ String attrVal = xpp.getAttributeValue(idx);
+ BodyQName aqn = BodyQName.createWithPrefix(
+ attrURI, attrLN, attrPrefix);
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest(" Attribute: {" + attrURI + "}"
+ + attrLN + " = '" + attrVal + "'");
+ }
+ result.addBodyAttributeValue(aqn, attrVal);
+ }
+ break;
+ }
+ return result;
+ } catch (RuntimeException rtx) {
+ thrown = rtx;
+ } catch (XmlPullParserException xmlppx) {
+ thrown = xmlppx;
+ } catch (IOException iox) {
+ thrown = iox;
+ }
+ throw(new BOSHException("Could not parse body:\n" + xml, thrown));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Private methods:
+
+ /**
+ * Gets a XmlPullParser for use in parsing incoming messages.
+ *
+ * @return parser instance
+ */
+ private static XmlPullParser getXmlPullParser() {
+ SoftReference<XmlPullParser> ref = XPP_PARSER.get();
+ XmlPullParser result = ref.get();
+ if (result == null) {
+ Exception thrown;
+ try {
+ XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ result = factory.newPullParser();
+ ref = new SoftReference<XmlPullParser>(result);
+ XPP_PARSER.set(ref);
+ return result;
+ } catch (Exception ex) {
+ thrown = ex;
+ }
+ throw(new IllegalStateException(
+ "Could not create XmlPull parser", thrown));
+ } else {
+ return result;
+ }
+ }
+
+}