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

Wed, 12 Jun 2013 14:47:09 +0100

author
mkos
date
Wed, 12 Jun 2013 14:47:09 +0100
changeset 384
8f2986ff0235
parent 368
0989ad8c0860
child 397
b99d7e355d4b
permissions
-rw-r--r--

8013021: Rebase 8005432 & 8003542 against the latest jdk8/jaxws
8003542: Improve processing of MTOM attachments
8005432: Update access to JAX-WS
Reviewed-by: mullan

     1 /*
     2  * Copyright (c) 1997, 2013, 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 com.sun.xml.internal.ws.util.xml.XmlUtil;
    32 import org.xml.sax.InputSource;
    34 import javax.xml.stream.XMLInputFactory;
    35 import javax.xml.stream.XMLStreamException;
    36 import javax.xml.stream.XMLStreamReader;
    37 import java.io.*;
    38 import java.lang.reflect.InvocationTargetException;
    39 import java.lang.reflect.Method;
    40 import java.net.URL;
    41 import java.security.AccessController;
    42 import java.util.logging.Level;
    43 import java.util.logging.Logger;
    45 /**
    46  * Factory for {@link XMLStreamReader}.
    47  *
    48  * <p>
    49  * This wraps {@link XMLInputFactory} and allows us to reuse {@link XMLStreamReader} instances
    50  * when appropriate.
    51  *
    52  * @author Kohsuke Kawaguchi
    53  */
    54 @SuppressWarnings("StaticNonFinalUsedInInitialization")
    55 public abstract class XMLStreamReaderFactory {
    57     private static final Logger LOGGER = Logger.getLogger(XMLStreamReaderFactory.class.getName());
    59     /**
    60      * Singleton instance.
    61      */
    62     private static volatile @NotNull XMLStreamReaderFactory theInstance;
    64     static {
    65         XMLInputFactory xif = getXMLInputFactory();
    66         XMLStreamReaderFactory f=null;
    68         // this system property can be used to disable the pooling altogether,
    69         // in case someone hits an issue with pooling in the production system.
    70         if(!getProperty(XMLStreamReaderFactory.class.getName()+".noPool")) {
    71             f = Zephyr.newInstance(xif);
    72         }
    74         if(f==null) {
    75             // is this Woodstox?
    76             if (xif.getClass().getName().equals("com.ctc.wstx.stax.WstxInputFactory")) {
    77                 f = new Woodstox(xif);
    78             }
    79         }
    81         if (f==null) {
    82             f = new Default();
    83         }
    85         theInstance = f;
    86         LOGGER.log(Level.FINE, "XMLStreamReaderFactory instance is = {0}", theInstance);
    87     }
    89     private static XMLInputFactory getXMLInputFactory() {
    90         XMLInputFactory xif = null;
    91         if (getProperty(XMLStreamReaderFactory.class.getName()+".woodstox")) {
    92             try {
    93                 xif = (XMLInputFactory)Class.forName("com.ctc.wstx.stax.WstxInputFactory").newInstance();
    94             } catch (Exception e) {
    95                 // Ignore and fallback to default XMLInputFactory
    96             }
    97         }
    98         if (xif == null) {
    99              xif = XmlUtil.newXMLInputFactory(true);
   100         }
   101         xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
   102         xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
   103         xif.setProperty(XMLInputFactory.IS_COALESCING, true);
   104         return xif;
   105     }
   108     /**
   109      * Overrides the singleton {@link XMLStreamReaderFactory} instance that
   110      * the JAX-WS RI uses.
   111      */
   112     public static void set(XMLStreamReaderFactory f) {
   113         if(f==null) {
   114             throw new IllegalArgumentException();
   115         }
   116         theInstance = f;
   117     }
   119     public static XMLStreamReaderFactory get() {
   120         return theInstance;
   121     }
   123     public static XMLStreamReader create(InputSource source, boolean rejectDTDs) {
   124         try {
   125             // Char stream available?
   126             if (source.getCharacterStream() != null) {
   127                 return get().doCreate(source.getSystemId(), source.getCharacterStream(), rejectDTDs);
   128             }
   130             // Byte stream available?
   131             if (source.getByteStream() != null) {
   132                 return get().doCreate(source.getSystemId(), source.getByteStream(), rejectDTDs);
   133             }
   135             // Otherwise, open URI
   136             return get().doCreate(source.getSystemId(), new URL(source.getSystemId()).openStream(),rejectDTDs);
   137         } catch (IOException e) {
   138             throw new XMLReaderException("stax.cantCreate",e);
   139         }
   140     }
   142     public static XMLStreamReader create(@Nullable String systemId, InputStream in, boolean rejectDTDs) {
   143         return get().doCreate(systemId,in,rejectDTDs);
   144     }
   146     public static XMLStreamReader create(@Nullable String systemId, InputStream in, @Nullable String encoding, boolean rejectDTDs) {
   147         return (encoding == null)
   148                 ? create(systemId, in, rejectDTDs)
   149                 : get().doCreate(systemId,in,encoding,rejectDTDs);
   150     }
   152     public static XMLStreamReader create(@Nullable String systemId, Reader reader, boolean rejectDTDs) {
   153         return get().doCreate(systemId,reader,rejectDTDs);
   154     }
   156     /**
   157      * Should be invoked when the code finished using an {@link XMLStreamReader}.
   158      *
   159      * <p>
   160      * If the recycled instance implements {@link RecycleAware},
   161      * {@link RecycleAware#onRecycled()} will be invoked to let the instance
   162      * know that it's being recycled.
   163      *
   164      * <p>
   165      * It is not a hard requirement to call this method on every {@link XMLStreamReader}
   166      * instance. Not doing so just reduces the performance by throwing away
   167      * possibly reusable instances. So the caller should always consider the effort
   168      * it takes to recycle vs the possible performance gain by doing so.
   169      *
   170      * <p>
   171      * This method may be invked by multiple threads concurrently.
   172      *
   173      * @param r
   174      *      The {@link XMLStreamReader} instance that the caller finished using.
   175      *      This could be any {@link XMLStreamReader} implementation, not just
   176      *      the ones that were created from this factory. So the implementation
   177      *      of this class needs to be aware of that.
   178      */
   179     public static void recycle(XMLStreamReader r) {
   180         get().doRecycle(r);
   181         if (r instanceof RecycleAware) {
   182             ((RecycleAware)r).onRecycled();
   183         }
   184     }
   186     // implementations
   188     public abstract XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs);
   190     private XMLStreamReader doCreate(String systemId, InputStream in, @NotNull String encoding, boolean rejectDTDs) {
   191         Reader reader;
   192         try {
   193             reader = new InputStreamReader(in, encoding);
   194         } catch(UnsupportedEncodingException ue) {
   195             throw new XMLReaderException("stax.cantCreate", ue);
   196         }
   197         return doCreate(systemId, reader, rejectDTDs);
   198     }
   200     public abstract XMLStreamReader doCreate(String systemId, Reader reader, boolean rejectDTDs);
   202     public abstract void doRecycle(XMLStreamReader r);
   204     /**
   205      * Interface that can be implemented by {@link XMLStreamReader} to
   206      * be notified when it's recycled.
   207      *
   208      * <p>
   209      * This provides a filtering {@link XMLStreamReader} an opportunity to
   210      * recycle its inner {@link XMLStreamReader}.
   211      */
   212     public interface RecycleAware {
   213         void onRecycled();
   214     }
   216     /**
   217      * {@link XMLStreamReaderFactory} implementation for SJSXP/JAXP RI.
   218      */
   219     private static final class Zephyr extends XMLStreamReaderFactory {
   220         private final XMLInputFactory xif;
   222         private final ThreadLocal<XMLStreamReader> pool = new ThreadLocal<XMLStreamReader>();
   224         /**
   225          * Sun StAX impl <code>XMLReaderImpl.setInputSource()</code> method via reflection.
   226          */
   227         private final Method setInputSourceMethod;
   229         /**
   230          * Sun StAX impl <code>XMLReaderImpl.reset()</code> method via reflection.
   231          */
   232         private final Method resetMethod;
   234         /**
   235          * The Sun StAX impl's {@link XMLStreamReader} implementation clas.
   236          */
   237         private final Class zephyrClass;
   239         /**
   240          * Creates {@link Zephyr} instance if the given {@link XMLInputFactory} is the one
   241          * from Zephyr.
   242          */
   243         public static @Nullable
   244         XMLStreamReaderFactory newInstance(XMLInputFactory xif) {
   245             // check if this is from Zephyr
   246             try {
   247                 Class<?> clazz = xif.createXMLStreamReader(new StringReader("<foo/>")).getClass();
   248                 // JDK has different XMLStreamReader impl class. Even if we check for that,
   249                 // it doesn't have setInputSource(InputSource). Let it use Default
   250                 if(!(clazz.getName().startsWith("com.sun.xml.internal.stream.")) )
   251                     return null;    // nope
   252                 return new Zephyr(xif,clazz);
   253             } catch (NoSuchMethodException e) {
   254                 return null;    // this factory is not for zephyr
   255             } catch (XMLStreamException e) {
   256                 return null;    // impossible to fail to parse <foo/>, but anyway
   257             }
   258         }
   260         public Zephyr(XMLInputFactory xif, Class clazz) throws NoSuchMethodException {
   261             zephyrClass = clazz;
   262             setInputSourceMethod = clazz.getMethod("setInputSource", InputSource.class);
   263             resetMethod = clazz.getMethod("reset");
   265             try {
   266                 // Turn OFF internal factory caching in Zephyr.
   267                 // Santiago told me that this makes it thread-safe.
   268                 xif.setProperty("reuse-instance", false);
   269             } catch (IllegalArgumentException e) {
   270                 // falls through
   271             }
   272             this.xif = xif;
   273         }
   275         /**
   276          * Fetchs an instance from the pool if available, otherwise null.
   277          */
   278         private @Nullable XMLStreamReader fetch() {
   279             XMLStreamReader sr = pool.get();
   280             if(sr==null)    return null;
   281             pool.set(null);
   282             return sr;
   283         }
   285         public void doRecycle(XMLStreamReader r) {
   286             if(zephyrClass.isInstance(r))
   287                 pool.set(r);
   288         }
   290         public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) {
   291             try {
   292                 XMLStreamReader xsr = fetch();
   293                 if(xsr==null)
   294                     return xif.createXMLStreamReader(systemId,in);
   296                 // try re-using this instance.
   297                 InputSource is = new InputSource(systemId);
   298                 is.setByteStream(in);
   299                 reuse(xsr,is);
   300                 return xsr;
   301             } catch (IllegalAccessException e) {
   302                 throw new XMLReaderException("stax.cantCreate",e);
   303             } catch (InvocationTargetException e) {
   304                 throw new XMLReaderException("stax.cantCreate",e);
   305             } catch (XMLStreamException e) {
   306                 throw new XMLReaderException("stax.cantCreate",e);
   307             }
   308         }
   310         public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) {
   311             try {
   312                 XMLStreamReader xsr = fetch();
   313                 if(xsr==null)
   314                     return xif.createXMLStreamReader(systemId,in);
   316                 // try re-using this instance.
   317                 InputSource is = new InputSource(systemId);
   318                 is.setCharacterStream(in);
   319                 reuse(xsr,is);
   320                 return xsr;
   321             } catch (IllegalAccessException e) {
   322                 throw new XMLReaderException("stax.cantCreate",e);
   323             } catch (InvocationTargetException e) {
   324                 Throwable cause = e.getCause();
   325                 if (cause == null) {
   326                     cause = e;
   327                 }
   328                 throw new XMLReaderException("stax.cantCreate", cause);
   329             } catch (XMLStreamException e) {
   330                 throw new XMLReaderException("stax.cantCreate",e);
   331             }
   332         }
   334         private void reuse(XMLStreamReader xsr, InputSource in) throws IllegalAccessException, InvocationTargetException {
   335             resetMethod.invoke(xsr);
   336             setInputSourceMethod.invoke(xsr,in);
   337         }
   338     }
   340     /**
   341      * Default {@link XMLStreamReaderFactory} implementation
   342      * that can work with any {@link XMLInputFactory}.
   343      *
   344      * <p>
   345      * {@link XMLInputFactory} is not required to be thread-safe, but
   346      * if the create method on this implementation is synchronized,
   347      * it may run into (see <a href="https://jax-ws.dev.java.net/issues/show_bug.cgi?id=555">
   348      * race condition</a>). Hence, using a XMLInputFactory per theread.
   349      */
   350     public static final class Default extends XMLStreamReaderFactory {
   352         private final ThreadLocal<XMLInputFactory> xif = new ThreadLocal<XMLInputFactory>() {
   353             @Override
   354             public XMLInputFactory initialValue() {
   355                 return getXMLInputFactory();
   356             }
   357         };
   359         public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) {
   360             try {
   361                 return xif.get().createXMLStreamReader(systemId,in);
   362             } catch (XMLStreamException e) {
   363                 throw new XMLReaderException("stax.cantCreate",e);
   364             }
   365         }
   367         public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) {
   368             try {
   369                 return xif.get().createXMLStreamReader(systemId,in);
   370             } catch (XMLStreamException e) {
   371                 throw new XMLReaderException("stax.cantCreate",e);
   372             }
   373         }
   375         public void doRecycle(XMLStreamReader r) {
   376             // there's no way to recycle with the default StAX API.
   377         }
   379     }
   381     /**
   382      * Similar to {@link Default} but doesn't do any synchronization.
   383      *
   384      * <p>
   385      * This is useful when you know your {@link XMLInputFactory} is thread-safe by itself.
   386      */
   387     public static class NoLock extends XMLStreamReaderFactory {
   388         private final XMLInputFactory xif;
   390         public NoLock(XMLInputFactory xif) {
   391             this.xif = xif;
   392         }
   394         public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) {
   395             try {
   396                 return xif.createXMLStreamReader(systemId,in);
   397             } catch (XMLStreamException e) {
   398                 throw new XMLReaderException("stax.cantCreate",e);
   399             }
   400         }
   402         public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) {
   403             try {
   404                 return xif.createXMLStreamReader(systemId,in);
   405             } catch (XMLStreamException e) {
   406                 throw new XMLReaderException("stax.cantCreate",e);
   407             }
   408         }
   410         public void doRecycle(XMLStreamReader r) {
   411             // there's no way to recycle with the default StAX API.
   412         }
   413     }
   415     /**
   416      * Handles Woodstox's XIF but set properties to do the string interning.
   417      * Woodstox {@link XMLInputFactory} is thread safe.
   418      */
   419     public static final class Woodstox extends NoLock {
   420         public Woodstox(XMLInputFactory xif) {
   421             super(xif);
   422             xif.setProperty("org.codehaus.stax2.internNsUris",true);
   423         }
   425         public XMLStreamReader doCreate(String systemId, InputStream in, boolean rejectDTDs) {
   426             return super.doCreate(systemId, in, rejectDTDs);
   427         }
   429         public XMLStreamReader doCreate(String systemId, Reader in, boolean rejectDTDs) {
   430             return super.doCreate(systemId, in, rejectDTDs);
   431         }
   432     }
   434     private static Boolean getProperty(final String prop) {
   435         return AccessController.doPrivileged(
   436             new java.security.PrivilegedAction<Boolean>() {
   437                 public Boolean run() {
   438                     String value = System.getProperty(prop);
   439                     return value != null ? Boolean.valueOf(value) : Boolean.FALSE;
   440                 }
   441             }
   442         );
   443     }
   444 }

mercurial