/*
* 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;
}
}
}