aboutsummaryrefslogtreecommitdiff
path: root/src/com/kenai/jbosh/BodyParserXmlPull.java
blob: 5f23b060eb1e15e053ba5c206763730948885463 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * 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;
        }
    }

}