• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.kenai.jbosh;
18 
19 import java.io.IOException;
20 import java.io.StringReader;
21 import java.lang.ref.SoftReference;
22 import java.util.logging.Level;
23 import java.util.logging.Logger;
24 import javax.xml.XMLConstants;
25 import org.xmlpull.v1.XmlPullParser;
26 import org.xmlpull.v1.XmlPullParserException;
27 import 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  */
34 final 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      */
parse(final String xml)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      */
getXmlPullParser()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 }
166