1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/*
2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Copyright 2009 Mike Cumings
3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Licensed under the Apache License, Version 2.0 (the "License");
5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * you may not use this file except in compliance with the License.
6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * You may obtain a copy of the License at
7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *   http://www.apache.org/licenses/LICENSE-2.0
9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Unless required by applicable law or agreed to in writing, software
11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * distributed under the License is distributed on an "AS IS" BASIS,
12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * See the License for the specific language governing permissions and
14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * limitations under the License.
15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpackage com.kenai.jbosh;
18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.ByteArrayInputStream;
20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.IOException;
21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.InputStream;
22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.lang.ref.SoftReference;
23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.logging.Level;
24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.logging.Logger;
25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport javax.xml.parsers.ParserConfigurationException;
26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport javax.xml.parsers.SAXParser;
27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport javax.xml.parsers.SAXParserFactory;
28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.xml.sax.Attributes;
29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.xml.sax.SAXException;
30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.xml.sax.helpers.DefaultHandler;
31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/**
33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Implementation of the BodyParser interface which uses the SAX API
34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * that is part of the JDK.  Due to the fact that we can cache and reuse
35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * SAXPArser instances, this has proven to be significantly faster than the
36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * use of the javax.xml.stream API introduced in Java 6 while simultaneously
37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * providing an implementation accessible to Java 5 users.
38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenfinal class BodyParserSAX implements BodyParser {
40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Logger.
43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final Logger LOG =
45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Logger.getLogger(BodyParserSAX.class.getName());
46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * SAX parser factory.
49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final SAXParserFactory SAX_FACTORY;
51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    static {
52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        SAX_FACTORY = SAXParserFactory.newInstance();
53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        SAX_FACTORY.setNamespaceAware(true);
54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        SAX_FACTORY.setValidating(false);
55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Thread local to contain a SAX parser instance for each thread that
59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * attempts to use one.  This allows us to gain an order of magnitude of
60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * performance as a result of not constructing parsers for each
61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * invocation while retaining thread safety.
62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final ThreadLocal<SoftReference<SAXParser>> PARSER =
64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        new ThreadLocal<SoftReference<SAXParser>>() {
65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            @Override protected SoftReference<SAXParser> initialValue() {
66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                return new SoftReference<SAXParser>(null);
67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        };
69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * SAX event handler class.
72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final class Handler extends DefaultHandler {
74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        private final BodyParserResults result;
75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        private final SAXParser parser;
76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        private String defaultNS = null;
77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        private Handler(SAXParser theParser, BodyParserResults results) {
79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            parser = theParser;
80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            result = results;
81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        /**
84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * {@inheritDoc}
85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         */
86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        @Override
87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        public void startElement(
88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                final String uri,
89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                final String localName,
90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                final String qName,
91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                final Attributes attributes) {
92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (LOG.isLoggable(Level.FINEST)) {
93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                LOG.finest("Start element: " + qName);
94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                LOG.finest("    URI: " + uri);
95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                LOG.finest("    local: " + localName);
96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            BodyQName bodyName = AbstractBody.getBodyQName();
99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // Make sure the first element is correct
100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (!(bodyName.getNamespaceURI().equals(uri)
101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    && bodyName.getLocalPart().equals(localName))) {
102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw(new IllegalStateException(
103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        "Root element was not '" + bodyName.getLocalPart()
104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        + "' in the '" + bodyName.getNamespaceURI()
105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        + "' namespace.  (Was '" + localName + "' in '" + uri
106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        + "')"));
107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // Read in the attributes, making sure to expand the namespaces
110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // as needed.
111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            for (int idx=0; idx < attributes.getLength(); idx++) {
112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                String attrURI = attributes.getURI(idx);
113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (attrURI.length() == 0) {
114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    attrURI = defaultNS;
115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                String attrLN = attributes.getLocalName(idx);
117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                String attrVal = attributes.getValue(idx);
118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (LOG.isLoggable(Level.FINEST)) {
119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    LOG.finest("    Attribute: {" + attrURI + "}"
120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            + attrLN + " = '" + attrVal + "'");
121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                BodyQName aqn = BodyQName.create(attrURI, attrLN);
124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                result.addBodyAttributeValue(aqn, attrVal);
125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            parser.reset();
128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        /**
131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * {@inheritDoc}
132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         *
133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * This implementation uses this event hook to keep track of the
134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * default namespace on the body element.
135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         */
136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        @Override
137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        public void startPrefixMapping(
138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                final String prefix,
139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                final String uri) {
140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (prefix.length() == 0) {
141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (LOG.isLoggable(Level.FINEST)) {
142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    LOG.finest("Prefix mapping: <DEFAULT> => " + uri);
143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                defaultNS = uri;
145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } else {
146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (LOG.isLoggable(Level.FINEST)) {
147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    LOG.info("Prefix mapping: " + prefix + " => " + uri);
148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    ///////////////////////////////////////////////////////////////////////////
154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    // BodyParser interface methods:
155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * {@inheritDoc}
158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public BodyParserResults parse(String xml) throws BOSHException {
160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        BodyParserResults result = new BodyParserResults();
161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Exception thrown;
162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            InputStream inStream = new ByteArrayInputStream(xml.getBytes());
164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            SAXParser parser = getSAXParser();
165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            parser.parse(inStream, new Handler(parser, result));
166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return result;
167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (SAXException saxx) {
168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            thrown = saxx;
169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (IOException iox) {
170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            thrown = iox;
171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        throw(new BOSHException("Could not parse body:\n" + xml, thrown));
173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    ///////////////////////////////////////////////////////////////////////////
176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    // Private methods:
177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Gets a SAXParser for use in parsing incoming messages.
180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return parser instance
182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static SAXParser getSAXParser() {
184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        SoftReference<SAXParser> ref = PARSER.get();
185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        SAXParser result = ref.get();
186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (result == null) {
187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Exception thrown;
188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                result = SAX_FACTORY.newSAXParser();
190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                ref = new SoftReference<SAXParser>(result);
191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                PARSER.set(ref);
192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                return result;
193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } catch (ParserConfigurationException ex) {
194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                thrown = ex;
195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } catch (SAXException ex) {
196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                thrown = ex;
197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalStateException(
199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    "Could not create SAX parser", thrown));
200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } else {
201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            result.reset();
202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return result;
203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
205d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
206d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen}
207