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