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  */
25 
26 package com.sun.xml.internal.ws.encoding.fastinfoset;
27 
28 import com.sun.xml.internal.fastinfoset.stax.StAXDocumentSerializer;
29 import com.sun.xml.internal.fastinfoset.stax.StAXDocumentParser;
30 import com.sun.xml.internal.fastinfoset.vocab.ParserVocabulary;
31 import com.sun.xml.internal.fastinfoset.vocab.SerializerVocabulary;
32 import com.sun.xml.internal.ws.api.SOAPVersion;
33 import com.sun.xml.internal.ws.api.message.Message;
34 import com.sun.xml.internal.ws.api.message.Messages;
35 import com.sun.xml.internal.ws.api.pipe.Codec;
36 import com.sun.xml.internal.ws.api.pipe.ContentType;
37 import com.sun.xml.internal.ws.api.message.Packet;
38 import com.sun.xml.internal.ws.encoding.ContentTypeImpl;
39 import java.io.BufferedInputStream;
40 
41 import javax.xml.stream.XMLStreamException;
42 import javax.xml.stream.XMLStreamWriter;
43 import javax.xml.ws.WebServiceException;
44 import java.io.OutputStream;
45 import java.io.InputStream;
46 import java.io.IOException;
47 import java.nio.channels.WritableByteChannel;
48 import java.nio.channels.ReadableByteChannel;
49 import com.sun.xml.internal.org.jvnet.fastinfoset.FastInfosetSource;
50 
51 /**
52  * A codec for encoding/decoding XML infosets to/from fast
53  * infoset documents.
54  *
55  * @author Paul Sandoz
56  */
57 public class FastInfosetCodec implements Codec {
58     private static final int DEFAULT_INDEXED_STRING_SIZE_LIMIT = 32;
59     private static final int DEFAULT_INDEXED_STRING_MEMORY_LIMIT = 4 * 1024 * 1024; //4M limit
60 
61     private StAXDocumentParser _parser;
62 
63     private StAXDocumentSerializer _serializer;
64 
65     private final boolean _retainState;
66 
67     private final ContentType _contentType;
68 
FastInfosetCodec(boolean retainState)69     /* package */ FastInfosetCodec(boolean retainState) {
70         _retainState = retainState;
71         _contentType = (retainState) ? new ContentTypeImpl(FastInfosetMIMETypes.STATEFUL_INFOSET) :
72             new ContentTypeImpl(FastInfosetMIMETypes.INFOSET);
73     }
74 
getMimeType()75     public String getMimeType() {
76         return _contentType.getContentType();
77     }
78 
copy()79     public Codec copy() {
80         return new FastInfosetCodec(_retainState);
81     }
82 
getStaticContentType(Packet packet)83     public ContentType getStaticContentType(Packet packet) {
84         return _contentType;
85     }
86 
encode(Packet packet, OutputStream out)87     public ContentType encode(Packet packet, OutputStream out) {
88         Message message = packet.getMessage();
89         if (message != null && message.hasPayload()) {
90             final XMLStreamWriter writer = getXMLStreamWriter(out);
91             try {
92                 writer.writeStartDocument();
93                 packet.getMessage().writePayloadTo(writer);
94                 writer.writeEndDocument();
95                 writer.flush();
96             } catch (XMLStreamException e) {
97                 throw new WebServiceException(e);
98             }
99         }
100 
101         return _contentType;
102     }
103 
encode(Packet packet, WritableByteChannel buffer)104     public ContentType encode(Packet packet, WritableByteChannel buffer) {
105         //TODO: not yet implemented
106         throw new UnsupportedOperationException();
107     }
108 
decode(InputStream in, String contentType, Packet packet)109     public void decode(InputStream in, String contentType, Packet packet) throws IOException {
110         /* Implements similar logic as the XMLMessage.create(String, InputStream).
111          * But it's faster, as we know the InputStream has FastInfoset content*/
112         Message message;
113         in = hasSomeData(in);
114         if (in != null) {
115             message = Messages.createUsingPayload(new FastInfosetSource(in),
116                     SOAPVersion.SOAP_11);
117         } else {
118             message = Messages.createEmpty(SOAPVersion.SOAP_11);
119         }
120 
121         packet.setMessage(message);
122     }
123 
decode(ReadableByteChannel in, String contentType, Packet response)124     public void decode(ReadableByteChannel in, String contentType, Packet response) {
125         throw new UnsupportedOperationException();
126     }
127 
getXMLStreamWriter(OutputStream out)128     private XMLStreamWriter getXMLStreamWriter(OutputStream out) {
129         if (_serializer != null) {
130             _serializer.setOutputStream(out);
131             return _serializer;
132         } else {
133             return _serializer = createNewStreamWriter(out, _retainState);
134         }
135     }
136 
137     /**
138      * Creates a new {@link FastInfosetCodec} instance.
139      *
140      * @return a new {@link FastInfosetCodec} instance.
141      */
create()142     public static FastInfosetCodec create() {
143         return create(false);
144     }
145 
146     /**
147      * Creates a new {@link FastInfosetCodec} instance.
148      *
149      * @param retainState if true the Codec should retain the state of
150      *        vocabulary tables for multiple encode/decode invocations.
151      * @return a new {@link FastInfosetCodec} instance.
152      */
create(boolean retainState)153     public static FastInfosetCodec create(boolean retainState) {
154         return new FastInfosetCodec(retainState);
155     }
156 
157     /**
158      * Create a new (@link StAXDocumentSerializer} instance.
159      *
160      * @param in the OutputStream to serialize to.
161      * @param retainState if true the serializer should retain the state of
162      *        vocabulary tables for multiple serializations.
163      * @return a new {@link StAXDocumentSerializer} instance.
164      */
createNewStreamWriter(OutputStream out, boolean retainState)165     /* package */ static StAXDocumentSerializer createNewStreamWriter(OutputStream out, boolean retainState) {
166         return createNewStreamWriter(out, retainState, DEFAULT_INDEXED_STRING_SIZE_LIMIT, DEFAULT_INDEXED_STRING_MEMORY_LIMIT);
167     }
168 
169     /**
170      * Create a new (@link StAXDocumentSerializer} instance.
171      *
172      * @param in the OutputStream to serialize to.
173      * @param retainState if true the serializer should retain the state of
174      *        vocabulary tables for multiple serializations.
175      * @return a new {@link StAXDocumentSerializer} instance.
176      */
createNewStreamWriter(OutputStream out, boolean retainState, int indexedStringSizeLimit, int stringsMemoryLimit)177     /* package */ static StAXDocumentSerializer createNewStreamWriter(OutputStream out,
178             boolean retainState, int indexedStringSizeLimit, int stringsMemoryLimit) {
179         StAXDocumentSerializer serializer = new StAXDocumentSerializer(out);
180         if (retainState) {
181             /**
182              * Create a serializer vocabulary external to the serializer.
183              * This will ensure that the vocabulary will never be cleared
184              * for each serialization and will be retained (and will grow)
185              * for each serialization
186              */
187             SerializerVocabulary vocabulary = new SerializerVocabulary();
188             serializer.setVocabulary(vocabulary);
189             serializer.setMinAttributeValueSize(0);
190             serializer.setMaxAttributeValueSize(indexedStringSizeLimit);
191             serializer.setMinCharacterContentChunkSize(0);
192             serializer.setMaxCharacterContentChunkSize(indexedStringSizeLimit);
193             serializer.setAttributeValueMapMemoryLimit(stringsMemoryLimit);
194             serializer.setCharacterContentChunkMapMemoryLimit(stringsMemoryLimit);
195         }
196         return serializer;
197     }
198 
199     /**
200      * Create a new (@link StAXDocumentParser} instance.
201      *
202      * @param in the InputStream to parse from.
203      * @param retainState if true the parser should retain the state of
204      *        vocabulary tables for multiple parses.
205      * @return a new {@link StAXDocumentParser} instance.
206      */
createNewStreamReader(InputStream in, boolean retainState)207     /* package */ static StAXDocumentParser createNewStreamReader(InputStream in, boolean retainState) {
208         StAXDocumentParser parser = new StAXDocumentParser(in);
209         parser.setStringInterning(true);
210         if (retainState) {
211             /**
212              * Create a parser vocabulary external to the parser.
213              * This will ensure that the vocabulary will never be cleared
214              * for each parse and will be retained (and will grow)
215              * for each parse.
216              */
217             ParserVocabulary vocabulary = new ParserVocabulary();
218             parser.setVocabulary(vocabulary);
219         }
220         return parser;
221     }
222 
223     /**
224      * Create a new (@link StAXDocumentParser} recyclable instance.
225      *
226      * @param in the InputStream to parse from.
227      * @param retainState if true the parser should retain the state of
228      *        vocabulary tables for multiple parses.
229      * @return a new recyclable {@link StAXDocumentParser} instance.
230      */
createNewStreamReaderRecyclable(InputStream in, boolean retainState)231     /* package */ static StAXDocumentParser createNewStreamReaderRecyclable(InputStream in, boolean retainState) {
232         StAXDocumentParser parser = new FastInfosetStreamReaderRecyclable(in);
233         parser.setStringInterning(true);
234         parser.setForceStreamClose(true);
235         if (retainState) {
236             /**
237              * Create a parser vocabulary external to the parser.
238              * This will ensure that the vocabulary will never be cleared
239              * for each parse and will be retained (and will grow)
240              * for each parse.
241              */
242             ParserVocabulary vocabulary = new ParserVocabulary();
243             parser.setVocabulary(vocabulary);
244         }
245         return parser;
246     }
247 
248     /**
249      * Method is copied from com.sun.xml.internal.ws.encoding.xml.XMLMessage
250      * @TODO method should be public in some util package?
251      *
252      * Finds if the stream has some content or not
253      *
254      * @return null if there is no data
255      *         else stream to be used
256      */
hasSomeData(InputStream in)257     private static InputStream hasSomeData(InputStream in) throws IOException {
258         if (in != null) {
259             if (in.available() < 1) {
260                 if (!in.markSupported()) {
261                     in = new BufferedInputStream(in);
262                 }
263                 in.mark(1);
264                 if (in.read() != -1) {
265                     in.reset();
266                 } else {
267                     in = null;          // No data
268                 }
269             }
270         }
271         return in;
272     }
273 }
274