001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.converter.jaxp;
018    
019    import java.io.*;
020    import java.security.AccessController;
021    import java.security.PrivilegedAction;
022    import java.util.concurrent.BlockingQueue;
023    import java.util.concurrent.LinkedBlockingQueue;
024    
025    import javax.xml.stream.XMLEventReader;
026    import javax.xml.stream.XMLEventWriter;
027    import javax.xml.stream.XMLInputFactory;
028    import javax.xml.stream.XMLOutputFactory;
029    import javax.xml.stream.XMLResolver;
030    import javax.xml.stream.XMLStreamException;
031    import javax.xml.stream.XMLStreamReader;
032    import javax.xml.stream.XMLStreamWriter;
033    import javax.xml.transform.Result;
034    import javax.xml.transform.Source;
035    import javax.xml.transform.dom.DOMResult;
036    import javax.xml.transform.dom.DOMSource;
037    
038    import org.apache.camel.Converter;
039    import org.apache.camel.Exchange;
040    import org.apache.camel.util.IOHelper;
041    import org.slf4j.Logger;
042    import org.slf4j.LoggerFactory;
043    
044    /**
045     * A converter of StAX objects
046     *
047     * @version 
048     */
049    @Converter
050    public class StaxConverter {
051        private static final transient Logger LOG = LoggerFactory.getLogger(XmlErrorListener.class);
052    
053        // TODO: do not use a cxf system property
054        // TODO: make higher default pool size as 20 is not much in high end systems
055        private static final BlockingQueue<XMLInputFactory> INPUT_FACTORY_POOL;
056        private static final BlockingQueue<XMLOutputFactory> OUTPUT_FACTORY_POOL;
057        static {
058            int i = 20;
059            try {
060                String s = AccessController.doPrivileged(new PrivilegedAction<String>() {
061                    @Override
062                    public String run() {
063                        return System.getProperty("org.apache.cxf.staxutils.pool-size", "-1");
064                    }
065                });
066                i = Integer.parseInt(s);
067            } catch (Throwable t) {
068                //ignore 
069                i = 20;
070            }
071            if (i <= 0) {
072                i = 20;
073            }
074            INPUT_FACTORY_POOL = new LinkedBlockingQueue<XMLInputFactory>(i);
075            OUTPUT_FACTORY_POOL = new LinkedBlockingQueue<XMLOutputFactory>(i);
076        }
077        
078        private XMLInputFactory inputFactory;
079        private XMLOutputFactory outputFactory;
080    
081        @Converter
082        public XMLEventWriter createXMLEventWriter(OutputStream out, Exchange exchange) throws XMLStreamException {
083            XMLOutputFactory factory = getOutputFactory();
084            try {
085                return factory.createXMLEventWriter(IOHelper.buffered(out), IOHelper.getCharsetName(exchange));
086            } finally {
087                returnXMLOutputFactory(factory);
088            }
089        }
090        
091        @Converter
092        public XMLEventWriter createXMLEventWriter(Writer writer) throws XMLStreamException {
093            XMLOutputFactory factory = getOutputFactory();
094            try {
095                return factory.createXMLEventWriter(IOHelper.buffered(writer));
096            } finally {
097                returnXMLOutputFactory(factory);
098            }
099        }
100    
101        @Converter
102        public XMLEventWriter createXMLEventWriter(Result result) throws XMLStreamException {
103            XMLOutputFactory factory = getOutputFactory();
104            try {
105                if (result instanceof DOMResult && !isWoodstox(factory)) {
106                    //FIXME - if not woodstox, this will likely not work well
107                    //likely should copy CXF's W3CDOM stuff
108                    LOG.info("DOMResult is known to have issues with {0}. We suggest using Woodstox",
109                             factory.getClass());
110                }
111                return factory.createXMLEventWriter(result);
112            } finally {
113                returnXMLOutputFactory(factory);
114            }
115        }
116        
117        @Converter
118        public XMLStreamWriter createXMLStreamWriter(OutputStream outputStream, Exchange exchange) throws XMLStreamException {
119            XMLOutputFactory factory = getOutputFactory();
120            try {
121                return factory.createXMLStreamWriter(IOHelper.buffered(outputStream), IOHelper.getCharsetName(exchange));
122            } finally {
123                returnXMLOutputFactory(factory);
124            }
125        }
126    
127        @Converter
128        public XMLStreamWriter createXMLStreamWriter(Writer writer) throws XMLStreamException {
129            XMLOutputFactory factory = getOutputFactory();
130            try {
131                return factory.createXMLStreamWriter(IOHelper.buffered(writer));
132            } finally {
133                returnXMLOutputFactory(factory);
134            }
135        }
136    
137        @Converter
138        public XMLStreamWriter createXMLStreamWriter(Result result) throws XMLStreamException {
139            XMLOutputFactory factory = getOutputFactory();
140            try {
141                if (result instanceof DOMResult && !isWoodstox(factory)) {
142                    //FIXME - if not woodstox, this will likely not work well
143                    //likely should copy CXF's W3CDOM stuff
144                    LOG.info("DOMResult is known to have issues with {0}. We suggest using Woodstox",
145                             factory.getClass());
146                }
147                return factory.createXMLStreamWriter(result);
148            } finally {
149                returnXMLOutputFactory(factory);
150            }
151        }
152        
153        /**
154         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
155         */
156        @Deprecated
157        public XMLStreamReader createXMLStreamReader(InputStream in) throws XMLStreamException {
158            XMLInputFactory factory = getInputFactory();
159            try {
160                return factory.createXMLStreamReader(IOHelper.buffered(in));
161            } finally {
162                returnXMLInputFactory(factory);
163            }
164        }
165        
166        @Converter
167        public XMLStreamReader createXMLStreamReader(InputStream in, Exchange exchange) throws XMLStreamException {
168            XMLInputFactory factory = getInputFactory();
169            try {
170                return factory.createXMLStreamReader(IOHelper.buffered(in), IOHelper.getCharsetName(exchange));
171            } finally {
172                returnXMLInputFactory(factory);
173            }
174        }
175    
176        @Converter
177        public XMLStreamReader createXMLStreamReader(File file, Exchange exchange) throws XMLStreamException, FileNotFoundException {
178            XMLInputFactory factory = getInputFactory();
179            try {
180                return factory.createXMLStreamReader(IOHelper.buffered(new FileInputStream(file)), IOHelper.getCharsetName(exchange));
181            } finally {
182                returnXMLInputFactory(factory);
183            }
184        }
185    
186        @Converter
187        public XMLStreamReader createXMLStreamReader(Reader reader) throws XMLStreamException {
188            XMLInputFactory factory = getInputFactory();
189            try {
190                return factory.createXMLStreamReader(IOHelper.buffered(reader));
191            } finally {
192                returnXMLInputFactory(factory);
193            }
194        }
195    
196        @Converter
197        public XMLStreamReader createXMLStreamReader(Source in) throws XMLStreamException {
198            XMLInputFactory factory = getInputFactory();
199            try {
200                if (in instanceof DOMSource && !isWoodstox(factory)) {
201                    //FIXME - if not woodstox, this will likely not work well
202                    //likely should copy CXF's W3CDOM stuff
203                    LOG.info("DOMSource is known to have issues with {0}. We suggest using Woodstox",
204                             factory.getClass());
205                }
206                return factory.createXMLStreamReader(in);
207            } finally {
208                returnXMLInputFactory(factory);
209            }
210        }
211    
212        @Converter
213        public XMLStreamReader createXMLStreamReader(String string) throws XMLStreamException {
214            XMLInputFactory factory = getInputFactory();
215            try {
216                return factory.createXMLStreamReader(new StringReader(string));
217            } finally {
218                returnXMLInputFactory(factory);
219            }
220        }
221        
222        /**
223         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
224         */
225        @Deprecated
226        public XMLEventReader createXMLEventReader(InputStream in) throws XMLStreamException {
227            XMLInputFactory factory = getInputFactory();
228            try {
229                return factory.createXMLEventReader(IOHelper.buffered(in));
230            } finally {
231                returnXMLInputFactory(factory);
232            }
233        }
234    
235        @Converter
236        public XMLEventReader createXMLEventReader(InputStream in, Exchange exchange) throws XMLStreamException {
237            XMLInputFactory factory = getInputFactory();
238            try {
239                return factory.createXMLEventReader(IOHelper.buffered(in), IOHelper.getCharsetName(exchange));
240            } finally {
241                returnXMLInputFactory(factory);
242            }
243        }
244    
245        @Converter
246        public XMLEventReader createXMLEventReader(File file, Exchange exchange) throws XMLStreamException, FileNotFoundException {
247            XMLInputFactory factory = getInputFactory();
248            try {
249                return factory.createXMLEventReader(IOHelper.buffered(new FileInputStream(file)), IOHelper.getCharsetName(exchange));
250            } finally {
251                returnXMLInputFactory(factory);
252            }
253        }
254    
255        @Converter
256        public XMLEventReader createXMLEventReader(Reader reader) throws XMLStreamException {
257            XMLInputFactory factory = getInputFactory();
258            try {
259                return factory.createXMLEventReader(IOHelper.buffered(reader));
260            } finally {
261                returnXMLInputFactory(factory);
262            }
263        }
264    
265        @Converter
266        public XMLEventReader createXMLEventReader(XMLStreamReader reader) throws XMLStreamException {
267            XMLInputFactory factory = getInputFactory();
268            try {
269                return factory.createXMLEventReader(reader);
270            } finally {
271                returnXMLInputFactory(factory);
272            }
273        }
274    
275        @Converter
276        public XMLEventReader createXMLEventReader(Source in) throws XMLStreamException {
277            XMLInputFactory factory = getInputFactory();
278            try {
279                if (in instanceof DOMSource && !isWoodstox(factory)) {
280                    //FIXME - if not woodstox, this will likely not work well
281                    LOG.info("DOMSource is known to have issues with {0}. We suggest using Woodstox",
282                             factory.getClass());
283                }
284                return factory.createXMLEventReader(in);
285            } finally {
286                returnXMLInputFactory(factory);
287            }
288        }
289    
290        private boolean isWoodstox(Object factory) {
291            return factory.getClass().getPackage().getName().startsWith("com.ctc.wstx");
292        }
293    
294        private XMLInputFactory getXMLInputFactory() {
295            XMLInputFactory f = INPUT_FACTORY_POOL.poll();
296            if (f == null) {
297                f = createXMLInputFactory(true);
298            }
299            return f;
300        }
301        
302        private void returnXMLInputFactory(XMLInputFactory factory) {
303            if (factory != inputFactory) {
304                INPUT_FACTORY_POOL.offer(factory);
305            }
306        }
307        
308        private XMLOutputFactory getXMLOutputFactory() {
309            XMLOutputFactory f = OUTPUT_FACTORY_POOL.poll();
310            if (f == null) {
311                f = XMLOutputFactory.newInstance();
312            }
313            return f;
314        }
315        
316        private void returnXMLOutputFactory(XMLOutputFactory factory) {
317            if (factory != outputFactory) {
318                OUTPUT_FACTORY_POOL.offer(factory);
319            }
320        }
321        
322        public static XMLInputFactory createXMLInputFactory(boolean nsAware) {
323            XMLInputFactory factory = XMLInputFactory.newInstance();
324            setProperty(factory, XMLInputFactory.IS_NAMESPACE_AWARE, nsAware);
325            setProperty(factory, XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
326            setProperty(factory, XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
327            setProperty(factory, XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
328            factory.setXMLResolver(new XMLResolver() {
329                public Object resolveEntity(String publicID, String systemID,
330                                            String baseURI, String namespace)
331                    throws XMLStreamException {
332                    throw new XMLStreamException("Reading external entities is disabled");
333                }
334            });
335            return factory;
336        }
337        
338        private static void setProperty(XMLInputFactory f, String p, Object o) {
339            try {
340                f.setProperty(p,  o);
341            } catch (Throwable t) {
342                //ignore
343            }
344        }
345        
346        // Properties
347        //-------------------------------------------------------------------------
348    
349        public XMLInputFactory getInputFactory() {
350            if (inputFactory == null) {
351                return getXMLInputFactory();
352            }
353            return inputFactory;
354        }
355    
356        public XMLOutputFactory getOutputFactory() {
357            if (outputFactory == null) {
358                return getXMLOutputFactory();
359            }
360            return outputFactory;
361        }
362        
363        public void setInputFactory(XMLInputFactory inputFactory) {
364            this.inputFactory = inputFactory;
365        }
366    
367        public void setOutputFactory(XMLOutputFactory outputFactory) {
368            this.outputFactory = outputFactory;
369        }
370    
371    }