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 }