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 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 String charsetName = IOHelper.getCharsetName(exchange, false); 171 if (charsetName == null) { 172 return factory.createXMLStreamReader(IOHelper.buffered(in)); 173 } else { 174 return factory.createXMLStreamReader(IOHelper.buffered(in), charsetName); 175 } 176 } finally { 177 returnXMLInputFactory(factory); 178 } 179 } 180 181 @Converter 182 public XMLStreamReader createXMLStreamReader(File file, Exchange exchange) throws XMLStreamException, FileNotFoundException { 183 XMLInputFactory factory = getInputFactory(); 184 try { 185 return factory.createXMLStreamReader(IOHelper.buffered(new FileInputStream(file)), IOHelper.getCharsetName(exchange)); 186 } finally { 187 returnXMLInputFactory(factory); 188 } 189 } 190 191 @Converter 192 public XMLStreamReader createXMLStreamReader(Reader reader) throws XMLStreamException { 193 XMLInputFactory factory = getInputFactory(); 194 try { 195 return factory.createXMLStreamReader(IOHelper.buffered(reader)); 196 } finally { 197 returnXMLInputFactory(factory); 198 } 199 } 200 201 @Converter 202 public XMLStreamReader createXMLStreamReader(Source in) throws XMLStreamException { 203 XMLInputFactory factory = getInputFactory(); 204 try { 205 if (in instanceof DOMSource && !isWoodstox(factory)) { 206 //FIXME - if not woodstox, this will likely not work well 207 //likely should copy CXF's W3CDOM stuff 208 LOG.info("DOMSource is known to have issues with {0}. We suggest using Woodstox", 209 factory.getClass()); 210 } 211 return factory.createXMLStreamReader(in); 212 } finally { 213 returnXMLInputFactory(factory); 214 } 215 } 216 217 @Converter 218 public XMLStreamReader createXMLStreamReader(String string) throws XMLStreamException { 219 XMLInputFactory factory = getInputFactory(); 220 try { 221 return factory.createXMLStreamReader(new StringReader(string)); 222 } finally { 223 returnXMLInputFactory(factory); 224 } 225 } 226 227 /** 228 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 229 */ 230 @Deprecated 231 public XMLEventReader createXMLEventReader(InputStream in) throws XMLStreamException { 232 XMLInputFactory factory = getInputFactory(); 233 try { 234 return factory.createXMLEventReader(IOHelper.buffered(in)); 235 } finally { 236 returnXMLInputFactory(factory); 237 } 238 } 239 240 @Converter 241 public XMLEventReader createXMLEventReader(InputStream in, Exchange exchange) throws XMLStreamException { 242 XMLInputFactory factory = getInputFactory(); 243 try { 244 String charsetName = IOHelper.getCharsetName(exchange, false); 245 if (charsetName == null) { 246 return factory.createXMLEventReader(IOHelper.buffered(in)); 247 } else { 248 return factory.createXMLEventReader(IOHelper.buffered(in), charsetName); 249 } 250 } finally { 251 returnXMLInputFactory(factory); 252 } 253 } 254 255 @Converter 256 public XMLEventReader createXMLEventReader(File file, Exchange exchange) throws XMLStreamException, FileNotFoundException { 257 XMLInputFactory factory = getInputFactory(); 258 try { 259 return factory.createXMLEventReader(IOHelper.buffered(new FileInputStream(file)), IOHelper.getCharsetName(exchange)); 260 } finally { 261 returnXMLInputFactory(factory); 262 } 263 } 264 265 @Converter 266 public XMLEventReader createXMLEventReader(Reader reader) throws XMLStreamException { 267 XMLInputFactory factory = getInputFactory(); 268 try { 269 return factory.createXMLEventReader(IOHelper.buffered(reader)); 270 } finally { 271 returnXMLInputFactory(factory); 272 } 273 } 274 275 @Converter 276 public XMLEventReader createXMLEventReader(XMLStreamReader reader) throws XMLStreamException { 277 XMLInputFactory factory = getInputFactory(); 278 try { 279 return factory.createXMLEventReader(reader); 280 } finally { 281 returnXMLInputFactory(factory); 282 } 283 } 284 285 @Converter 286 public XMLEventReader createXMLEventReader(Source in) throws XMLStreamException { 287 XMLInputFactory factory = getInputFactory(); 288 try { 289 if (in instanceof DOMSource && !isWoodstox(factory)) { 290 //FIXME - if not woodstox, this will likely not work well 291 LOG.info("DOMSource is known to have issues with {0}. We suggest using Woodstox", 292 factory.getClass()); 293 } 294 return factory.createXMLEventReader(in); 295 } finally { 296 returnXMLInputFactory(factory); 297 } 298 } 299 300 private boolean isWoodstox(Object factory) { 301 return factory.getClass().getPackage().getName().startsWith("com.ctc.wstx"); 302 } 303 304 private XMLInputFactory getXMLInputFactory() { 305 XMLInputFactory f = INPUT_FACTORY_POOL.poll(); 306 if (f == null) { 307 f = createXMLInputFactory(true); 308 } 309 return f; 310 } 311 312 private void returnXMLInputFactory(XMLInputFactory factory) { 313 if (factory != inputFactory) { 314 INPUT_FACTORY_POOL.offer(factory); 315 } 316 } 317 318 private XMLOutputFactory getXMLOutputFactory() { 319 XMLOutputFactory f = OUTPUT_FACTORY_POOL.poll(); 320 if (f == null) { 321 f = XMLOutputFactory.newInstance(); 322 } 323 return f; 324 } 325 326 private void returnXMLOutputFactory(XMLOutputFactory factory) { 327 if (factory != outputFactory) { 328 OUTPUT_FACTORY_POOL.offer(factory); 329 } 330 } 331 332 public static XMLInputFactory createXMLInputFactory(boolean nsAware) { 333 XMLInputFactory factory = XMLInputFactory.newInstance(); 334 setProperty(factory, XMLInputFactory.IS_NAMESPACE_AWARE, nsAware); 335 setProperty(factory, XMLInputFactory.SUPPORT_DTD, Boolean.FALSE); 336 setProperty(factory, XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE); 337 setProperty(factory, XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); 338 factory.setXMLResolver(new XMLResolver() { 339 public Object resolveEntity(String publicID, String systemID, 340 String baseURI, String namespace) 341 throws XMLStreamException { 342 throw new XMLStreamException("Reading external entities is disabled"); 343 } 344 }); 345 return factory; 346 } 347 348 private static void setProperty(XMLInputFactory f, String p, Object o) { 349 try { 350 f.setProperty(p, o); 351 } catch (Throwable t) { 352 //ignore 353 } 354 } 355 356 // Properties 357 //------------------------------------------------------------------------- 358 359 public XMLInputFactory getInputFactory() { 360 if (inputFactory == null) { 361 return getXMLInputFactory(); 362 } 363 return inputFactory; 364 } 365 366 public XMLOutputFactory getOutputFactory() { 367 if (outputFactory == null) { 368 return getXMLOutputFactory(); 369 } 370 return outputFactory; 371 } 372 373 public void setInputFactory(XMLInputFactory inputFactory) { 374 this.inputFactory = inputFactory; 375 } 376 377 public void setOutputFactory(XMLOutputFactory outputFactory) { 378 this.outputFactory = outputFactory; 379 } 380 381 }