blob: 5f23b060eb1e15e053ba5c206763730948885463 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/*
2 * Copyright 2009 Mike Cumings
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.kenai.jbosh;
18
19import java.io.IOException;
20import java.io.StringReader;
21import java.lang.ref.SoftReference;
22import java.util.logging.Level;
23import java.util.logging.Logger;
24import javax.xml.XMLConstants;
25import org.xmlpull.v1.XmlPullParser;
26import org.xmlpull.v1.XmlPullParserException;
27import org.xmlpull.v1.XmlPullParserFactory;
28
29/**
30 * Implementation of the BodyParser interface which uses the XmlPullParser
31 * API. When available, this API provides an order of magnitude performance
32 * improvement over the default SAX parser implementation.
33 */
34final class BodyParserXmlPull implements BodyParser {
35
36 /**
37 * Logger.
38 */
39 private static final Logger LOG =
40 Logger.getLogger(BodyParserXmlPull.class.getName());
41
42 /**
43 * Thread local to contain a XmlPullParser instance for each thread that
44 * attempts to use one. This allows us to gain an order of magnitude of
45 * performance as a result of not constructing parsers for each
46 * invocation while retaining thread safety.
47 */
48 private static final ThreadLocal<SoftReference<XmlPullParser>> XPP_PARSER =
49 new ThreadLocal<SoftReference<XmlPullParser>>() {
50 @Override protected SoftReference<XmlPullParser> initialValue() {
51 return new SoftReference<XmlPullParser>(null);
52 }
53 };
54
55 ///////////////////////////////////////////////////////////////////////////
56 // BodyParser interface methods:
57
58 /**
59 * {@inheritDoc}
60 */
61 public BodyParserResults parse(final String xml) throws BOSHException {
62 BodyParserResults result = new BodyParserResults();
63 Exception thrown;
64 try {
65 XmlPullParser xpp = getXmlPullParser();
66
67 xpp.setInput(new StringReader(xml));
68 int eventType = xpp.getEventType();
69 while (eventType != XmlPullParser.END_DOCUMENT) {
70 if (eventType == XmlPullParser.START_TAG) {
71 if (LOG.isLoggable(Level.FINEST)) {
72 LOG.finest("Start tag: " + xpp.getName());
73 }
74 } else {
75 eventType = xpp.next();
76 continue;
77 }
78
79 String prefix = xpp.getPrefix();
80 if (prefix == null) {
81 prefix = XMLConstants.DEFAULT_NS_PREFIX;
82 }
83 String uri = xpp.getNamespace();
84 String localName = xpp.getName();
85 QName name = new QName(uri, localName, prefix);
86 if (LOG.isLoggable(Level.FINEST)) {
87 LOG.finest("Start element: ");
88 LOG.finest(" prefix: " + prefix);
89 LOG.finest(" URI: " + uri);
90 LOG.finest(" local: " + localName);
91 }
92
93 BodyQName bodyName = AbstractBody.getBodyQName();
94 if (!bodyName.equalsQName(name)) {
95 throw(new IllegalStateException(
96 "Root element was not '" + bodyName.getLocalPart()
97 + "' in the '" + bodyName.getNamespaceURI()
98 + "' namespace. (Was '" + localName
99 + "' in '" + uri + "')"));
100 }
101
102 for (int idx=0; idx < xpp.getAttributeCount(); idx++) {
103 String attrURI = xpp.getAttributeNamespace(idx);
104 if (attrURI.length() == 0) {
105 attrURI = xpp.getNamespace(null);
106 }
107 String attrPrefix = xpp.getAttributePrefix(idx);
108 if (attrPrefix == null) {
109 attrPrefix = XMLConstants.DEFAULT_NS_PREFIX;
110 }
111 String attrLN = xpp.getAttributeName(idx);
112 String attrVal = xpp.getAttributeValue(idx);
113 BodyQName aqn = BodyQName.createWithPrefix(
114 attrURI, attrLN, attrPrefix);
115 if (LOG.isLoggable(Level.FINEST)) {
116 LOG.finest(" Attribute: {" + attrURI + "}"
117 + attrLN + " = '" + attrVal + "'");
118 }
119 result.addBodyAttributeValue(aqn, attrVal);
120 }
121 break;
122 }
123 return result;
124 } catch (RuntimeException rtx) {
125 thrown = rtx;
126 } catch (XmlPullParserException xmlppx) {
127 thrown = xmlppx;
128 } catch (IOException iox) {
129 thrown = iox;
130 }
131 throw(new BOSHException("Could not parse body:\n" + xml, thrown));
132 }
133
134 ///////////////////////////////////////////////////////////////////////////
135 // Private methods:
136
137 /**
138 * Gets a XmlPullParser for use in parsing incoming messages.
139 *
140 * @return parser instance
141 */
142 private static XmlPullParser getXmlPullParser() {
143 SoftReference<XmlPullParser> ref = XPP_PARSER.get();
144 XmlPullParser result = ref.get();
145 if (result == null) {
146 Exception thrown;
147 try {
148 XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
149 factory.setNamespaceAware(true);
150 factory.setValidating(false);
151 result = factory.newPullParser();
152 ref = new SoftReference<XmlPullParser>(result);
153 XPP_PARSER.set(ref);
154 return result;
155 } catch (Exception ex) {
156 thrown = ex;
157 }
158 throw(new IllegalStateException(
159 "Could not create XmlPull parser", thrown));
160 } else {
161 return result;
162 }
163 }
164
165}