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 }