/* * 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> XPP_PARSER = new ThreadLocal>() { @Override protected SoftReference initialValue() { return new SoftReference(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 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(result); XPP_PARSER.set(ref); return result; } catch (Exception ex) { thrown = ex; } throw(new IllegalStateException( "Could not create XmlPull parser", thrown)); } else { return result; } } }