Thu, 31 Aug 2017 15:18:52 +0800
merge
aoqi@0 | 1 | /* |
aoqi@0 | 2 | * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. |
aoqi@0 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
aoqi@0 | 4 | * |
aoqi@0 | 5 | * This code is free software; you can redistribute it and/or modify it |
aoqi@0 | 6 | * under the terms of the GNU General Public License version 2 only, as |
aoqi@0 | 7 | * published by the Free Software Foundation. Oracle designates this |
aoqi@0 | 8 | * particular file as subject to the "Classpath" exception as provided |
aoqi@0 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
aoqi@0 | 10 | * |
aoqi@0 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
aoqi@0 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
aoqi@0 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
aoqi@0 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
aoqi@0 | 15 | * accompanied this code). |
aoqi@0 | 16 | * |
aoqi@0 | 17 | * You should have received a copy of the GNU General Public License version |
aoqi@0 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
aoqi@0 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
aoqi@0 | 20 | * |
aoqi@0 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
aoqi@0 | 22 | * or visit www.oracle.com if you need additional information or have any |
aoqi@0 | 23 | * questions. |
aoqi@0 | 24 | */ |
aoqi@0 | 25 | |
aoqi@0 | 26 | package com.sun.xml.internal.ws.api.streaming; |
aoqi@0 | 27 | |
aoqi@0 | 28 | import com.sun.istack.internal.NotNull; |
aoqi@0 | 29 | import com.sun.istack.internal.Nullable; |
aoqi@0 | 30 | import com.sun.xml.internal.ws.streaming.XMLReaderException; |
aoqi@0 | 31 | import com.sun.xml.internal.ws.util.xml.XmlUtil; |
aoqi@0 | 32 | import org.xml.sax.InputSource; |
aoqi@0 | 33 | |
aoqi@0 | 34 | import javax.xml.stream.XMLInputFactory; |
aoqi@0 | 35 | import javax.xml.stream.XMLStreamException; |
aoqi@0 | 36 | import javax.xml.stream.XMLStreamReader; |
aoqi@0 | 37 | import java.io.IOException; |
aoqi@0 | 38 | import java.io.InputStream; |
aoqi@0 | 39 | import java.io.InputStreamReader; |
aoqi@0 | 40 | import java.io.Reader; |
aoqi@0 | 41 | import java.io.StringReader; |
aoqi@0 | 42 | import java.io.UnsupportedEncodingException; |
aoqi@0 | 43 | import java.lang.reflect.InvocationTargetException; |
aoqi@0 | 44 | import java.lang.reflect.Method; |
aoqi@0 | 45 | import java.net.URL; |
aoqi@0 | 46 | import java.security.AccessController; |
aoqi@0 | 47 | import java.util.logging.Level; |
aoqi@0 | 48 | import java.util.logging.Logger; |
aoqi@0 | 49 | |
aoqi@0 | 50 | import com.sun.xml.internal.ws.resources.StreamingMessages; |
aoqi@0 | 51 | |
aoqi@0 | 52 | /** |
aoqi@0 | 53 | * Factory for {@link XMLStreamReader}. |
aoqi@0 | 54 | * |
aoqi@0 | 55 | * <p> |
aoqi@0 | 56 | * This wraps {@link XMLInputFactory} and allows us to reuse {@link XMLStreamReader} instances |
aoqi@0 | 57 | * when appropriate. |
aoqi@0 | 58 | * |
aoqi@0 | 59 | * @author Kohsuke Kawaguchi |
aoqi@0 | 60 | */ |
aoqi@0 | 61 | @SuppressWarnings("StaticNonFinalUsedInInitialization") |
aoqi@0 | 62 | public abstract class XMLStreamReaderFactory { |
aoqi@0 | 63 | |
aoqi@0 | 64 | private static final Logger LOGGER = Logger.getLogger(XMLStreamReaderFactory.class.getName()); |
aoqi@0 | 65 | |
aoqi@0 | 66 | private static final String CLASS_NAME_OF_WSTXINPUTFACTORY = "com.ctc.wstx.stax.WstxInputFactory"; |
aoqi@0 | 67 | |
aoqi@0 | 68 | /** |
aoqi@0 | 69 | * Singleton instance. |
aoqi@0 | 70 | */ |
aoqi@0 | 71 | private static volatile ContextClassloaderLocal<XMLStreamReaderFactory> streamReader = |
aoqi@0 | 72 | new ContextClassloaderLocal<XMLStreamReaderFactory>() { |
aoqi@0 | 73 | |
aoqi@0 | 74 | @Override |
aoqi@0 | 75 | protected XMLStreamReaderFactory initialValue() { |
aoqi@0 | 76 | |
aoqi@0 | 77 | XMLInputFactory xif = getXMLInputFactory(); |
aoqi@0 | 78 | XMLStreamReaderFactory f=null; |
aoqi@0 | 79 | |
aoqi@0 | 80 | // this system property can be used to disable the pooling altogether, |
aoqi@0 | 81 | // in case someone hits an issue with pooling in the production system. |
aoqi@0 | 82 | if(!getProperty(XMLStreamReaderFactory.class.getName()+".noPool")) { |
aoqi@0 | 83 | f = Zephyr.newInstance(xif); |
aoqi@0 | 84 | } |
aoqi@0 | 85 | |
aoqi@0 | 86 | if(f==null) { |
aoqi@0 | 87 | // is this Woodstox? |
aoqi@0 | 88 | if (xif.getClass().getName().equals(CLASS_NAME_OF_WSTXINPUTFACTORY)) { |
aoqi@0 | 89 | f = new Woodstox(xif); |
aoqi@0 | 90 | } |
aoqi@0 | 91 | } |
aoqi@0 | 92 | |
aoqi@0 | 93 | if (f==null) { |
aoqi@0 | 94 | f = new Default(); |
aoqi@0 | 95 | } |
aoqi@0 | 96 | |
aoqi@0 | 97 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 98 | LOGGER.log(Level.FINE, "XMLStreamReaderFactory instance is = {0}", f); |
aoqi@0 | 99 | } |
aoqi@0 | 100 | return f; |
aoqi@0 | 101 | } |
aoqi@0 | 102 | }; |
aoqi@0 | 103 | |
aoqi@0 | 104 | private static XMLInputFactory getXMLInputFactory() { |
aoqi@0 | 105 | XMLInputFactory xif = null; |
aoqi@0 | 106 | if (getProperty(XMLStreamReaderFactory.class.getName()+".woodstox")) { |
aoqi@0 | 107 | try { |
aoqi@0 | 108 | xif = (XMLInputFactory)Class.forName("com.ctc.wstx.stax.WstxInputFactory").newInstance(); |
aoqi@0 | 109 | } catch (Exception e) { |
aoqi@0 | 110 | if (LOGGER.isLoggable(Level.WARNING)) { |
aoqi@0 | 111 | LOGGER.log(Level.WARNING, StreamingMessages.WOODSTOX_CANT_LOAD(CLASS_NAME_OF_WSTXINPUTFACTORY), e); |
aoqi@0 | 112 | } |
aoqi@0 | 113 | } |
aoqi@0 | 114 | } |
aoqi@0 | 115 | if (xif == null) { |
aoqi@0 | 116 | xif = XmlUtil.newXMLInputFactory(true); |
aoqi@0 | 117 | } |
aoqi@0 | 118 | xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true); |
aoqi@0 | 119 | xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); |
aoqi@0 | 120 | xif.setProperty(XMLInputFactory.IS_COALESCING, true); |
aoqi@0 | 121 | |
aoqi@0 | 122 | return xif; |
aoqi@0 | 123 | } |
aoqi@0 | 124 | |
aoqi@0 | 125 | /** |
aoqi@0 | 126 | * Overrides the singleton {@link XMLStreamReaderFactory} instance that |
aoqi@0 | 127 | * the JAX-WS RI uses. |
aoqi@0 | 128 | */ |
aoqi@0 | 129 | public static void set(XMLStreamReaderFactory f) { |
aoqi@0 | 130 | if(f==null) { |
aoqi@0 | 131 | throw new IllegalArgumentException(); |
aoqi@0 | 132 | } |
aoqi@0 | 133 | streamReader.set(f); |
aoqi@0 | 134 | } |
aoqi@0 | 135 | |
aoqi@0 | 136 | public static XMLStreamReaderFactory get() { |
aoqi@0 | 137 | return streamReader.get(); |
aoqi@0 | 138 | } |
aoqi@0 | 139 | |
aoqi@0 | 140 | public static XMLStreamReader create(InputSource source, boolean rejectDTDs) { |
aoqi@0 | 141 | try { |
aoqi@0 | 142 | // Char stream available? |
aoqi@0 | 143 | if (source.getCharacterStream() != null) { |
aoqi@0 | 144 | return get().doCreate(source.getSystemId(), source.getCharacterStream(), rejectDTDs); |
aoqi@0 | 145 | } |
aoqi@0 | 146 | |
aoqi@0 | 147 | // Byte stream available? |
aoqi@0 | 148 | if (source.getByteStream() != null) { |
aoqi@0 | 149 | return get().doCreate(source.getSystemId(), source.getByteStream(), rejectDTDs); |
aoqi@0 | 150 | } |
aoqi@0 | 151 | |
aoqi@0 | 152 | // Otherwise, open URI |
aoqi@0 | 153 | return get().doCreate(source.getSystemId(), new URL(source.getSystemId()).openStream(),rejectDTDs); |
aoqi@0 | 154 | } catch (IOException e) { |
aoqi@0 | 155 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 156 | } |
aoqi@0 | 157 | } |
aoqi@0 | 158 | |
aoqi@0 | 159 | public static XMLStreamReader create(@Nullable String systemId, InputStream in, boolean rejectDTDs) { |
aoqi@0 | 160 | return get().doCreate(systemId,in,rejectDTDs); |
aoqi@0 | 161 | } |
aoqi@0 | 162 | |
aoqi@0 | 163 | public static XMLStreamReader create(@Nullable String systemId, InputStream in, @Nullable String encoding, boolean rejectDTDs) { |
aoqi@0 | 164 | return (encoding == null) |
aoqi@0 | 165 | ? create(systemId, in, rejectDTDs) |
aoqi@0 | 166 | : get().doCreate(systemId,in,encoding,rejectDTDs); |
aoqi@0 | 167 | } |
aoqi@0 | 168 | |
aoqi@0 | 169 | public static XMLStreamReader create(@Nullable String systemId, Reader reader, boolean rejectDTDs) { |
aoqi@0 | 170 | return get().doCreate(systemId,reader,rejectDTDs); |
aoqi@0 | 171 | } |
aoqi@0 | 172 | |
aoqi@0 | 173 | /** |
aoqi@0 | 174 | * Should be invoked when the code finished using an {@link XMLStreamReader}. |
aoqi@0 | 175 | * |
aoqi@0 | 176 | * <p> |
aoqi@0 | 177 | * If the recycled instance implements {@link RecycleAware}, |
aoqi@0 | 178 | * {@link RecycleAware#onRecycled()} will be invoked to let the instance |
aoqi@0 | 179 | * know that it's being recycled. |
aoqi@0 | 180 | * |
aoqi@0 | 181 | * <p> |
aoqi@0 | 182 | * It is not a hard requirement to call this method on every {@link XMLStreamReader} |
aoqi@0 | 183 | * instance. Not doing so just reduces the performance by throwing away |
aoqi@0 | 184 | * possibly reusable instances. So the caller should always consider the effort |
aoqi@0 | 185 | * it takes to recycle vs the possible performance gain by doing so. |
aoqi@0 | 186 | * |
aoqi@0 | 187 | * <p> |
aoqi@0 | 188 | * This method may be invoked by multiple threads concurrently. |
aoqi@0 | 189 | * |
aoqi@0 | 190 | * @param r |
aoqi@0 | 191 | * The {@link XMLStreamReader} instance that the caller finished using. |
aoqi@0 | 192 | * This could be any {@link XMLStreamReader} implementation, not just |
aoqi@0 | 193 | * the ones that were created from this factory. So the implementation |
aoqi@0 | 194 | * of this class needs to be aware of that. |
aoqi@0 | 195 | */ |
aoqi@0 | 196 | public static void recycle(XMLStreamReader r) { |
aoqi@0 | 197 | get().doRecycle(r); |
aoqi@0 | 198 | if (r instanceof RecycleAware) { |
aoqi@0 | 199 | ((RecycleAware)r).onRecycled(); |
aoqi@0 | 200 | } |
aoqi@0 | 201 | } |
aoqi@0 | 202 | |
aoqi@0 | 203 | // implementations |
aoqi@0 | 204 | |
aoqi@0 | 205 | public abstract XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs); |
aoqi@0 | 206 | |
aoqi@0 | 207 | private XMLStreamReader doCreate(String systemId, InputStream in, @NotNull String encoding, boolean rejectDTDs) { |
aoqi@0 | 208 | Reader reader; |
aoqi@0 | 209 | try { |
aoqi@0 | 210 | reader = new InputStreamReader(in, encoding); |
aoqi@0 | 211 | } catch(UnsupportedEncodingException ue) { |
aoqi@0 | 212 | throw new XMLReaderException("stax.cantCreate", ue); |
aoqi@0 | 213 | } |
aoqi@0 | 214 | return doCreate(systemId, reader, rejectDTDs); |
aoqi@0 | 215 | } |
aoqi@0 | 216 | |
aoqi@0 | 217 | public abstract XMLStreamReader doCreate(String systemId, Reader reader, boolean rejectDTDs); |
aoqi@0 | 218 | |
aoqi@0 | 219 | public abstract void doRecycle(XMLStreamReader r); |
aoqi@0 | 220 | |
aoqi@0 | 221 | /** |
aoqi@0 | 222 | * Interface that can be implemented by {@link XMLStreamReader} to |
aoqi@0 | 223 | * be notified when it's recycled. |
aoqi@0 | 224 | * |
aoqi@0 | 225 | * <p> |
aoqi@0 | 226 | * This provides a filtering {@link XMLStreamReader} an opportunity to |
aoqi@0 | 227 | * recycle its inner {@link XMLStreamReader}. |
aoqi@0 | 228 | */ |
aoqi@0 | 229 | public interface RecycleAware { |
aoqi@0 | 230 | void onRecycled(); |
aoqi@0 | 231 | } |
aoqi@0 | 232 | |
aoqi@0 | 233 | /** |
aoqi@0 | 234 | * {@link XMLStreamReaderFactory} implementation for SJSXP/JAXP RI. |
aoqi@0 | 235 | */ |
aoqi@0 | 236 | private static final class Zephyr extends XMLStreamReaderFactory { |
aoqi@0 | 237 | private final XMLInputFactory xif; |
aoqi@0 | 238 | |
aoqi@0 | 239 | private final ThreadLocal<XMLStreamReader> pool = new ThreadLocal<XMLStreamReader>(); |
aoqi@0 | 240 | |
aoqi@0 | 241 | /** |
aoqi@0 | 242 | * Sun StAX impl <code>XMLReaderImpl.setInputSource()</code> method via reflection. |
aoqi@0 | 243 | */ |
aoqi@0 | 244 | private final Method setInputSourceMethod; |
aoqi@0 | 245 | |
aoqi@0 | 246 | /** |
aoqi@0 | 247 | * Sun StAX impl <code>XMLReaderImpl.reset()</code> method via reflection. |
aoqi@0 | 248 | */ |
aoqi@0 | 249 | private final Method resetMethod; |
aoqi@0 | 250 | |
aoqi@0 | 251 | /** |
aoqi@0 | 252 | * The Sun StAX impl's {@link XMLStreamReader} implementation clas. |
aoqi@0 | 253 | */ |
aoqi@0 | 254 | private final Class zephyrClass; |
aoqi@0 | 255 | |
aoqi@0 | 256 | /** |
aoqi@0 | 257 | * Creates {@link Zephyr} instance if the given {@link XMLInputFactory} is the one |
aoqi@0 | 258 | * from Zephyr. |
aoqi@0 | 259 | */ |
aoqi@0 | 260 | public static @Nullable |
aoqi@0 | 261 | XMLStreamReaderFactory newInstance(XMLInputFactory xif) { |
aoqi@0 | 262 | // check if this is from Zephyr |
aoqi@0 | 263 | try { |
aoqi@0 | 264 | Class<?> clazz = xif.createXMLStreamReader(new StringReader("<foo/>")).getClass(); |
aoqi@0 | 265 | // JDK has different XMLStreamReader impl class. Even if we check for that, |
aoqi@0 | 266 | // it doesn't have setInputSource(InputSource). Let it use Default |
aoqi@0 | 267 | if(!(clazz.getName().startsWith("com.sun.xml.internal.stream.")) ) |
aoqi@0 | 268 | return null; // nope |
aoqi@0 | 269 | return new Zephyr(xif,clazz); |
aoqi@0 | 270 | } catch (NoSuchMethodException e) { |
aoqi@0 | 271 | return null; // this factory is not for zephyr |
aoqi@0 | 272 | } catch (XMLStreamException e) { |
aoqi@0 | 273 | return null; // impossible to fail to parse <foo/>, but anyway |
aoqi@0 | 274 | } |
aoqi@0 | 275 | } |
aoqi@0 | 276 | |
aoqi@0 | 277 | public Zephyr(XMLInputFactory xif, Class clazz) throws NoSuchMethodException { |
aoqi@0 | 278 | zephyrClass = clazz; |
aoqi@0 | 279 | setInputSourceMethod = clazz.getMethod("setInputSource", InputSource.class); |
aoqi@0 | 280 | resetMethod = clazz.getMethod("reset"); |
aoqi@0 | 281 | |
aoqi@0 | 282 | try { |
aoqi@0 | 283 | // Turn OFF internal factory caching in Zephyr. |
aoqi@0 | 284 | // Santiago told me that this makes it thread-safe. |
aoqi@0 | 285 | xif.setProperty("reuse-instance", false); |
aoqi@0 | 286 | } catch (IllegalArgumentException e) { |
aoqi@0 | 287 | // falls through |
aoqi@0 | 288 | } |
aoqi@0 | 289 | this.xif = xif; |
aoqi@0 | 290 | } |
aoqi@0 | 291 | |
aoqi@0 | 292 | /** |
aoqi@0 | 293 | * Fetchs an instance from the pool if available, otherwise null. |
aoqi@0 | 294 | */ |
aoqi@0 | 295 | private @Nullable XMLStreamReader fetch() { |
aoqi@0 | 296 | XMLStreamReader sr = pool.get(); |
aoqi@0 | 297 | if(sr==null) return null; |
aoqi@0 | 298 | pool.set(null); |
aoqi@0 | 299 | return sr; |
aoqi@0 | 300 | } |
aoqi@0 | 301 | |
aoqi@0 | 302 | @Override |
aoqi@0 | 303 | public void doRecycle(XMLStreamReader r) { |
aoqi@0 | 304 | if(zephyrClass.isInstance(r)) |
aoqi@0 | 305 | pool.set(r); |
aoqi@0 | 306 | } |
aoqi@0 | 307 | |
aoqi@0 | 308 | @Override |
aoqi@0 | 309 | public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) { |
aoqi@0 | 310 | try { |
aoqi@0 | 311 | XMLStreamReader xsr = fetch(); |
aoqi@0 | 312 | if(xsr==null) |
aoqi@0 | 313 | return xif.createXMLStreamReader(systemId,in); |
aoqi@0 | 314 | |
aoqi@0 | 315 | // try re-using this instance. |
aoqi@0 | 316 | InputSource is = new InputSource(systemId); |
aoqi@0 | 317 | is.setByteStream(in); |
aoqi@0 | 318 | reuse(xsr,is); |
aoqi@0 | 319 | return xsr; |
aoqi@0 | 320 | } catch (IllegalAccessException e) { |
aoqi@0 | 321 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 322 | } catch (InvocationTargetException e) { |
aoqi@0 | 323 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 324 | } catch (XMLStreamException e) { |
aoqi@0 | 325 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 326 | } |
aoqi@0 | 327 | } |
aoqi@0 | 328 | |
aoqi@0 | 329 | @Override |
aoqi@0 | 330 | public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) { |
aoqi@0 | 331 | try { |
aoqi@0 | 332 | XMLStreamReader xsr = fetch(); |
aoqi@0 | 333 | if(xsr==null) |
aoqi@0 | 334 | return xif.createXMLStreamReader(systemId,in); |
aoqi@0 | 335 | |
aoqi@0 | 336 | // try re-using this instance. |
aoqi@0 | 337 | InputSource is = new InputSource(systemId); |
aoqi@0 | 338 | is.setCharacterStream(in); |
aoqi@0 | 339 | reuse(xsr,is); |
aoqi@0 | 340 | return xsr; |
aoqi@0 | 341 | } catch (IllegalAccessException e) { |
aoqi@0 | 342 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 343 | } catch (InvocationTargetException e) { |
aoqi@0 | 344 | Throwable cause = e.getCause(); |
aoqi@0 | 345 | if (cause == null) { |
aoqi@0 | 346 | cause = e; |
aoqi@0 | 347 | } |
aoqi@0 | 348 | throw new XMLReaderException("stax.cantCreate", cause); |
aoqi@0 | 349 | } catch (XMLStreamException e) { |
aoqi@0 | 350 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 351 | } |
aoqi@0 | 352 | } |
aoqi@0 | 353 | |
aoqi@0 | 354 | private void reuse(XMLStreamReader xsr, InputSource in) throws IllegalAccessException, InvocationTargetException { |
aoqi@0 | 355 | resetMethod.invoke(xsr); |
aoqi@0 | 356 | setInputSourceMethod.invoke(xsr,in); |
aoqi@0 | 357 | } |
aoqi@0 | 358 | } |
aoqi@0 | 359 | |
aoqi@0 | 360 | /** |
aoqi@0 | 361 | * Default {@link XMLStreamReaderFactory} implementation |
aoqi@0 | 362 | * that can work with any {@link XMLInputFactory}. |
aoqi@0 | 363 | * |
aoqi@0 | 364 | * <p> |
aoqi@0 | 365 | * {@link XMLInputFactory} is not required to be thread-safe, but |
aoqi@0 | 366 | * if the create method on this implementation is synchronized, |
aoqi@0 | 367 | * it may run into (see <a href="https://jax-ws.dev.java.net/issues/show_bug.cgi?id=555"> |
aoqi@0 | 368 | * race condition</a>). Hence, using a XMLInputFactory per thread. |
aoqi@0 | 369 | */ |
aoqi@0 | 370 | public static final class Default extends XMLStreamReaderFactory { |
aoqi@0 | 371 | |
aoqi@0 | 372 | private final ThreadLocal<XMLInputFactory> xif = new ThreadLocal<XMLInputFactory>() { |
aoqi@0 | 373 | @Override |
aoqi@0 | 374 | public XMLInputFactory initialValue() { |
aoqi@0 | 375 | return getXMLInputFactory(); |
aoqi@0 | 376 | } |
aoqi@0 | 377 | }; |
aoqi@0 | 378 | |
aoqi@0 | 379 | @Override |
aoqi@0 | 380 | public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) { |
aoqi@0 | 381 | try { |
aoqi@0 | 382 | return xif.get().createXMLStreamReader(systemId,in); |
aoqi@0 | 383 | } catch (XMLStreamException e) { |
aoqi@0 | 384 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 385 | } |
aoqi@0 | 386 | } |
aoqi@0 | 387 | |
aoqi@0 | 388 | @Override |
aoqi@0 | 389 | public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) { |
aoqi@0 | 390 | try { |
aoqi@0 | 391 | return xif.get().createXMLStreamReader(systemId,in); |
aoqi@0 | 392 | } catch (XMLStreamException e) { |
aoqi@0 | 393 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 394 | } |
aoqi@0 | 395 | } |
aoqi@0 | 396 | |
aoqi@0 | 397 | @Override |
aoqi@0 | 398 | public void doRecycle(XMLStreamReader r) { |
aoqi@0 | 399 | // there's no way to recycle with the default StAX API. |
aoqi@0 | 400 | } |
aoqi@0 | 401 | |
aoqi@0 | 402 | } |
aoqi@0 | 403 | |
aoqi@0 | 404 | /** |
aoqi@0 | 405 | * Similar to {@link Default} but doesn't do any synchronization. |
aoqi@0 | 406 | * |
aoqi@0 | 407 | * <p> |
aoqi@0 | 408 | * This is useful when you know your {@link XMLInputFactory} is thread-safe by itself. |
aoqi@0 | 409 | */ |
aoqi@0 | 410 | public static class NoLock extends XMLStreamReaderFactory { |
aoqi@0 | 411 | private final XMLInputFactory xif; |
aoqi@0 | 412 | |
aoqi@0 | 413 | public NoLock(XMLInputFactory xif) { |
aoqi@0 | 414 | this.xif = xif; |
aoqi@0 | 415 | } |
aoqi@0 | 416 | |
aoqi@0 | 417 | @Override |
aoqi@0 | 418 | public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) { |
aoqi@0 | 419 | try { |
aoqi@0 | 420 | return xif.createXMLStreamReader(systemId,in); |
aoqi@0 | 421 | } catch (XMLStreamException e) { |
aoqi@0 | 422 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 423 | } |
aoqi@0 | 424 | } |
aoqi@0 | 425 | |
aoqi@0 | 426 | @Override |
aoqi@0 | 427 | public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) { |
aoqi@0 | 428 | try { |
aoqi@0 | 429 | return xif.createXMLStreamReader(systemId,in); |
aoqi@0 | 430 | } catch (XMLStreamException e) { |
aoqi@0 | 431 | throw new XMLReaderException("stax.cantCreate",e); |
aoqi@0 | 432 | } |
aoqi@0 | 433 | } |
aoqi@0 | 434 | |
aoqi@0 | 435 | @Override |
aoqi@0 | 436 | public void doRecycle(XMLStreamReader r) { |
aoqi@0 | 437 | // there's no way to recycle with the default StAX API. |
aoqi@0 | 438 | } |
aoqi@0 | 439 | } |
aoqi@0 | 440 | |
aoqi@0 | 441 | /** |
aoqi@0 | 442 | * Handles Woodstox's XIF, but sets properties to do the string interning, sets various limits, ... |
aoqi@0 | 443 | * Woodstox {@link XMLInputFactory} is thread safe. |
aoqi@0 | 444 | */ |
aoqi@0 | 445 | public static final class Woodstox extends NoLock { |
aoqi@0 | 446 | |
aoqi@0 | 447 | public final static String PROPERTY_MAX_ATTRIBUTES_PER_ELEMENT = "xml.ws.maximum.AttributesPerElement"; |
aoqi@0 | 448 | public final static String PROPERTY_MAX_ATTRIBUTE_SIZE = "xml.ws.maximum.AttributeSize"; |
aoqi@0 | 449 | public final static String PROPERTY_MAX_CHILDREN_PER_ELEMENT = "xml.ws.maximum.ChildrenPerElement"; |
aoqi@0 | 450 | public final static String PROPERTY_MAX_ELEMENT_COUNT = "xml.ws.maximum.ElementCount"; |
aoqi@0 | 451 | public final static String PROPERTY_MAX_ELEMENT_DEPTH = "xml.ws.maximum.ElementDepth"; |
aoqi@0 | 452 | public final static String PROPERTY_MAX_CHARACTERS = "xml.ws.maximum.Characters"; |
aoqi@0 | 453 | |
aoqi@0 | 454 | private static final int DEFAULT_MAX_ATTRIBUTES_PER_ELEMENT = 500; |
aoqi@0 | 455 | private static final int DEFAULT_MAX_ATTRIBUTE_SIZE = 65536 * 8; |
aoqi@0 | 456 | private static final int DEFAULT_MAX_CHILDREN_PER_ELEMENT = Integer.MAX_VALUE; |
aoqi@0 | 457 | private static final int DEFAULT_MAX_ELEMENT_DEPTH = 500; |
aoqi@0 | 458 | private static final long DEFAULT_MAX_ELEMENT_COUNT = Integer.MAX_VALUE; |
aoqi@0 | 459 | private static final long DEFAULT_MAX_CHARACTERS = Long.MAX_VALUE; |
aoqi@0 | 460 | |
aoqi@0 | 461 | /* Woodstox default setting: |
aoqi@0 | 462 | int mMaxAttributesPerElement = 1000; |
aoqi@0 | 463 | int mMaxAttributeSize = 65536 * 8; |
aoqi@0 | 464 | int mMaxChildrenPerElement = Integer.MAX_VALUE; |
aoqi@0 | 465 | int mMaxElementDepth = 1000; |
aoqi@0 | 466 | long mMaxElementCount = Long.MAX_VALUE; |
aoqi@0 | 467 | long mMaxCharacters = Long.MAX_VALUE; |
aoqi@0 | 468 | */ |
aoqi@0 | 469 | |
aoqi@0 | 470 | private int maxAttributesPerElement = DEFAULT_MAX_ATTRIBUTES_PER_ELEMENT; |
aoqi@0 | 471 | private int maxAttributeSize = DEFAULT_MAX_ATTRIBUTE_SIZE; |
aoqi@0 | 472 | private int maxChildrenPerElement = DEFAULT_MAX_CHILDREN_PER_ELEMENT; |
aoqi@0 | 473 | private int maxElementDepth = DEFAULT_MAX_ELEMENT_DEPTH; |
aoqi@0 | 474 | private long maxElementCount = DEFAULT_MAX_ELEMENT_COUNT; |
aoqi@0 | 475 | private long maxCharacters = DEFAULT_MAX_CHARACTERS; |
aoqi@0 | 476 | |
aoqi@0 | 477 | // Note: this is a copy from com.ctc.wstx.api.WstxInputProperties, to be removed in the future |
aoqi@0 | 478 | private static final java.lang.String P_MAX_ATTRIBUTES_PER_ELEMENT = "com.ctc.wstx.maxAttributesPerElement"; |
aoqi@0 | 479 | private static final java.lang.String P_MAX_ATTRIBUTE_SIZE = "com.ctc.wstx.maxAttributeSize"; |
aoqi@0 | 480 | private static final java.lang.String P_MAX_CHILDREN_PER_ELEMENT = "com.ctc.wstx.maxChildrenPerElement"; |
aoqi@0 | 481 | private static final java.lang.String P_MAX_ELEMENT_COUNT = "com.ctc.wstx.maxElementCount"; |
aoqi@0 | 482 | private static final java.lang.String P_MAX_ELEMENT_DEPTH = "com.ctc.wstx.maxElementDepth"; |
aoqi@0 | 483 | private static final java.lang.String P_MAX_CHARACTERS = "com.ctc.wstx.maxCharacters"; |
aoqi@0 | 484 | private static final java.lang.String P_INTERN_NSURIS = "org.codehaus.stax2.internNsUris"; |
aoqi@0 | 485 | |
aoqi@0 | 486 | public Woodstox(XMLInputFactory xif) { |
aoqi@0 | 487 | super(xif); |
aoqi@0 | 488 | |
aoqi@0 | 489 | if (xif.isPropertySupported(P_INTERN_NSURIS)) { |
aoqi@0 | 490 | xif.setProperty(P_INTERN_NSURIS, true); |
aoqi@0 | 491 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 492 | LOGGER.log(Level.FINE, P_INTERN_NSURIS + " is {0}", true); |
aoqi@0 | 493 | } |
aoqi@0 | 494 | } |
aoqi@0 | 495 | |
aoqi@0 | 496 | if (xif.isPropertySupported(P_MAX_ATTRIBUTES_PER_ELEMENT)) { |
aoqi@0 | 497 | maxAttributesPerElement = Integer.valueOf(buildIntegerValue( |
aoqi@0 | 498 | PROPERTY_MAX_ATTRIBUTES_PER_ELEMENT, DEFAULT_MAX_ATTRIBUTES_PER_ELEMENT) |
aoqi@0 | 499 | ); |
aoqi@0 | 500 | xif.setProperty(P_MAX_ATTRIBUTES_PER_ELEMENT, maxAttributesPerElement); |
aoqi@0 | 501 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 502 | LOGGER.log(Level.FINE, P_MAX_ATTRIBUTES_PER_ELEMENT + " is {0}", maxAttributesPerElement); |
aoqi@0 | 503 | } |
aoqi@0 | 504 | } |
aoqi@0 | 505 | |
aoqi@0 | 506 | if (xif.isPropertySupported(P_MAX_ATTRIBUTE_SIZE)) { |
aoqi@0 | 507 | maxAttributeSize = Integer.valueOf(buildIntegerValue( |
aoqi@0 | 508 | PROPERTY_MAX_ATTRIBUTE_SIZE, DEFAULT_MAX_ATTRIBUTE_SIZE) |
aoqi@0 | 509 | ); |
aoqi@0 | 510 | xif.setProperty(P_MAX_ATTRIBUTE_SIZE, maxAttributeSize); |
aoqi@0 | 511 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 512 | LOGGER.log(Level.FINE, P_MAX_ATTRIBUTE_SIZE + " is {0}", maxAttributeSize); |
aoqi@0 | 513 | } |
aoqi@0 | 514 | } |
aoqi@0 | 515 | |
aoqi@0 | 516 | if (xif.isPropertySupported(P_MAX_CHILDREN_PER_ELEMENT)) { |
aoqi@0 | 517 | maxChildrenPerElement = Integer.valueOf(buildIntegerValue( |
aoqi@0 | 518 | PROPERTY_MAX_CHILDREN_PER_ELEMENT, DEFAULT_MAX_CHILDREN_PER_ELEMENT) |
aoqi@0 | 519 | ); |
aoqi@0 | 520 | xif.setProperty(P_MAX_CHILDREN_PER_ELEMENT, maxChildrenPerElement); |
aoqi@0 | 521 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 522 | LOGGER.log(Level.FINE, P_MAX_CHILDREN_PER_ELEMENT + " is {0}", maxChildrenPerElement); |
aoqi@0 | 523 | } |
aoqi@0 | 524 | } |
aoqi@0 | 525 | |
aoqi@0 | 526 | if (xif.isPropertySupported(P_MAX_ELEMENT_DEPTH)) { |
aoqi@0 | 527 | maxElementDepth = Integer.valueOf(buildIntegerValue( |
aoqi@0 | 528 | PROPERTY_MAX_ELEMENT_DEPTH, DEFAULT_MAX_ELEMENT_DEPTH) |
aoqi@0 | 529 | ); |
aoqi@0 | 530 | xif.setProperty(P_MAX_ELEMENT_DEPTH, maxElementDepth); |
aoqi@0 | 531 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 532 | LOGGER.log(Level.FINE, P_MAX_ELEMENT_DEPTH + " is {0}", maxElementDepth); |
aoqi@0 | 533 | } |
aoqi@0 | 534 | } |
aoqi@0 | 535 | |
aoqi@0 | 536 | if (xif.isPropertySupported(P_MAX_ELEMENT_COUNT)) { |
aoqi@0 | 537 | maxElementCount = Long.valueOf(buildLongValue( |
aoqi@0 | 538 | PROPERTY_MAX_ELEMENT_COUNT, DEFAULT_MAX_ELEMENT_COUNT) |
aoqi@0 | 539 | ); |
aoqi@0 | 540 | xif.setProperty(P_MAX_ELEMENT_COUNT, maxElementCount); |
aoqi@0 | 541 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 542 | LOGGER.log(Level.FINE, P_MAX_ELEMENT_COUNT + " is {0}", maxElementCount); |
aoqi@0 | 543 | } |
aoqi@0 | 544 | } |
aoqi@0 | 545 | |
aoqi@0 | 546 | if (xif.isPropertySupported(P_MAX_CHARACTERS)) { |
aoqi@0 | 547 | maxCharacters = Long.valueOf(buildLongValue( |
aoqi@0 | 548 | PROPERTY_MAX_CHARACTERS, DEFAULT_MAX_CHARACTERS) |
aoqi@0 | 549 | ); |
aoqi@0 | 550 | xif.setProperty(P_MAX_CHARACTERS, maxCharacters); |
aoqi@0 | 551 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 552 | LOGGER.log(Level.FINE, P_MAX_CHARACTERS + " is {0}", maxCharacters); |
aoqi@0 | 553 | } |
aoqi@0 | 554 | } |
aoqi@0 | 555 | } |
aoqi@0 | 556 | |
aoqi@0 | 557 | @Override |
aoqi@0 | 558 | public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) { |
aoqi@0 | 559 | return super.doCreate(systemId, in, rejectDTDs); |
aoqi@0 | 560 | } |
aoqi@0 | 561 | |
aoqi@0 | 562 | @Override |
aoqi@0 | 563 | public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) { |
aoqi@0 | 564 | return super.doCreate(systemId, in, rejectDTDs); |
aoqi@0 | 565 | } |
aoqi@0 | 566 | } |
aoqi@0 | 567 | |
aoqi@0 | 568 | private static int buildIntegerValue(String propertyName, int defaultValue) { |
aoqi@0 | 569 | String propVal = System.getProperty(propertyName); |
aoqi@0 | 570 | if (propVal != null && propVal.length() > 0) { |
aoqi@0 | 571 | try { |
aoqi@0 | 572 | Integer value = Integer.parseInt(propVal); |
aoqi@0 | 573 | if (value > 0) { |
aoqi@0 | 574 | // return with the value in System property |
aoqi@0 | 575 | return value; |
aoqi@0 | 576 | } |
aoqi@0 | 577 | } catch (NumberFormatException nfe) { |
aoqi@0 | 578 | if (LOGGER.isLoggable(Level.WARNING)) { |
aoqi@0 | 579 | LOGGER.log(Level.WARNING, StreamingMessages.INVALID_PROPERTY_VALUE_INTEGER(propertyName, propVal, Integer.toString(defaultValue)), nfe); |
aoqi@0 | 580 | } |
aoqi@0 | 581 | } |
aoqi@0 | 582 | } |
aoqi@0 | 583 | // return with the default value |
aoqi@0 | 584 | return defaultValue; |
aoqi@0 | 585 | } |
aoqi@0 | 586 | |
aoqi@0 | 587 | private static long buildLongValue(String propertyName, long defaultValue) { |
aoqi@0 | 588 | String propVal = System.getProperty(propertyName); |
aoqi@0 | 589 | if (propVal != null && propVal.length() > 0) { |
aoqi@0 | 590 | try { |
aoqi@0 | 591 | long value = Long.parseLong(propVal); |
aoqi@0 | 592 | if (value > 0L) { |
aoqi@0 | 593 | // return with the value in System property |
aoqi@0 | 594 | return value; |
aoqi@0 | 595 | } |
aoqi@0 | 596 | } catch (NumberFormatException nfe) { |
aoqi@0 | 597 | // defult will be returned |
aoqi@0 | 598 | if (LOGGER.isLoggable(Level.WARNING)) { |
aoqi@0 | 599 | LOGGER.log(Level.WARNING, StreamingMessages.INVALID_PROPERTY_VALUE_LONG(propertyName, propVal, Long.toString(defaultValue)), nfe); |
aoqi@0 | 600 | } |
aoqi@0 | 601 | } |
aoqi@0 | 602 | } |
aoqi@0 | 603 | // return with the default value |
aoqi@0 | 604 | return defaultValue; |
aoqi@0 | 605 | } |
aoqi@0 | 606 | |
aoqi@0 | 607 | private static Boolean getProperty(final String prop) { |
aoqi@0 | 608 | return AccessController.doPrivileged( |
aoqi@0 | 609 | new java.security.PrivilegedAction<Boolean>() { |
aoqi@0 | 610 | @Override |
aoqi@0 | 611 | public Boolean run() { |
aoqi@0 | 612 | String value = System.getProperty(prop); |
aoqi@0 | 613 | return value != null ? Boolean.valueOf(value) : Boolean.FALSE; |
aoqi@0 | 614 | } |
aoqi@0 | 615 | } |
aoqi@0 | 616 | ); |
aoqi@0 | 617 | } |
aoqi@0 | 618 | |
aoqi@0 | 619 | } |