src/share/jaxws_classes/com/sun/xml/internal/ws/api/streaming/XMLStreamReaderFactory.java

Tue, 06 Mar 2012 16:09:35 -0800

author
ohair
date
Tue, 06 Mar 2012 16:09:35 -0800
changeset 286
f50545b5e2f1
child 368
0989ad8c0860
permissions
-rw-r--r--

7150322: Stop using drop source bundles in jaxws
Reviewed-by: darcy, ohrstrom

     1 /*
     2  * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.xml.internal.ws.api.streaming;
    28 import com.sun.istack.internal.NotNull;
    29 import com.sun.istack.internal.Nullable;
    30 import com.sun.xml.internal.ws.streaming.XMLReaderException;
    31 import org.xml.sax.InputSource;
    33 import javax.xml.stream.XMLInputFactory;
    34 import javax.xml.stream.XMLStreamException;
    35 import javax.xml.stream.XMLStreamReader;
    36 import java.io.IOException;
    37 import java.io.InputStream;
    38 import java.io.InputStreamReader;
    39 import java.io.Reader;
    40 import java.io.StringReader;
    41 import java.io.UnsupportedEncodingException;
    42 import java.lang.reflect.InvocationTargetException;
    43 import java.lang.reflect.Method;
    44 import java.net.URL;
    45 import java.util.logging.Logger;
    46 import java.security.AccessController;
    48 /**
    49  * Factory for {@link XMLStreamReader}.
    50  *
    51  * <p>
    52  * This wraps {@link XMLInputFactory} and allows us to reuse {@link XMLStreamReader} instances
    53  * when appropriate.
    54  *
    55  * @author Kohsuke Kawaguchi
    56  */
    57 public abstract class XMLStreamReaderFactory {
    59     private static final Logger LOGGER = Logger.getLogger(XMLStreamReaderFactory.class.getName());
    61     /**
    62      * Singleton instance.
    63      */
    64     private static volatile @NotNull XMLStreamReaderFactory theInstance;
    66     static {
    67         XMLInputFactory xif = getXMLInputFactory();
    68         XMLStreamReaderFactory f=null;
    70         // this system property can be used to disable the pooling altogether,
    71         // in case someone hits an issue with pooling in the production system.
    72         if(!getProperty(XMLStreamReaderFactory.class.getName()+".noPool"))
    73             f = Zephyr.newInstance(xif);
    75         if(f==null) {
    76             // is this Woodstox?
    77             if(xif.getClass().getName().equals("com.ctc.wstx.stax.WstxInputFactory"))
    78                 f = new Woodstox(xif);
    79         }
    81         if(f==null)
    82             f = new Default();
    84         theInstance = f;
    85         LOGGER.fine("XMLStreamReaderFactory instance is = "+theInstance);
    86     }
    88     private static XMLInputFactory getXMLInputFactory() {
    89         XMLInputFactory xif = null;
    90         if (getProperty(XMLStreamReaderFactory.class.getName()+".woodstox")) {
    91             try {
    92                 xif = (XMLInputFactory)Class.forName("com.ctc.wstx.stax.WstxInputFactory").newInstance();
    93             } catch (Exception e) {
    94                 // Ignore and fallback to default XMLInputFactory
    95             }
    96         }
    97         if (xif == null) {
    98             xif = XMLInputFactory.newInstance();
    99         }
   100         xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
   101         xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
   102         xif.setProperty(XMLInputFactory.IS_COALESCING, true);
   103         return xif;
   104     }
   107     /**
   108      * Overrides the singleton {@link XMLStreamReaderFactory} instance that
   109      * the JAX-WS RI uses.
   110      */
   111     public static void set(XMLStreamReaderFactory f) {
   112         if(f==null) throw new IllegalArgumentException();
   113         theInstance = f;
   114     }
   116     public static XMLStreamReaderFactory get() {
   117         return theInstance;
   118     }
   120     public static XMLStreamReader create(InputSource source, boolean rejectDTDs) {
   121         try {
   122             // Char stream available?
   123             if (source.getCharacterStream() != null) {
   124                 return get().doCreate(source.getSystemId(), source.getCharacterStream(), rejectDTDs);
   125             }
   127             // Byte stream available?
   128             if (source.getByteStream() != null) {
   129                 return get().doCreate(source.getSystemId(), source.getByteStream(), rejectDTDs);
   130             }
   132             // Otherwise, open URI
   133             return get().doCreate(source.getSystemId(), new URL(source.getSystemId()).openStream(),rejectDTDs);
   134         } catch (IOException e) {
   135             throw new XMLReaderException("stax.cantCreate",e);
   136         }
   137     }
   139     public static XMLStreamReader create(@Nullable String systemId, InputStream in, boolean rejectDTDs) {
   140         return get().doCreate(systemId,in,rejectDTDs);
   141     }
   143     public static XMLStreamReader create(@Nullable String systemId, InputStream in, @Nullable String encoding, boolean rejectDTDs) {
   144         return (encoding == null)
   145                 ? create(systemId, in, rejectDTDs)
   146                 : get().doCreate(systemId,in,encoding,rejectDTDs);
   147     }
   149     public static XMLStreamReader create(@Nullable String systemId, Reader reader, boolean rejectDTDs) {
   150         return get().doCreate(systemId,reader,rejectDTDs);
   151     }
   153     /**
   154      * Should be invoked when the code finished using an {@link XMLStreamReader}.
   155      *
   156      * <p>
   157      * If the recycled instance implements {@link RecycleAware},
   158      * {@link RecycleAware#onRecycled()} will be invoked to let the instance
   159      * know that it's being recycled.
   160      *
   161      * <p>
   162      * It is not a hard requirement to call this method on every {@link XMLStreamReader}
   163      * instance. Not doing so just reduces the performance by throwing away
   164      * possibly reusable instances. So the caller should always consider the effort
   165      * it takes to recycle vs the possible performance gain by doing so.
   166      *
   167      * <p>
   168      * This method may be invked by multiple threads concurrently.
   169      *
   170      * @param r
   171      *      The {@link XMLStreamReader} instance that the caller finished using.
   172      *      This could be any {@link XMLStreamReader} implementation, not just
   173      *      the ones that were created from this factory. So the implementation
   174      *      of this class needs to be aware of that.
   175      */
   176     public static void recycle(XMLStreamReader r) {
   177         get().doRecycle(r);
   178         if (r instanceof RecycleAware) {
   179             ((RecycleAware)r).onRecycled();
   180         }
   181     }
   183     // implementations
   185     public abstract XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs);
   187     private XMLStreamReader doCreate(String systemId, InputStream in, @NotNull String encoding, boolean rejectDTDs) {
   188         Reader reader;
   189         try {
   190             reader = new InputStreamReader(in, encoding);
   191         } catch(UnsupportedEncodingException ue) {
   192             throw new XMLReaderException("stax.cantCreate", ue);
   193         }
   194         return doCreate(systemId, reader, rejectDTDs);
   195     }
   197     public abstract XMLStreamReader doCreate(String systemId, Reader reader, boolean rejectDTDs);
   199     public abstract void doRecycle(XMLStreamReader r);
   201     /**
   202      * Interface that can be implemented by {@link XMLStreamReader} to
   203      * be notified when it's recycled.
   204      *
   205      * <p>
   206      * This provides a filtering {@link XMLStreamReader} an opportunity to
   207      * recycle its inner {@link XMLStreamReader}.
   208      */
   209     public interface RecycleAware {
   210         void onRecycled();
   211     }
   213     /**
   214      * {@link XMLStreamReaderFactory} implementation for SJSXP/JAXP RI.
   215      */
   216     public static final class Zephyr extends XMLStreamReaderFactory {
   217         private final XMLInputFactory xif;
   219         private final ThreadLocal<XMLStreamReader> pool = new ThreadLocal<XMLStreamReader>();
   221         /**
   222          * Sun StAX impl <code>XMLReaderImpl.setInputSource()</code> method via reflection.
   223          */
   224         private final Method setInputSourceMethod;
   226         /**
   227          * Sun StAX impl <code>XMLReaderImpl.reset()</code> method via reflection.
   228          */
   229         private final Method resetMethod;
   231         /**
   232          * The Sun StAX impl's {@link XMLStreamReader} implementation clas.
   233          */
   234         private final Class zephyrClass;
   236         /**
   237          * Creates {@link Zephyr} instance if the given {@link XMLInputFactory} is the one
   238          * from Zephyr.
   239          */
   240         public static @Nullable
   241         XMLStreamReaderFactory newInstance(XMLInputFactory xif) {
   242             // check if this is from Zephyr
   243             try {
   244                 Class<?> clazz = xif.createXMLStreamReader(new StringReader("<foo/>")).getClass();
   245                 // JDK has different XMLStreamReader impl class. Even if we check for that,
   246                 // it doesn't have setInputSource(InputSource). Let it use Default
   247                 if(!(clazz.getName().startsWith("com.sun.xml.internal.stream.")) )
   248                     return null;    // nope
   249                 return new Zephyr(xif,clazz);
   250             } catch (NoSuchMethodException e) {
   251                 return null;    // this factory is not for zephyr
   252             } catch (XMLStreamException e) {
   253                 return null;    // impossible to fail to parse <foo/>, but anyway
   254             }
   255         }
   257         public Zephyr(XMLInputFactory xif, Class clazz) throws NoSuchMethodException {
   258             zephyrClass = clazz;
   259             setInputSourceMethod = clazz.getMethod("setInputSource", InputSource.class);
   260             resetMethod = clazz.getMethod("reset");
   262             try {
   263                 // Turn OFF internal factory caching in Zephyr.
   264                 // Santiago told me that this makes it thread-safe.
   265                 xif.setProperty("reuse-instance", false);
   266             } catch (IllegalArgumentException e) {
   267                 // falls through
   268             }
   269             this.xif = xif;
   270         }
   272         /**
   273          * Fetchs an instance from the pool if available, otherwise null.
   274          */
   275         private @Nullable XMLStreamReader fetch() {
   276             XMLStreamReader sr = pool.get();
   277             if(sr==null)    return null;
   278             pool.set(null);
   279             return sr;
   280         }
   282         public void doRecycle(XMLStreamReader r) {
   283             if(zephyrClass.isInstance(r))
   284                 pool.set(r);
   285         }
   287         public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) {
   288             try {
   289                 XMLStreamReader xsr = fetch();
   290                 if(xsr==null)
   291                     return xif.createXMLStreamReader(systemId,in);
   293                 // try re-using this instance.
   294                 InputSource is = new InputSource(systemId);
   295                 is.setByteStream(in);
   296                 reuse(xsr,is);
   297                 return xsr;
   298             } catch (IllegalAccessException e) {
   299                 throw new XMLReaderException("stax.cantCreate",e);
   300             } catch (InvocationTargetException e) {
   301                 throw new XMLReaderException("stax.cantCreate",e);
   302             } catch (XMLStreamException e) {
   303                 throw new XMLReaderException("stax.cantCreate",e);
   304             }
   305         }
   307         public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) {
   308             try {
   309                 XMLStreamReader xsr = fetch();
   310                 if(xsr==null)
   311                     return xif.createXMLStreamReader(systemId,in);
   313                 // try re-using this instance.
   314                 InputSource is = new InputSource(systemId);
   315                 is.setCharacterStream(in);
   316                 reuse(xsr,is);
   317                 return xsr;
   318             } catch (IllegalAccessException e) {
   319                 throw new XMLReaderException("stax.cantCreate",e);
   320             } catch (InvocationTargetException e) {
   321                 Throwable cause = e.getCause();
   322                 if (cause == null) {
   323                     cause = e;
   324                 }
   325                 throw new XMLReaderException("stax.cantCreate", cause);
   326             } catch (XMLStreamException e) {
   327                 throw new XMLReaderException("stax.cantCreate",e);
   328             }
   329         }
   331         private void reuse(XMLStreamReader xsr, InputSource in) throws IllegalAccessException, InvocationTargetException {
   332             resetMethod.invoke(xsr);
   333             setInputSourceMethod.invoke(xsr,in);
   334         }
   335     }
   337     /**
   338      * Default {@link XMLStreamReaderFactory} implementation
   339      * that can work with any {@link XMLInputFactory}.
   340      *
   341      * <p>
   342      * {@link XMLInputFactory} is not required to be thread-safe, but
   343      * if the create method on this implementation is synchronized,
   344      * it may run into (see <a href="https://jax-ws.dev.java.net/issues/show_bug.cgi?id=555">
   345      * race condition</a>). Hence, using a XMLInputFactory per theread.
   346      */
   347     public static final class Default extends XMLStreamReaderFactory {
   349         private final ThreadLocal<XMLInputFactory> xif = new ThreadLocal<XMLInputFactory>() {
   350             @Override
   351             public XMLInputFactory initialValue() {
   352                 return getXMLInputFactory();
   353             }
   354         };
   356         public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) {
   357             try {
   358                 return xif.get().createXMLStreamReader(systemId,in);
   359             } catch (XMLStreamException e) {
   360                 throw new XMLReaderException("stax.cantCreate",e);
   361             }
   362         }
   364         public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) {
   365             try {
   366                 return xif.get().createXMLStreamReader(systemId,in);
   367             } catch (XMLStreamException e) {
   368                 throw new XMLReaderException("stax.cantCreate",e);
   369             }
   370         }
   372         public void doRecycle(XMLStreamReader r) {
   373             // there's no way to recycle with the default StAX API.
   374         }
   376     }
   378     /**
   379      * Similar to {@link Default} but doesn't do any synchronization.
   380      *
   381      * <p>
   382      * This is useful when you know your {@link XMLInputFactory} is thread-safe by itself.
   383      */
   384     public static class NoLock extends XMLStreamReaderFactory {
   385         private final XMLInputFactory xif;
   387         public NoLock(XMLInputFactory xif) {
   388             this.xif = xif;
   389         }
   391         public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) {
   392             try {
   393                 return xif.createXMLStreamReader(systemId,in);
   394             } catch (XMLStreamException e) {
   395                 throw new XMLReaderException("stax.cantCreate",e);
   396             }
   397         }
   399         public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) {
   400             try {
   401                 return xif.createXMLStreamReader(systemId,in);
   402             } catch (XMLStreamException e) {
   403                 throw new XMLReaderException("stax.cantCreate",e);
   404             }
   405         }
   407         public void doRecycle(XMLStreamReader r) {
   408             // there's no way to recycle with the default StAX API.
   409         }
   410     }
   412     /**
   413      * Handles Woodstox's XIF but set properties to do the string interning.
   414      * Woodstox {@link XMLInputFactory} is thread safe.
   415      */
   416     public static final class Woodstox extends NoLock {
   417         public Woodstox(XMLInputFactory xif) {
   418             super(xif);
   419             xif.setProperty("org.codehaus.stax2.internNsUris",true);
   420         }
   422         public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) {
   423             return super.doCreate(systemId, in, rejectDTDs);
   424         }
   426         public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) {
   427             return super.doCreate(systemId, in, rejectDTDs);
   428         }
   429     }
   431     private static Boolean getProperty(final String prop) {
   432         return AccessController.doPrivileged(
   433             new java.security.PrivilegedAction<Boolean>() {
   434                 public Boolean run() {
   435                     String value = System.getProperty(prop);
   436                     return value != null ? Boolean.valueOf(value) : Boolean.FALSE;
   437                 }
   438             }
   439         );
   440     }
   441 }

mercurial