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