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: package com.sun.xml.ws.encoding.fastinfoset;
037:
038: import com.sun.xml.fastinfoset.stax.StAXDocumentSerializer;
039: import com.sun.xml.fastinfoset.stax.StAXDocumentParser;
040: import com.sun.xml.fastinfoset.vocab.ParserVocabulary;
041: import com.sun.xml.fastinfoset.vocab.SerializerVocabulary;
042: import com.sun.xml.ws.api.SOAPVersion;
043: import com.sun.xml.ws.api.message.Message;
044: import com.sun.xml.ws.api.message.Messages;
045: import com.sun.xml.ws.api.pipe.Codec;
046: import com.sun.xml.ws.api.pipe.ContentType;
047: import com.sun.xml.ws.api.message.Packet;
048: import com.sun.xml.ws.encoding.ContentTypeImpl;
049: import java.io.BufferedInputStream;
050:
051: import javax.xml.stream.XMLStreamException;
052: import javax.xml.stream.XMLStreamWriter;
053: import javax.xml.stream.XMLStreamReader;
054: import javax.xml.ws.WebServiceException;
055: import java.io.OutputStream;
056: import java.io.InputStream;
057: import java.io.IOException;
058: import java.nio.channels.WritableByteChannel;
059: import java.nio.channels.ReadableByteChannel;
060: import org.jvnet.fastinfoset.FastInfosetSource;
061: import org.jvnet.fastinfoset.stax.FastInfosetStreamReader;
062:
063: /**
064: * A codec for encoding/decoding XML infosets to/from fast
065: * infoset documents.
066: *
067: * @author Paul Sandoz
068: */
069: public class FastInfosetCodec implements Codec {
070: private static final int DEFAULT_INDEXED_STRING_SIZE_LIMIT = 32;
071: private static final int DEFAULT_INDEXED_STRING_MEMORY_LIMIT = 4 * 1024 * 1024; //4M limit
072:
073: private StAXDocumentParser _parser;
074:
075: private StAXDocumentSerializer _serializer;
076:
077: private final boolean _retainState;
078:
079: private final ContentType _contentType;
080:
081: /* package */FastInfosetCodec(boolean retainState) {
082: _retainState = retainState;
083: _contentType = (retainState) ? new ContentTypeImpl(
084: FastInfosetMIMETypes.STATEFUL_INFOSET)
085: : new ContentTypeImpl(FastInfosetMIMETypes.INFOSET);
086: }
087:
088: public String getMimeType() {
089: return _contentType.getContentType();
090: }
091:
092: public Codec copy() {
093: return new FastInfosetCodec(_retainState);
094: }
095:
096: public ContentType getStaticContentType(Packet packet) {
097: return _contentType;
098: }
099:
100: public ContentType encode(Packet packet, OutputStream out) {
101: Message message = packet.getMessage();
102: if (message != null && message.hasPayload()) {
103: final XMLStreamWriter writer = getXMLStreamWriter(out);
104: try {
105: writer.writeStartDocument();
106: packet.getMessage().writePayloadTo(writer);
107: writer.writeEndDocument();
108: writer.flush();
109: } catch (XMLStreamException e) {
110: throw new WebServiceException(e);
111: }
112: }
113:
114: return _contentType;
115: }
116:
117: public ContentType encode(Packet packet, WritableByteChannel buffer) {
118: //TODO: not yet implemented
119: throw new UnsupportedOperationException();
120: }
121:
122: public void decode(InputStream in, String contentType, Packet packet)
123: throws IOException {
124: /* Implements similar logic as the XMLMessage.create(String, InputStream).
125: * But it's faster, as we know the InputStream has FastInfoset content*/
126: Message message = null;
127: in = hasSomeData(in);
128: if (in != null) {
129: message = Messages.createUsingPayload(
130: new FastInfosetSource(in), SOAPVersion.SOAP_11);
131: } else {
132: message = Messages.createEmpty(SOAPVersion.SOAP_11);
133: }
134:
135: packet.setMessage(message);
136: }
137:
138: public void decode(ReadableByteChannel in, String contentType,
139: Packet response) {
140: throw new UnsupportedOperationException();
141: }
142:
143: private XMLStreamWriter getXMLStreamWriter(OutputStream out) {
144: if (_serializer != null) {
145: _serializer.setOutputStream(out);
146: return _serializer;
147: } else {
148: return _serializer = createNewStreamWriter(out,
149: _retainState);
150: }
151: }
152:
153: private XMLStreamReader getXMLStreamReader(InputStream in) {
154: if (_parser != null) {
155: _parser.setInputStream(in);
156: return _parser;
157: } else {
158: return _parser = createNewStreamReader(in, _retainState);
159: }
160: }
161:
162: /**
163: * Creates a new {@link FastInfosetCodec} instance.
164: *
165: * @return a new {@link FastInfosetCodec} instance.
166: */
167: public static FastInfosetCodec create() {
168: return create(false);
169: }
170:
171: /**
172: * Creates a new {@link FastInfosetCodec} instance.
173: *
174: * @param retainState if true the Codec should retain the state of
175: * vocabulary tables for multiple encode/decode invocations.
176: * @return a new {@link FastInfosetCodec} instance.
177: */
178: public static FastInfosetCodec create(boolean retainState) {
179: return new FastInfosetCodec(retainState);
180: }
181:
182: /**
183: * Create a new (@link StAXDocumentSerializer} instance.
184: *
185: * @param in the OutputStream to serialize to.
186: * @param retainState if true the serializer should retain the state of
187: * vocabulary tables for multiple serializations.
188: * @return a new {@link StAXDocumentSerializer} instance.
189: */
190: /* package */static StAXDocumentSerializer createNewStreamWriter(
191: OutputStream out, boolean retainState) {
192: return createNewStreamWriter(out, retainState,
193: DEFAULT_INDEXED_STRING_SIZE_LIMIT,
194: DEFAULT_INDEXED_STRING_MEMORY_LIMIT);
195: }
196:
197: /**
198: * Create a new (@link StAXDocumentSerializer} instance.
199: *
200: * @param in the OutputStream to serialize to.
201: * @param retainState if true the serializer should retain the state of
202: * vocabulary tables for multiple serializations.
203: * @return a new {@link StAXDocumentSerializer} instance.
204: */
205: /* package */static StAXDocumentSerializer createNewStreamWriter(
206: OutputStream out, boolean retainState,
207: int indexedStringSizeLimit, int stringsMemoryLimit) {
208: StAXDocumentSerializer serializer = new StAXDocumentSerializer(
209: out);
210: if (retainState) {
211: /**
212: * Create a serializer vocabulary external to the serializer.
213: * This will ensure that the vocabulary will never be cleared
214: * for each serialization and will be retained (and will grow)
215: * for each serialization
216: */
217: SerializerVocabulary vocabulary = new SerializerVocabulary();
218: serializer.setVocabulary(vocabulary);
219: serializer
220: .setAttributeValueSizeLimit(indexedStringSizeLimit);
221: serializer
222: .setCharacterContentChunkSizeLimit(indexedStringSizeLimit);
223: serializer
224: .setAttributeValueMapMemoryLimit(stringsMemoryLimit);
225: serializer
226: .setCharacterContentChunkMapMemoryLimit(stringsMemoryLimit);
227: }
228: return serializer;
229: }
230:
231: /**
232: * Create a new (@link StAXDocumentParser} instance.
233: *
234: * @param in the InputStream to parse from.
235: * @param retainState if true the parser should retain the state of
236: * vocabulary tables for multiple parses.
237: * @return a new {@link StAXDocumentParser} instance.
238: */
239: /* package */static StAXDocumentParser createNewStreamReader(
240: InputStream in, boolean retainState) {
241: StAXDocumentParser parser = new StAXDocumentParser(in);
242: parser.setStringInterning(true);
243: if (retainState) {
244: /**
245: * Create a parser vocabulary external to the parser.
246: * This will ensure that the vocabulary will never be cleared
247: * for each parse and will be retained (and will grow)
248: * for each parse.
249: */
250: ParserVocabulary vocabulary = new ParserVocabulary();
251: parser.setVocabulary(vocabulary);
252: }
253: return parser;
254: }
255:
256: /**
257: * Create a new (@link StAXDocumentParser} recyclable instance.
258: *
259: * @param in the InputStream to parse from.
260: * @param retainState if true the parser should retain the state of
261: * vocabulary tables for multiple parses.
262: * @return a new recyclable {@link StAXDocumentParser} instance.
263: */
264: /* package */static StAXDocumentParser createNewStreamReaderRecyclable(
265: InputStream in, boolean retainState) {
266: StAXDocumentParser parser = new FastInfosetStreamReaderRecyclable(
267: in);
268: parser.setStringInterning(true);
269: parser.setForceStreamClose(true);
270: if (retainState) {
271: /**
272: * Create a parser vocabulary external to the parser.
273: * This will ensure that the vocabulary will never be cleared
274: * for each parse and will be retained (and will grow)
275: * for each parse.
276: */
277: ParserVocabulary vocabulary = new ParserVocabulary();
278: parser.setVocabulary(vocabulary);
279: }
280: return parser;
281: }
282:
283: /**
284: * Method is copied from com.sun.xml.ws.encoding.xml.XMLMessage
285: * @TODO method should be public in some util package?
286: *
287: * Finds if the stream has some content or not
288: *
289: * @return null if there is no data
290: * else stream to be used
291: */
292: private static InputStream hasSomeData(InputStream in)
293: throws IOException {
294: if (in != null) {
295: if (in.available() < 1) {
296: if (!in.markSupported()) {
297: in = new BufferedInputStream(in);
298: }
299: in.mark(1);
300: if (in.read() != -1) {
301: in.reset();
302: } else {
303: in = null; // No data
304: }
305: }
306: }
307: return in;
308: }
309: }
|