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.encoding;
038:
039: import com.sun.xml.ws.api.SOAPVersion;
040: import com.sun.xml.ws.api.WSBinding;
041: import com.sun.xml.ws.api.message.Packet;
042: import com.sun.xml.ws.api.pipe.Codec;
043: import com.sun.xml.ws.api.pipe.ContentType;
044: import com.sun.xml.ws.client.ContentNegotiation;
045: import com.sun.xml.ws.developer.StreamingAttachmentFeature;
046: import com.sun.xml.ws.encoding.xml.XMLCodec;
047: import com.sun.xml.ws.encoding.xml.XMLMessage;
048: import com.sun.xml.ws.encoding.xml.XMLMessage.MessageDataSource;
049: import com.sun.xml.ws.encoding.xml.XMLMessage.UnknownContent;
050: import com.sun.xml.ws.encoding.xml.XMLMessage.XMLMultiPart;
051: import com.sun.xml.ws.resources.StreamingMessages;
052: import com.sun.xml.ws.util.ByteArrayBuffer;
053:
054: import javax.activation.DataSource;
055: import javax.xml.ws.WebServiceException;
056: import java.io.IOException;
057: import java.io.InputStream;
058: import java.io.OutputStream;
059: import java.lang.reflect.Method;
060: import java.nio.channels.WritableByteChannel;
061: import java.util.StringTokenizer;
062:
063: /**
064: * XML (infoset) over HTTP binding {@link Codec}.
065: * <p>
066: * TODO: Support FI for multipart/related
067: * Support FI for MessageDataSource
068: *
069: * @author Jitendra Kotamraju
070: */
071: public final class XMLHTTPBindingCodec extends MimeCodec {
072: /**
073: * Base HTTP Accept request-header.
074: */
075: private static final String BASE_ACCEPT_VALUE = "*";
076:
077: /**
078: * Fast Infoset MIME type.
079: */
080: private static final String APPLICATION_FAST_INFOSET_MIME_TYPE = "application/fastinfoset";
081:
082: /**
083: * True if the Fast Infoset codec should be used
084: */
085: private boolean useFastInfosetForEncoding;
086:
087: /**
088: * The XML codec
089: */
090: private final Codec xmlCodec;
091:
092: /**
093: * The FI codec
094: */
095: private final Codec fiCodec;
096:
097: /**
098: * The Accept header for XML encodings
099: */
100: private final String xmlAccept;
101:
102: /**
103: * The Accept header for Fast Infoset and XML encodings
104: */
105: private final String fiXmlAccept;
106:
107: private class AcceptContentType implements ContentType {
108: private ContentType _c;
109: private String _accept;
110:
111: public AcceptContentType set(Packet p, ContentType c) {
112: // TODO: need to compose based on underlying codecs
113: if (p.contentNegotiation == ContentNegotiation.optimistic
114: || p.contentNegotiation == ContentNegotiation.pessimistic) {
115: _accept = fiXmlAccept;
116: } else {
117: _accept = xmlAccept;
118: }
119: _c = c;
120: return this ;
121: }
122:
123: public String getContentType() {
124: return _c.getContentType();
125: }
126:
127: public String getSOAPActionHeader() {
128: return _c.getSOAPActionHeader();
129: }
130:
131: public String getAcceptHeader() {
132: return _accept;
133: }
134: }
135:
136: private AcceptContentType _adaptingContentType = new AcceptContentType();
137:
138: public XMLHTTPBindingCodec(WSBinding binding) {
139: super (SOAPVersion.SOAP_11, binding);
140:
141: xmlCodec = new XMLCodec(binding);
142:
143: fiCodec = getFICodec();
144:
145: xmlAccept = null;
146:
147: fiXmlAccept = APPLICATION_FAST_INFOSET_MIME_TYPE + ", "
148: + BASE_ACCEPT_VALUE;
149: }
150:
151: public String getMimeType() {
152: return null;
153: }
154:
155: @Override
156: public ContentType getStaticContentType(Packet packet) {
157: setRootCodec(packet);
158:
159: ContentType ct = null;
160: if (packet.getMessage() instanceof MessageDataSource) {
161: final MessageDataSource mds = (MessageDataSource) packet
162: .getMessage();
163: if (mds.hasUnconsumedDataSource()) {
164: ct = getStaticContentType(mds);
165: return (ct != null) ? _adaptingContentType.set(packet,
166: ct) : null;
167: }
168: }
169:
170: ct = super .getStaticContentType(packet);
171: return (ct != null) ? _adaptingContentType.set(packet, ct)
172: : null;
173: }
174:
175: @Override
176: public ContentType encode(Packet packet, OutputStream out)
177: throws IOException {
178: setRootCodec(packet);
179:
180: if (packet.getMessage() instanceof MessageDataSource) {
181: final MessageDataSource mds = (MessageDataSource) packet
182: .getMessage();
183: if (mds.hasUnconsumedDataSource())
184: return _adaptingContentType.set(packet,
185: encode(mds, out));
186: }
187:
188: return _adaptingContentType.set(packet, super .encode(packet,
189: out));
190: }
191:
192: public ContentType encode(Packet packet, WritableByteChannel buffer) {
193: throw new UnsupportedOperationException();
194: }
195:
196: @Override
197: public void decode(InputStream in, String contentType, Packet packet)
198: throws IOException {
199: /**
200: * Reset the encoding state when on the server side for each
201: * decode/encode step.
202: */
203: if (packet.contentNegotiation == null)
204: useFastInfosetForEncoding = false;
205:
206: if (contentType == null) {
207: xmlCodec.decode(in, contentType, packet);
208: } else if (isMultipartRelated(contentType)) {
209: packet.setMessage(new XMLMultiPart(contentType, in, binding
210: .getFeature(StreamingAttachmentFeature.class)));
211: } else if (isFastInfoset(contentType)) {
212: if (fiCodec == null) {
213: throw new RuntimeException(StreamingMessages
214: .FASTINFOSET_NO_IMPLEMENTATION());
215: }
216:
217: useFastInfosetForEncoding = true;
218: fiCodec.decode(in, contentType, packet);
219: } else if (isXml(contentType)) {
220: xmlCodec.decode(in, contentType, packet);
221: } else {
222: packet.setMessage(new UnknownContent(contentType, in));
223: }
224:
225: if (!useFastInfosetForEncoding) {
226: useFastInfosetForEncoding = isFastInfosetAcceptable(packet.acceptableMimeTypes);
227: }
228: }
229:
230: protected void decode(MimeMultipartParser mpp, Packet packet)
231: throws IOException {
232: // This method will never be invoked
233: }
234:
235: public MimeCodec copy() {
236: return new XMLHTTPBindingCodec(binding);
237: }
238:
239: private boolean isMultipartRelated(String contentType) {
240: return compareStrings(contentType,
241: MimeCodec.MULTIPART_RELATED_MIME_TYPE);
242: }
243:
244: private boolean isApplicationXopXml(String contentType) {
245: return compareStrings(contentType, MtomCodec.XOP_XML_MIME_TYPE);
246: }
247:
248: private boolean isXml(String contentType) {
249: return compareStrings(contentType,
250: XMLCodec.XML_APPLICATION_MIME_TYPE)
251: || compareStrings(contentType,
252: XMLCodec.XML_TEXT_MIME_TYPE)
253: || (compareStrings(contentType, "application/") && (contentType
254: .toLowerCase().indexOf("+xml") != -1));
255: }
256:
257: private boolean isFastInfoset(String contentType) {
258: return compareStrings(contentType,
259: APPLICATION_FAST_INFOSET_MIME_TYPE);
260: }
261:
262: private boolean compareStrings(String a, String b) {
263: return a.length() >= b.length()
264: && b.equalsIgnoreCase(a.substring(0, b.length()));
265: }
266:
267: private boolean isFastInfosetAcceptable(String accept) {
268: if (accept == null)
269: return false;
270:
271: StringTokenizer st = new StringTokenizer(accept, ",");
272: while (st.hasMoreTokens()) {
273: final String token = st.nextToken().trim();
274: if (token
275: .equalsIgnoreCase(APPLICATION_FAST_INFOSET_MIME_TYPE)) {
276: return true;
277: }
278: }
279: return false;
280: }
281:
282: private ContentType getStaticContentType(MessageDataSource mds) {
283: final String contentType = mds.getDataSource().getContentType();
284: final boolean isFastInfoset = XMLMessage
285: .isFastInfoset(contentType);
286:
287: if (!requiresTransformationOfDataSource(isFastInfoset,
288: useFastInfosetForEncoding)) {
289: return new ContentTypeImpl(contentType);
290: } else {
291: return null;
292: }
293: }
294:
295: private ContentType encode(MessageDataSource mds, OutputStream out) {
296: try {
297: final boolean isFastInfoset = XMLMessage.isFastInfoset(mds
298: .getDataSource().getContentType());
299: DataSource ds = transformDataSource(mds.getDataSource(),
300: isFastInfoset, useFastInfosetForEncoding, binding);
301:
302: InputStream is = ds.getInputStream();
303: byte[] buf = new byte[1024];
304: int count;
305: while ((count = is.read(buf)) != -1) {
306: out.write(buf, 0, count);
307: }
308: return new ContentTypeImpl(ds.getContentType());
309: } catch (IOException ioe) {
310: throw new WebServiceException(ioe);
311: }
312: }
313:
314: private void setRootCodec(Packet p) {
315: /**
316: * The following logic is only for outbound packets
317: * to be encoded by client.
318: * On the server the p.contentNegotiation == null.
319: */
320: if (p.contentNegotiation == ContentNegotiation.none) {
321: // The client may have changed the negotiation property from
322: // pessismistic to none between invocations
323: useFastInfosetForEncoding = false;
324: } else if (p.contentNegotiation == ContentNegotiation.optimistic) {
325: // Always encode using Fast Infoset if in optimisitic mode
326: useFastInfosetForEncoding = true;
327: }
328:
329: rootCodec = (useFastInfosetForEncoding && fiCodec != null) ? fiCodec
330: : xmlCodec;
331: }
332:
333: public static boolean requiresTransformationOfDataSource(
334: boolean isFastInfoset, boolean useFastInfoset) {
335: return (isFastInfoset && !useFastInfoset)
336: || (!isFastInfoset && useFastInfoset);
337: }
338:
339: public static DataSource transformDataSource(DataSource in,
340: boolean isFastInfoset, boolean useFastInfoset,
341: WSBinding binding) {
342: try {
343: if (isFastInfoset && !useFastInfoset) {
344: // Convert from Fast Infoset to XML
345: Codec codec = new XMLHTTPBindingCodec(binding);
346: Packet p = new Packet();
347: codec.decode(in.getInputStream(), in.getContentType(),
348: p);
349:
350: p.getMessage().getAttachments();
351: codec.getStaticContentType(p);
352:
353: ByteArrayBuffer bos = new ByteArrayBuffer();
354: ContentType ct = codec.encode(p, bos);
355: return XMLMessage.createDataSource(ct.getContentType(),
356: bos.newInputStream());
357: } else if (!isFastInfoset && useFastInfoset) {
358: // Convert from XML to Fast Infoset
359: Codec codec = new XMLHTTPBindingCodec(binding);
360: Packet p = new Packet();
361: codec.decode(in.getInputStream(), in.getContentType(),
362: p);
363:
364: p.contentNegotiation = ContentNegotiation.optimistic;
365: p.getMessage().getAttachments();
366: codec.getStaticContentType(p);
367:
368: ByteArrayBuffer bos = new ByteArrayBuffer();
369: com.sun.xml.ws.api.pipe.ContentType ct = codec.encode(
370: p, bos);
371: return XMLMessage.createDataSource(ct.getContentType(),
372: bos.newInputStream());
373: }
374: } catch (Exception ex) {
375: throw new WebServiceException(ex);
376: }
377:
378: return in;
379: }
380:
381: /**
382: * Obtain an FI SOAP codec instance using reflection.
383: */
384: private static Codec getFICodec() {
385: try {
386: Class c = Class
387: .forName("com.sun.xml.ws.encoding.fastinfoset.FastInfosetCodec");
388: Method m = c.getMethod("create");
389: return (Codec) m.invoke(null);
390: } catch (Exception e) {
391: return null;
392: }
393: }
394: }
|