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:
043: import javax.xml.stream.XMLOutputFactory;
044: import javax.xml.stream.XMLStreamException;
045: import javax.xml.stream.XMLStreamReader;
046: import javax.xml.stream.XMLStreamWriter;
047: import javax.xml.transform.stream.StreamResult;
048: import javax.xml.ws.WebServiceException;
049: import java.io.OutputStream;
050: import java.io.StringWriter;
051: import java.lang.reflect.InvocationTargetException;
052: import java.lang.reflect.Method;
053: import java.util.logging.Logger;
054:
055: /**
056: * Factory for {@link XMLStreamWriter}.
057: *
058: * <p>
059: * This wraps {@link XMLOutputFactory} and allows us to reuse {@link XMLStreamWriter} instances
060: * when appropriate.
061: *
062: * @author Kohsuke Kawaguchi
063: */
064: public abstract class XMLStreamWriterFactory {
065:
066: private static final Logger LOGGER = Logger
067: .getLogger(XMLStreamWriterFactory.class.getName());
068:
069: /**
070: * Singleton instance.
071: */
072: private static volatile @NotNull
073: XMLStreamWriterFactory theInstance;
074:
075: static {
076: XMLOutputFactory xof = null;
077: if (Boolean.getBoolean(XMLStreamWriterFactory.class.getName()
078: + ".woodstox")) {
079: try {
080: xof = (XMLOutputFactory) Class.forName(
081: "com.ctc.wstx.stax.WstxOutputFactory")
082: .newInstance();
083: } catch (Exception e) {
084: // Ignore and fallback to default XMLOutputFactory
085: }
086: }
087: if (xof == null) {
088: xof = XMLOutputFactory.newInstance();
089: }
090:
091: XMLStreamWriterFactory f = null;
092:
093: // this system property can be used to disable the pooling altogether,
094: // in case someone hits an issue with pooling in the production system.
095: if (!Boolean.getBoolean(XMLStreamWriterFactory.class.getName()
096: + ".noPool"))
097: f = Zephyr.newInstance(xof);
098: if (f == null) {
099: // is this Woodstox?
100: if (xof.getClass().getName().equals(
101: "com.ctc.wstx.stax.WstxOutputFactory"))
102: f = new NoLock(xof);
103: }
104: if (f == null)
105: f = new Default(xof);
106:
107: theInstance = f;
108: LOGGER.fine("XMLStreamWriterFactory instance is = "
109: + theInstance);
110: }
111:
112: /**
113: * See {@link #create(OutputStream)} for the contract.
114: * This method may be invoked concurrently.
115: */
116: public abstract XMLStreamWriter doCreate(OutputStream out);
117:
118: /**
119: * See {@link #create(OutputStream,String)} for the contract.
120: * This method may be invoked concurrently.
121: */
122: public abstract XMLStreamWriter doCreate(OutputStream out,
123: String encoding);
124:
125: /**
126: * See {@link #recycle(XMLStreamWriter)} for the contract.
127: * This method may be invoked concurrently.
128: */
129: public abstract void doRecycle(XMLStreamWriter r);
130:
131: /**
132: * Should be invoked when the code finished using an {@link XMLStreamWriter}.
133: *
134: * <p>
135: * If the recycled instance implements {@link RecycleAware},
136: * {@link RecycleAware#onRecycled()} will be invoked to let the instance
137: * know that it's being recycled.
138: *
139: * <p>
140: * It is not a hard requirement to call this method on every {@link XMLStreamReader}
141: * instance. Not doing so just reduces the performance by throwing away
142: * possibly reusable instances. So the caller should always consider the effort
143: * it takes to recycle vs the possible performance gain by doing so.
144: *
145: * <p>
146: * This method may be invked by multiple threads concurrently.
147: *
148: * @param r
149: * The {@link XMLStreamReader} instance that the caller finished using.
150: * This could be any {@link XMLStreamReader} implementation, not just
151: * the ones that were created from this factory. So the implementation
152: * of this class needs to be aware of that.
153: */
154: public static void recycle(XMLStreamWriter r) {
155: get().doRecycle(r);
156: }
157:
158: /**
159: * Interface that can be implemented by {@link XMLStreamWriter} to
160: * be notified when it's recycled.
161: *
162: * <p>
163: * This provides a filtering {@link XMLStreamWriter} an opportunity to
164: * recycle its inner {@link XMLStreamWriter}.
165: */
166: public interface RecycleAware {
167: void onRecycled();
168: }
169:
170: /**
171: * Gets the singleton instance.
172: */
173: public static @NotNull
174: XMLStreamWriterFactory get() {
175: return theInstance;
176: }
177:
178: /**
179: * Overrides the singleton {@link XMLStreamWriterFactory} instance that
180: * the JAX-WS RI uses.
181: *
182: * @param f
183: * must not be null.
184: */
185: public static void set(@NotNull
186: XMLStreamWriterFactory f) {
187: if (f == null)
188: throw new IllegalArgumentException();
189: theInstance = f;
190: }
191:
192: /**
193: * Short-cut for {@link #create(OutputStream, String)} with UTF-8.
194: */
195: public static XMLStreamWriter create(OutputStream out) {
196: return get().doCreate(out);
197: }
198:
199: public static XMLStreamWriter create(OutputStream out,
200: String encoding) {
201: return get().doCreate(out, encoding);
202: }
203:
204: /**
205: * @deprecated
206: * Use {@link #create(OutputStream)}
207: */
208: public static XMLStreamWriter createXMLStreamWriter(OutputStream out) {
209: return create(out);
210: }
211:
212: /**
213: * @deprecated
214: * Use {@link #create(OutputStream, String)}
215: */
216: public static XMLStreamWriter createXMLStreamWriter(
217: OutputStream out, String encoding) {
218: return create(out, encoding);
219: }
220:
221: /**
222: * @deprecated
223: * Use {@link #create(OutputStream, String)}. The boolean flag was unused anyway.
224: */
225: public static XMLStreamWriter createXMLStreamWriter(
226: OutputStream out, String encoding, boolean declare) {
227: return create(out, encoding);
228: }
229:
230: /**
231: * Default {@link XMLStreamWriterFactory} implementation
232: * that can work with any {@link XMLOutputFactory}.
233: *
234: * <p>
235: * {@link XMLOutputFactory} is not required to be thread-safe, so the
236: * create method on this implementation is synchronized.
237: */
238: public static final class Default extends XMLStreamWriterFactory {
239: private final XMLOutputFactory xof;
240:
241: public Default(XMLOutputFactory xof) {
242: this .xof = xof;
243: }
244:
245: public XMLStreamWriter doCreate(OutputStream out) {
246: return doCreate(out, "UTF-8");
247: }
248:
249: public synchronized XMLStreamWriter doCreate(OutputStream out,
250: String encoding) {
251: try {
252: return xof.createXMLStreamWriter(out, encoding);
253: } catch (XMLStreamException e) {
254: throw new XMLReaderException("stax.cantCreate", e);
255: }
256: }
257:
258: public void doRecycle(XMLStreamWriter r) {
259: // no recycling
260: }
261: }
262:
263: /**
264: * {@link XMLStreamWriterFactory} implementation for Sun's StaX implementation.
265: *
266: * <p>
267: * This implementation supports instance reuse.
268: */
269: public static final class Zephyr extends XMLStreamWriterFactory {
270: private final XMLOutputFactory xof;
271: private final ThreadLocal<XMLStreamWriter> pool = new ThreadLocal<XMLStreamWriter>();
272: private final Method resetMethod;
273: private final Method setOutputMethod;
274: private final Class zephyrClass;
275:
276: public static XMLStreamWriterFactory newInstance(
277: XMLOutputFactory xof) {
278: try {
279: Class<?> clazz = xof.createXMLStreamWriter(
280: new StringWriter()).getClass();
281:
282: if (!clazz.getName().startsWith("com.sun.xml.stream."))
283: return null; // nope
284:
285: return new Zephyr(xof, clazz);
286: } catch (XMLStreamException e) {
287: return null; // impossible
288: } catch (NoSuchMethodException e) {
289: return null; // this xof wasn't Zephyr
290: }
291: }
292:
293: private Zephyr(XMLOutputFactory xof, Class clazz)
294: throws NoSuchMethodException {
295: this .xof = xof;
296:
297: zephyrClass = clazz;
298: setOutputMethod = clazz.getMethod("setOutput",
299: StreamResult.class, String.class);
300: resetMethod = clazz.getMethod("reset");
301: }
302:
303: /**
304: * Fetchs an instance from the pool if available, otherwise null.
305: */
306: private @Nullable
307: XMLStreamWriter fetch() {
308: XMLStreamWriter sr = pool.get();
309: if (sr == null)
310: return null;
311: pool.set(null);
312: return sr;
313: }
314:
315: public XMLStreamWriter doCreate(OutputStream out) {
316: return doCreate(out, "UTF-8");
317: }
318:
319: public XMLStreamWriter doCreate(OutputStream out,
320: String encoding) {
321: XMLStreamWriter xsw = fetch();
322: if (xsw != null) {
323: // try to reuse
324: try {
325: resetMethod.invoke(xsw);
326: setOutputMethod.invoke(xsw, new StreamResult(out),
327: encoding);
328: return xsw;
329: } catch (IllegalAccessException e) {
330: throw new XMLReaderException("stax.cantCreate", e);
331: } catch (InvocationTargetException e) {
332: throw new XMLReaderException("stax.cantCreate", e);
333: }
334: }
335:
336: // create a new instance
337: try {
338: return xof.createXMLStreamWriter(out, encoding);
339: } catch (XMLStreamException e) {
340: throw new XMLReaderException("stax.cantCreate", e);
341: }
342: }
343:
344: public void doRecycle(XMLStreamWriter r) {
345: if (zephyrClass.isInstance(r)) {
346: // this flushes the underlying stream, so it might cause chunking issue
347: try {
348: r.close();
349: } catch (XMLStreamException e) {
350: throw new WebServiceException(e);
351: }
352: pool.set(r);
353: }
354: if (r instanceof RecycleAware)
355: ((RecycleAware) r).onRecycled();
356: }
357: }
358:
359: /**
360: *
361: * For {@link javax.xml.stream.XMLOutputFactory} is thread safe.
362: */
363: public static final class NoLock extends XMLStreamWriterFactory {
364: private final XMLOutputFactory xof;
365:
366: public NoLock(XMLOutputFactory xof) {
367: this .xof = xof;
368: }
369:
370: public XMLStreamWriter doCreate(OutputStream out) {
371: return doCreate(out, "UTF-8");
372: }
373:
374: public XMLStreamWriter doCreate(OutputStream out,
375: String encoding) {
376: try {
377: return xof.createXMLStreamWriter(out, encoding);
378: } catch (XMLStreamException e) {
379: throw new XMLReaderException("stax.cantCreate", e);
380: }
381: }
382:
383: public void doRecycle(XMLStreamWriter r) {
384: // no recycling
385: }
386:
387: }
388: }
|