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.Attachment;
042: import com.sun.xml.ws.api.message.Message;
043: import com.sun.xml.ws.api.message.Packet;
044: import com.sun.xml.ws.api.pipe.Codec;
045: import com.sun.xml.ws.api.pipe.ContentType;
046: import com.sun.xml.ws.developer.StreamingAttachmentFeature;
047:
048: import javax.activation.CommandMap;
049: import javax.activation.MailcapCommandMap;
050: import java.io.IOException;
051: import java.io.InputStream;
052: import java.io.OutputStream;
053: import java.nio.channels.ReadableByteChannel;
054: import java.util.UUID;
055:
056: /**
057: * {@link Codec}s that uses the MIME multipart as the underlying format.
058: *
059: * <p>
060: * When the runtime needs to dynamically choose a {@link Codec}, and
061: * when there are more than one {@link Codec}s that use MIME multipart,
062: * it is often impossible to determine the right {@link Codec} unless
063: * you parse the multipart message to some extent.
064: *
065: * <p>
066: * By having all such {@link Codec}s extending from this class,
067: * the "sniffer" can decode a multipart message partially, and then
068: * pass the partial parse result to the ultimately-responsible {@link Codec}.
069: * This improves the performance.
070: *
071: * @author Kohsuke Kawaguchi
072: */
073: abstract class MimeCodec implements Codec {
074:
075: static {
076: // DataHandler.writeTo() may search for DCH. So adding some default ones.
077: try {
078: CommandMap map = CommandMap.getDefaultCommandMap();
079: if (map instanceof MailcapCommandMap) {
080: MailcapCommandMap mailMap = (MailcapCommandMap) map;
081: String hndlrStr = ";;x-java-content-handler=";
082: mailMap.addMailcap("text/xml" + hndlrStr
083: + XmlDataContentHandler.class.getName());
084: mailMap.addMailcap("application/xml" + hndlrStr
085: + XmlDataContentHandler.class.getName());
086: mailMap.addMailcap("image/*" + hndlrStr
087: + ImageDataContentHandler.class.getName());
088: mailMap.addMailcap("text/plain" + hndlrStr
089: + StringDataContentHandler.class.getName());
090: }
091: } catch (Throwable t) {
092: // ignore the exception.
093: }
094: }
095:
096: public static final String MULTIPART_RELATED_MIME_TYPE = "multipart/related";
097: private static final byte[] newline = { '\r', '\n' };
098:
099: private String boundary;
100: private String messageContentType;
101: private boolean hasAttachments;
102: protected Codec rootCodec;
103: protected final SOAPVersion version;
104: protected final WSBinding binding;
105:
106: protected MimeCodec(SOAPVersion version, WSBinding binding) {
107: this .version = version;
108: this .binding = binding;
109: }
110:
111: public String getMimeType() {
112: return MULTIPART_RELATED_MIME_TYPE;
113: }
114:
115: // TODO: preencode String literals to byte[] so that they don't have to
116: // go through char[]->byte[] conversion at runtime.
117: public ContentType encode(Packet packet, OutputStream out)
118: throws IOException {
119: Message msg = packet.getMessage();
120: if (msg == null) {
121: return null;
122: }
123:
124: if (hasAttachments) {
125: writeln("--" + boundary, out);
126: writeln("Content-Type: " + rootCodec.getMimeType(), out);
127: writeln(out);
128: }
129: ContentType primaryCt = rootCodec.encode(packet, out);
130:
131: if (hasAttachments) {
132: writeln(out);
133: // Encode all the attchments
134: for (Attachment att : msg.getAttachments()) {
135: writeln("--" + boundary, out);
136: //SAAJ's AttachmentPart.getContentId() returns content id already enclosed with
137: //angle brackets. For now put angle bracket only if its not there
138: String cid = att.getContentId();
139: if (cid != null && cid.length() > 0
140: && cid.charAt(0) != '<')
141: cid = '<' + cid + '>';
142: writeln("Content-Id:" + cid, out);
143: writeln("Content-Type: " + att.getContentType(), out);
144: writeln("Content-Transfer-Encoding: binary", out);
145: writeln(out); // write \r\n
146: att.writeTo(out);
147: writeln(out); // write \r\n
148: }
149: writeAsAscii("--" + boundary, out);
150: writeAsAscii("--", out);
151: }
152: // TODO not returing correct multipart/related type(no boundary)
153: return hasAttachments ? new ContentTypeImpl(messageContentType,
154: packet.soapAction, null) : primaryCt;
155: }
156:
157: public ContentType getStaticContentType(Packet packet) {
158: Message msg = packet.getMessage();
159: hasAttachments = !msg.getAttachments().isEmpty();
160:
161: if (hasAttachments) {
162: boundary = "uuid:" + UUID.randomUUID().toString();
163: String boundaryParameter = "boundary=\"" + boundary + "\"";
164: // TODO use primaryEncoder to get type
165: messageContentType = MULTIPART_RELATED_MIME_TYPE
166: + "; type=\"" + rootCodec.getMimeType() + "\"; "
167: + boundaryParameter;
168: return new ContentTypeImpl(messageContentType,
169: packet.soapAction, null);
170: } else {
171: return rootCodec.getStaticContentType(packet);
172: }
173: }
174:
175: /**
176: * Copy constructor.
177: */
178: protected MimeCodec(MimeCodec that) {
179: this .version = that.version;
180: this .binding = that.binding;
181: }
182:
183: public void decode(InputStream in, String contentType, Packet packet)
184: throws IOException {
185: MimeMultipartParser parser = new MimeMultipartParser(in,
186: contentType, binding
187: .getFeature(StreamingAttachmentFeature.class));
188: decode(parser, packet);
189: }
190:
191: public void decode(ReadableByteChannel in, String contentType,
192: Packet packet) {
193: throw new UnsupportedOperationException();
194: }
195:
196: /**
197: * Parses a {@link Packet} from a {@link MimeMultipartParser}.
198: */
199: protected abstract void decode(MimeMultipartParser mpp,
200: Packet packet) throws IOException;
201:
202: public abstract MimeCodec copy();
203:
204: public static void writeln(String s, OutputStream out)
205: throws IOException {
206: writeAsAscii(s, out);
207: writeln(out);
208: }
209:
210: /**
211: * Writes a string as ASCII string.
212: */
213: public static void writeAsAscii(String s, OutputStream out)
214: throws IOException {
215: int len = s.length();
216: for (int i = 0; i < len; i++)
217: out.write((byte) s.charAt(i));
218: }
219:
220: public static void writeln(OutputStream out) throws IOException {
221: out.write(newline);
222: }
223: }
|