src/share/jaxws_classes/com/sun/xml/internal/ws/encoding/XMLHTTPBindingCodec.java

changeset 286
f50545b5e2f1
child 368
0989ad8c0860
equal deleted inserted replaced
284:88b85470e72c 286:f50545b5e2f1
1 /*
2 * Copyright (c) 1997, 2011, 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;
27
28 import com.sun.xml.internal.ws.api.SOAPVersion;
29 import com.sun.xml.internal.ws.api.WSFeatureList;
30 import com.sun.xml.internal.ws.api.message.Packet;
31 import com.sun.xml.internal.ws.api.pipe.Codec;
32 import com.sun.xml.internal.ws.api.pipe.ContentType;
33 import com.sun.xml.internal.ws.client.ContentNegotiation;
34 import com.sun.xml.internal.ws.encoding.xml.XMLCodec;
35 import com.sun.xml.internal.ws.encoding.xml.XMLMessage;
36 import com.sun.xml.internal.ws.encoding.xml.XMLMessage.MessageDataSource;
37 import com.sun.xml.internal.ws.encoding.xml.XMLMessage.UnknownContent;
38 import com.sun.xml.internal.ws.encoding.xml.XMLMessage.XMLMultiPart;
39 import com.sun.xml.internal.ws.resources.StreamingMessages;
40 import com.sun.xml.internal.ws.util.ByteArrayBuffer;
41
42 import javax.activation.DataSource;
43 import javax.xml.ws.WebServiceException;
44 import javax.xml.ws.WebServiceFeature;
45
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.OutputStream;
49 import java.lang.reflect.Method;
50 import java.nio.channels.WritableByteChannel;
51 import java.util.StringTokenizer;
52
53 /**
54 * XML (infoset) over HTTP binding {@link Codec}.
55 * <p>
56 * TODO: Support FI for multipart/related
57 * Support FI for MessageDataSource
58 *
59 * @author Jitendra Kotamraju
60 */
61 public final class XMLHTTPBindingCodec extends MimeCodec {
62 /**
63 * Base HTTP Accept request-header.
64 */
65 private static final String BASE_ACCEPT_VALUE =
66 "*";
67
68 /**
69 * Fast Infoset MIME type.
70 */
71 private static final String APPLICATION_FAST_INFOSET_MIME_TYPE =
72 "application/fastinfoset";
73
74 /**
75 * True if the Fast Infoset codec should be used
76 */
77 private boolean useFastInfosetForEncoding;
78
79 /**
80 * The XML codec
81 */
82 private final Codec xmlCodec;
83
84 /**
85 * The FI codec
86 */
87 private final Codec fiCodec;
88
89 /**
90 * The Accept header for XML encodings
91 */
92 private static final String xmlAccept = null;
93
94 /**
95 * The Accept header for Fast Infoset and XML encodings
96 */
97 private static final String fiXmlAccept = APPLICATION_FAST_INFOSET_MIME_TYPE + ", " + BASE_ACCEPT_VALUE;
98
99 private class AcceptContentType implements ContentType {
100 private ContentType _c;
101 private String _accept;
102
103 public AcceptContentType set(Packet p, ContentType c) {
104 // TODO: need to compose based on underlying codecs
105 if (p.contentNegotiation == ContentNegotiation.optimistic
106 || p.contentNegotiation == ContentNegotiation.pessimistic) {
107 _accept = fiXmlAccept;
108 } else {
109 _accept = xmlAccept;
110 }
111 _c = c;
112 return this;
113 }
114
115 public String getContentType() {
116 return _c.getContentType();
117 }
118
119 public String getSOAPActionHeader() {
120 return _c.getSOAPActionHeader();
121 }
122
123 public String getAcceptHeader() {
124 return _accept;
125 }
126 }
127
128 private AcceptContentType _adaptingContentType = new AcceptContentType();
129
130 public XMLHTTPBindingCodec(WSFeatureList f) {
131 super(SOAPVersion.SOAP_11, f);
132
133 xmlCodec = new XMLCodec(f);
134
135 fiCodec = getFICodec();
136 }
137
138 public String getMimeType() {
139 return null;
140 }
141
142 @Override
143 public ContentType getStaticContentType(Packet packet) {
144 setRootCodec(packet);
145
146 ContentType ct = null;
147 if (packet.getMessage() instanceof MessageDataSource) {
148 final MessageDataSource mds = (MessageDataSource)packet.getMessage();
149 if (mds.hasUnconsumedDataSource()) {
150 ct = getStaticContentType(mds);
151 return (ct != null)
152 ? _adaptingContentType.set(packet, ct)
153 : null;
154 }
155 }
156
157 ct = super.getStaticContentType(packet);
158 return (ct != null)
159 ? _adaptingContentType.set(packet, ct)
160 : null;
161 }
162
163 @Override
164 public ContentType encode(Packet packet, OutputStream out) throws IOException {
165 setRootCodec(packet);
166
167 if (packet.getMessage() instanceof MessageDataSource) {
168 final MessageDataSource mds = (MessageDataSource)packet.getMessage();
169 if (mds.hasUnconsumedDataSource())
170 return _adaptingContentType.set(packet, encode(mds, out));
171 }
172
173 return _adaptingContentType.set(packet, super.encode(packet, out));
174 }
175
176 public ContentType encode(Packet packet, WritableByteChannel buffer) {
177 throw new UnsupportedOperationException();
178 }
179
180 @Override
181 public void decode(InputStream in, String contentType, Packet packet) throws IOException {
182 /**
183 * Reset the encoding state when on the server side for each
184 * decode/encode step.
185 */
186 if (packet.contentNegotiation == null)
187 useFastInfosetForEncoding = false;
188
189 if (contentType == null) {
190 xmlCodec.decode(in, contentType, packet);
191 } else if (isMultipartRelated(contentType)) {
192 packet.setMessage(new XMLMultiPart(contentType, in, features));
193 } else if(isFastInfoset(contentType)) {
194 if (fiCodec == null) {
195 throw new RuntimeException(StreamingMessages.FASTINFOSET_NO_IMPLEMENTATION());
196 }
197
198 useFastInfosetForEncoding = true;
199 fiCodec.decode(in, contentType, packet);
200 } else if (isXml(contentType)) {
201 xmlCodec.decode(in, contentType, packet);
202 } else {
203 packet.setMessage(new UnknownContent(contentType, in));
204 }
205
206 if (!useFastInfosetForEncoding) {
207 useFastInfosetForEncoding = isFastInfosetAcceptable(packet.acceptableMimeTypes);
208 }
209 }
210
211 protected void decode(MimeMultipartParser mpp, Packet packet) throws IOException {
212 // This method will never be invoked
213 }
214
215 public MimeCodec copy() {
216 return new XMLHTTPBindingCodec(features);
217 }
218
219 private boolean isMultipartRelated(String contentType) {
220 return compareStrings(contentType, MimeCodec.MULTIPART_RELATED_MIME_TYPE);
221 }
222
223 private boolean isApplicationXopXml(String contentType) {
224 return compareStrings(contentType, MtomCodec.XOP_XML_MIME_TYPE);
225 }
226
227 private boolean isXml(String contentType) {
228 return compareStrings(contentType, XMLCodec.XML_APPLICATION_MIME_TYPE)
229 || compareStrings(contentType, XMLCodec.XML_TEXT_MIME_TYPE)
230 || (compareStrings(contentType, "application/")&&(contentType.toLowerCase().indexOf("+xml") != -1));
231 }
232
233 private boolean isFastInfoset(String contentType) {
234 return compareStrings(contentType, APPLICATION_FAST_INFOSET_MIME_TYPE);
235 }
236
237 private boolean compareStrings(String a, String b) {
238 return a.length() >= b.length() &&
239 b.equalsIgnoreCase(
240 a.substring(0,
241 b.length()));
242 }
243
244 private boolean isFastInfosetAcceptable(String accept) {
245 if (accept == null) return false;
246
247 StringTokenizer st = new StringTokenizer(accept, ",");
248 while (st.hasMoreTokens()) {
249 final String token = st.nextToken().trim();
250 if (token.equalsIgnoreCase(APPLICATION_FAST_INFOSET_MIME_TYPE)) {
251 return true;
252 }
253 }
254 return false;
255 }
256
257 private ContentType getStaticContentType(MessageDataSource mds) {
258 final String contentType = mds.getDataSource().getContentType();
259 final boolean isFastInfoset = XMLMessage.isFastInfoset(contentType);
260
261 if (!requiresTransformationOfDataSource(isFastInfoset,
262 useFastInfosetForEncoding)) {
263 return new ContentTypeImpl(contentType);
264 } else {
265 return null;
266 }
267 }
268
269 private ContentType encode(MessageDataSource mds, OutputStream out) {
270 try {
271 final boolean isFastInfoset = XMLMessage.isFastInfoset(
272 mds.getDataSource().getContentType());
273 DataSource ds = transformDataSource(mds.getDataSource(),
274 isFastInfoset, useFastInfosetForEncoding, features);
275
276 InputStream is = ds.getInputStream();
277 byte[] buf = new byte[1024];
278 int count;
279 while((count=is.read(buf)) != -1) {
280 out.write(buf, 0, count);
281 }
282 return new ContentTypeImpl(ds.getContentType());
283 } catch(IOException ioe) {
284 throw new WebServiceException(ioe);
285 }
286 }
287
288 private void setRootCodec(Packet p) {
289 /**
290 * The following logic is only for outbound packets
291 * to be encoded by client.
292 * On the server the p.contentNegotiation == null.
293 */
294 if (p.contentNegotiation == ContentNegotiation.none) {
295 // The client may have changed the negotiation property from
296 // pessismistic to none between invocations
297 useFastInfosetForEncoding = false;
298 } else if (p.contentNegotiation == ContentNegotiation.optimistic) {
299 // Always encode using Fast Infoset if in optimisitic mode
300 useFastInfosetForEncoding = true;
301 }
302
303 rootCodec = (useFastInfosetForEncoding && fiCodec != null)
304 ? fiCodec : xmlCodec;
305 }
306
307 public static boolean requiresTransformationOfDataSource(
308 boolean isFastInfoset, boolean useFastInfoset) {
309 return (isFastInfoset && !useFastInfoset) || (!isFastInfoset && useFastInfoset);
310 }
311
312 public static DataSource transformDataSource(DataSource in,
313 boolean isFastInfoset, boolean useFastInfoset, WSFeatureList f) {
314 try {
315 if (isFastInfoset && !useFastInfoset) {
316 // Convert from Fast Infoset to XML
317 Codec codec = new XMLHTTPBindingCodec(f);
318 Packet p = new Packet();
319 codec.decode(in.getInputStream(), in.getContentType(), p);
320
321 p.getMessage().getAttachments();
322 codec.getStaticContentType(p);
323
324 ByteArrayBuffer bos = new ByteArrayBuffer();
325 ContentType ct = codec.encode(p, bos);
326 return XMLMessage.createDataSource(ct.getContentType(), bos.newInputStream());
327 } else if (!isFastInfoset && useFastInfoset) {
328 // Convert from XML to Fast Infoset
329 Codec codec = new XMLHTTPBindingCodec(f);
330 Packet p = new Packet();
331 codec.decode(in.getInputStream(), in.getContentType(), p);
332
333 p.contentNegotiation = ContentNegotiation.optimistic;
334 p.getMessage().getAttachments();
335 codec.getStaticContentType(p);
336
337 ByteArrayBuffer bos = new ByteArrayBuffer();
338 com.sun.xml.internal.ws.api.pipe.ContentType ct = codec.encode(p, bos);
339 return XMLMessage.createDataSource(ct.getContentType(), bos.newInputStream());
340 }
341 } catch(Exception ex) {
342 throw new WebServiceException(ex);
343 }
344
345 return in;
346 }
347
348 /**
349 * Obtain an FI SOAP codec instance using reflection.
350 */
351 private static Codec getFICodec() {
352 try {
353 Class c = Class.forName("com.sun.xml.internal.ws.encoding.fastinfoset.FastInfosetCodec");
354 Method m = c.getMethod("create");
355 return (Codec)m.invoke(null);
356 } catch (Exception e) {
357 return null;
358 }
359 }
360 }

mercurial