001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.ws.api.streaming;
038:
039: import com.sun.istack.NotNull;
040: import com.sun.istack.Nullable;
041: import com.sun.xml.ws.streaming.XMLReaderException;
042: import org.xml.sax.InputSource;
043:
044: import javax.xml.stream.XMLInputFactory;
045: import javax.xml.stream.XMLStreamException;
046: import javax.xml.stream.XMLStreamReader;
047: import java.io.IOException;
048: import java.io.InputStream;
049: import java.io.InputStreamReader;
050: import java.io.Reader;
051: import java.io.StringReader;
052: import java.io.UnsupportedEncodingException;
053: import java.lang.reflect.InvocationTargetException;
054: import java.lang.reflect.Method;
055: import java.net.URL;
056: import java.util.logging.Logger;
057:
058: /**
059: * Factory for {@link XMLStreamReader}.
060: *
061: * <p>
062: * This wraps {@link XMLInputFactory} and allows us to reuse {@link XMLStreamReader} instances
063: * when appropriate.
064: *
065: * @author Kohsuke Kawaguchi
066: */
067: public abstract class XMLStreamReaderFactory {
068:
069: private static final Logger LOGGER = Logger
070: .getLogger(XMLStreamReaderFactory.class.getName());
071:
072: /**
073: * Singleton instance.
074: */
075: private static volatile @NotNull
076: XMLStreamReaderFactory theInstance;
077:
078: static {
079: XMLInputFactory xif = null;
080: if (Boolean.getBoolean(XMLStreamReaderFactory.class.getName()
081: + ".woodstox")) {
082: try {
083: xif = (XMLInputFactory) Class.forName(
084: "com.ctc.wstx.stax.WstxInputFactory")
085: .newInstance();
086: } catch (Exception e) {
087: // Ignore and fallback to default XMLInputFactory
088: }
089: }
090: if (xif == null) {
091: xif = XMLInputFactory.newInstance();
092: }
093: xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
094:
095: XMLStreamReaderFactory f = null;
096:
097: // this system property can be used to disable the pooling altogether,
098: // in case someone hits an issue with pooling in the production system.
099: if (!Boolean.getBoolean(XMLStreamReaderFactory.class.getName()
100: + ".noPool"))
101: f = Zephyr.newInstance(xif);
102:
103: if (f == null) {
104: // is this Woodstox?
105: if (xif.getClass().getName().equals(
106: "com.ctc.wstx.stax.WstxInputFactory"))
107: f = new Woodstox(xif);
108: }
109:
110: if (f == null)
111: f = new Default(xif);
112:
113: theInstance = f;
114: LOGGER.fine("XMLStreamReaderFactory instance is = "
115: + theInstance);
116: }
117:
118: /**
119: * Overrides the singleton {@link XMLStreamReaderFactory} instance that
120: * the JAX-WS RI uses.
121: */
122: public static void set(XMLStreamReaderFactory f) {
123: if (f == null)
124: throw new IllegalArgumentException();
125: theInstance = f;
126: }
127:
128: public static XMLStreamReaderFactory get() {
129: return theInstance;
130: }
131:
132: public static XMLStreamReader create(InputSource source,
133: boolean rejectDTDs) {
134: try {
135: // Char stream available?
136: if (source.getCharacterStream() != null) {
137: return get().doCreate(source.getSystemId(),
138: source.getCharacterStream(), rejectDTDs);
139: }
140:
141: // Byte stream available?
142: if (source.getByteStream() != null) {
143: return get().doCreate(source.getSystemId(),
144: source.getByteStream(), rejectDTDs);
145: }
146:
147: // Otherwise, open URI
148: return get().doCreate(source.getSystemId(),
149: new URL(source.getSystemId()).openStream(),
150: rejectDTDs);
151: } catch (IOException e) {
152: throw new XMLReaderException("stax.cantCreate", e);
153: }
154: }
155:
156: public static XMLStreamReader create(@Nullable
157: String systemId, InputStream in, boolean rejectDTDs) {
158: return get().doCreate(systemId, in, rejectDTDs);
159: }
160:
161: public static XMLStreamReader create(@Nullable
162: String systemId, InputStream in, @Nullable
163: String encoding, boolean rejectDTDs) {
164: return (encoding == null) ? create(systemId, in, rejectDTDs)
165: : get().doCreate(systemId, in, encoding, rejectDTDs);
166: }
167:
168: public static XMLStreamReader create(@Nullable
169: String systemId, Reader reader, boolean rejectDTDs) {
170: return get().doCreate(systemId, reader, rejectDTDs);
171: }
172:
173: /**
174: * Should be invoked when the code finished using an {@link XMLStreamReader}.
175: *
176: * <p>
177: * If the recycled instance implements {@link RecycleAware},
178: * {@link RecycleAware#onRecycled()} will be invoked to let the instance
179: * know that it's being recycled.
180: *
181: * <p>
182: * It is not a hard requirement to call this method on every {@link XMLStreamReader}
183: * instance. Not doing so just reduces the performance by throwing away
184: * possibly reusable instances. So the caller should always consider the effort
185: * it takes to recycle vs the possible performance gain by doing so.
186: *
187: * <p>
188: * This method may be invked by multiple threads concurrently.
189: *
190: * @param r
191: * The {@link XMLStreamReader} instance that the caller finished using.
192: * This could be any {@link XMLStreamReader} implementation, not just
193: * the ones that were created from this factory. So the implementation
194: * of this class needs to be aware of that.
195: */
196: public static void recycle(XMLStreamReader r) {
197: get().doRecycle(r);
198: }
199:
200: // implementations
201:
202: public abstract XMLStreamReader doCreate(String systemId,
203: InputStream in, boolean rejectDTDs);
204:
205: private XMLStreamReader doCreate(String systemId, InputStream in,
206: @NotNull
207: String encoding, boolean rejectDTDs) {
208: Reader reader;
209: try {
210: reader = new InputStreamReader(in, encoding);
211: } catch (UnsupportedEncodingException ue) {
212: throw new XMLReaderException("stax.cantCreate", ue);
213: }
214: return doCreate(systemId, reader, rejectDTDs);
215: }
216:
217: public abstract XMLStreamReader doCreate(String systemId,
218: Reader reader, boolean rejectDTDs);
219:
220: public abstract void doRecycle(XMLStreamReader r);
221:
222: /**
223: * Interface that can be implemented by {@link XMLStreamReader} to
224: * be notified when it's recycled.
225: *
226: * <p>
227: * This provides a filtering {@link XMLStreamReader} an opportunity to
228: * recycle its inner {@link XMLStreamReader}.
229: */
230: public interface RecycleAware {
231: void onRecycled();
232: }
233:
234: /**
235: * {@link XMLStreamReaderFactory} implementation for SJSXP/JAXP RI.
236: */
237: public static final class Zephyr extends XMLStreamReaderFactory {
238: private final XMLInputFactory xif;
239:
240: private final ThreadLocal<XMLStreamReader> pool = new ThreadLocal<XMLStreamReader>();
241:
242: /**
243: * Sun StAX impl <code>XMLReaderImpl.setInputSource()</code> method via reflection.
244: */
245: private final Method setInputSourceMethod;
246:
247: /**
248: * Sun StAX impl <code>XMLReaderImpl.reset()</code> method via reflection.
249: */
250: private final Method resetMethod;
251:
252: /**
253: * The Sun StAX impl's {@link XMLStreamReader} implementation clas.
254: */
255: private final Class zephyrClass;
256:
257: /**
258: * Creates {@link Zephyr} instance if the given {@link XMLInputFactory} is the one
259: * from Zephyr.
260: */
261: public static @Nullable
262: XMLStreamReaderFactory newInstance(XMLInputFactory xif) {
263: // check if this is from Zephyr
264: try {
265: Class<?> clazz = xif.createXMLStreamReader(
266: new StringReader("<foo/>")).getClass();
267:
268: if (!clazz.getName().startsWith("com.sun.xml.stream."))
269: return null; // nope
270:
271: return new Zephyr(xif, clazz);
272: } catch (NoSuchMethodException e) {
273: return null; // this factory is not for zephyr
274: } catch (XMLStreamException e) {
275: return null; // impossible to fail to parse <foo/>, but anyway
276: }
277: }
278:
279: public Zephyr(XMLInputFactory xif, Class clazz)
280: throws NoSuchMethodException {
281: zephyrClass = clazz;
282: setInputSourceMethod = clazz.getMethod("setInputSource",
283: InputSource.class);
284: resetMethod = clazz.getMethod("reset");
285:
286: try {
287: // Turn OFF internal factory caching in Zephyr.
288: // Santiago told me that this makes it thread-safe.
289: xif.setProperty("reuse-instance", false);
290: } catch (IllegalArgumentException e) {
291: // falls through
292: }
293: this .xif = xif;
294: }
295:
296: /**
297: * Fetchs an instance from the pool if available, otherwise null.
298: */
299: private @Nullable
300: XMLStreamReader fetch() {
301: XMLStreamReader sr = pool.get();
302: if (sr == null)
303: return null;
304: pool.set(null);
305: return sr;
306: }
307:
308: public void doRecycle(XMLStreamReader r) {
309: if (zephyrClass.isInstance(r))
310: pool.set(r);
311: if (r instanceof RecycleAware)
312: ((RecycleAware) r).onRecycled();
313: }
314:
315: public XMLStreamReader doCreate(String systemId,
316: InputStream in, boolean rejectDTDs) {
317: try {
318: XMLStreamReader xsr = fetch();
319: if (xsr == null)
320: return xif.createXMLStreamReader(systemId, in);
321:
322: // try re-using this instance.
323: InputSource is = new InputSource(systemId);
324: is.setByteStream(in);
325: reuse(xsr, is);
326: return xsr;
327: } catch (IllegalAccessException e) {
328: throw new XMLReaderException("stax.cantCreate", e);
329: } catch (InvocationTargetException e) {
330: throw new XMLReaderException("stax.cantCreate", e);
331: } catch (XMLStreamException e) {
332: throw new XMLReaderException("stax.cantCreate", e);
333: }
334: }
335:
336: public XMLStreamReader doCreate(String systemId, Reader in,
337: boolean rejectDTDs) {
338: try {
339: XMLStreamReader xsr = fetch();
340: if (xsr == null)
341: return xif.createXMLStreamReader(systemId, in);
342:
343: // try re-using this instance.
344: InputSource is = new InputSource(systemId);
345: is.setCharacterStream(in);
346: reuse(xsr, is);
347: return xsr;
348: } catch (IllegalAccessException e) {
349: throw new XMLReaderException("stax.cantCreate", e);
350: } catch (InvocationTargetException e) {
351: Throwable cause = e.getCause();
352: if (cause == null) {
353: cause = e;
354: }
355: throw new XMLReaderException("stax.cantCreate", cause);
356: } catch (XMLStreamException e) {
357: throw new XMLReaderException("stax.cantCreate", e);
358: }
359: }
360:
361: private void reuse(XMLStreamReader xsr, InputSource in)
362: throws IllegalAccessException,
363: InvocationTargetException {
364: resetMethod.invoke(xsr);
365: setInputSourceMethod.invoke(xsr, in);
366: }
367: }
368:
369: /**
370: * Default {@link XMLStreamReaderFactory} implementation
371: * that can work with any {@link XMLInputFactory}.
372: *
373: * <p>
374: * {@link XMLInputFactory} is not required to be thread-safe, so the
375: * create method on this implementation is synchronized.
376: */
377: public static final class Default extends NoLock {
378: public Default(XMLInputFactory xif) {
379: super (xif);
380: }
381:
382: public synchronized XMLStreamReader doCreate(String systemId,
383: InputStream in, boolean rejectDTDs) {
384: return super .doCreate(systemId, in, rejectDTDs);
385: }
386:
387: public synchronized XMLStreamReader doCreate(String systemId,
388: Reader in, boolean rejectDTDs) {
389: return super .doCreate(systemId, in, rejectDTDs);
390: }
391: }
392:
393: /**
394: * Similar to {@link Default} but doesn't do any synchronization.
395: *
396: * <p>
397: * This is useful when you know your {@link XMLInputFactory} is thread-safe by itself.
398: */
399: public static class NoLock extends XMLStreamReaderFactory {
400: private final XMLInputFactory xif;
401:
402: public NoLock(XMLInputFactory xif) {
403: this .xif = xif;
404: }
405:
406: public XMLStreamReader doCreate(String systemId,
407: InputStream in, boolean rejectDTDs) {
408: try {
409: return xif.createXMLStreamReader(systemId, in);
410: } catch (XMLStreamException e) {
411: throw new XMLReaderException("stax.cantCreate", e);
412: }
413: }
414:
415: public XMLStreamReader doCreate(String systemId, Reader in,
416: boolean rejectDTDs) {
417: try {
418: return xif.createXMLStreamReader(systemId, in);
419: } catch (XMLStreamException e) {
420: throw new XMLReaderException("stax.cantCreate", e);
421: }
422: }
423:
424: public void doRecycle(XMLStreamReader r) {
425: // there's no way to recycle with the default StAX API.
426: }
427: }
428:
429: /**
430: * Handles Woodstox's XIF but set properties to do the string interning.
431: * Woodstox {@link XMLInputFactory} is thread safe.
432: */
433: public static final class Woodstox extends NoLock {
434: public Woodstox(XMLInputFactory xif) {
435: super (xif);
436: xif.setProperty("org.codehaus.stax2.internNsUris", true);
437: }
438:
439: public XMLStreamReader doCreate(String systemId,
440: InputStream in, boolean rejectDTDs) {
441: return super .doCreate(systemId, in, rejectDTDs);
442: }
443:
444: public XMLStreamReader doCreate(String systemId, Reader in,
445: boolean rejectDTDs) {
446: return super.doCreate(systemId, in, rejectDTDs);
447: }
448: }
449: }
|