0001: /*
0002: * Portions Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.xml.internal.ws.encoding.xml;
0027:
0028: import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException;
0029: import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.ContentType;
0030: import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.InternetHeaders;
0031: import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeBodyPart;
0032: import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeMultipart;
0033: import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
0034: import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
0035: import com.sun.xml.internal.ws.encoding.jaxb.JAXBTypeSerializer;
0036: import com.sun.xml.internal.ws.protocol.xml.XMLMessageException;
0037: import com.sun.xml.internal.ws.spi.runtime.WSConnection;
0038: import com.sun.xml.internal.ws.streaming.XMLStreamWriterFactory;
0039: import com.sun.xml.internal.ws.util.ByteArrayBuffer;
0040: import com.sun.xml.internal.ws.util.FastInfosetReflection;
0041: import com.sun.xml.internal.ws.util.FastInfosetUtil;
0042: import com.sun.xml.internal.ws.util.xml.XmlUtil;
0043: import java.io.BufferedInputStream;
0044: import java.util.HashMap;
0045: import java.util.Map;
0046: import java.util.Properties;
0047: import javax.activation.DataHandler;
0048:
0049: import javax.activation.DataSource;
0050: import javax.xml.bind.JAXBContext;
0051: import javax.xml.soap.MimeHeaders;
0052: import javax.xml.transform.OutputKeys;
0053: import javax.xml.transform.Source;
0054: import javax.xml.transform.Transformer;
0055: import javax.xml.transform.dom.DOMSource;
0056: import javax.xml.transform.stream.StreamResult;
0057: import javax.xml.transform.stream.StreamSource;
0058: import javax.xml.ws.WebServiceException;
0059: import javax.xml.ws.http.HTTPException;
0060: import java.io.ByteArrayInputStream;
0061: import java.io.IOException;
0062: import java.io.InputStream;
0063: import java.io.OutputStream;
0064: import java.util.logging.Logger;
0065:
0066: /**
0067: *
0068: * @author WS Developement Team
0069: */
0070: public final class XMLMessage {
0071:
0072: private static final Logger log = Logger
0073: .getLogger(com.sun.xml.internal.ws.util.Constants.LoggingDomain
0074: + ".protocol.xml");
0075:
0076: // So that SAAJ registers DCHs for MIME types
0077: static {
0078: new com.sun.xml.internal.messaging.saaj.soap.AttachmentPartImpl();
0079: }
0080:
0081: private static final int PLAIN_XML_FLAG = 1; // 00001
0082: private static final int MIME_MULTIPART_FLAG = 2; // 00010
0083: private static final int FI_ENCODED_FLAG = 16; // 10000
0084:
0085: private final DataRepresentation data;
0086:
0087: /**
0088: * Indicates when Fast Infoset should be used to serialize
0089: * this message.
0090: */
0091: protected boolean useFastInfoset = false;
0092:
0093: /**
0094: * Construct a message from an input stream. When messages are
0095: * received, there's two parts -- the transport headers and the
0096: * message content in a transport specific stream.
0097: */
0098: public XMLMessage(MimeHeaders headers, final InputStream in) {
0099: String ct = null;
0100: if (headers != null) {
0101: ct = getContentType(headers);
0102: }
0103: this .data = getData(ct, in);
0104: // TODO should headers be set on the data?
0105: }
0106:
0107: /**
0108: * Finds if the stream has some content or not
0109: *
0110: * @return null if there is no data
0111: * else stream to be used
0112: */
0113: private InputStream hasSomeData(InputStream in) throws IOException {
0114: if (in != null) {
0115: if (in.available() < 1) {
0116: if (!in.markSupported()) {
0117: in = new BufferedInputStream(in);
0118: }
0119: in.mark(1);
0120: if (in.read() != -1) {
0121: in.reset();
0122: } else {
0123: in = null; // No data
0124: }
0125: }
0126: }
0127: return in;
0128: }
0129:
0130: private DataRepresentation getData(final String ct, InputStream in) {
0131: DataRepresentation data;
0132: try {
0133: in = hasSomeData(in);
0134: if (in == null) {
0135: return new NullContent();
0136: }
0137: if (ct != null) {
0138: ContentType contentType = new ContentType(ct);
0139: int contentTypeId = identifyContentType(contentType);
0140: boolean isFastInfoset = (contentTypeId & FI_ENCODED_FLAG) > 0;
0141: if ((contentTypeId & MIME_MULTIPART_FLAG) != 0) {
0142: data = new XMLMultiPart(ct, in, isFastInfoset);
0143: } else if ((contentTypeId & PLAIN_XML_FLAG) != 0
0144: || (contentTypeId & FI_ENCODED_FLAG) != 0) {
0145: data = new XMLSource(in, isFastInfoset);
0146: } else {
0147: data = new UnknownContent(ct, in);
0148: }
0149: } else {
0150: data = new NullContent();
0151: }
0152: } catch (Exception ex) {
0153: throw new WebServiceException(ex);
0154: }
0155: return data;
0156: }
0157:
0158: public XMLMessage(Source source, boolean useFastInfoset) {
0159: this .data = (source == null) ? new NullContent()
0160: : new XMLSource(source);
0161: this .useFastInfoset = useFastInfoset;
0162:
0163: this .data.getMimeHeaders()
0164: .addHeader(
0165: "Content-Type",
0166: useFastInfoset ? "application/fastinfoset"
0167: : "text/xml");
0168: }
0169:
0170: public XMLMessage(Exception err, boolean useFastInfoset) {
0171: this .data = new XMLErr(err);
0172: this .useFastInfoset = useFastInfoset;
0173:
0174: this .data.getMimeHeaders()
0175: .addHeader(
0176: "Content-Type",
0177: useFastInfoset ? "application/fastinfoset"
0178: : "text/xml");
0179: }
0180:
0181: public XMLMessage(DataSource ds, boolean useFastInfoset) {
0182: this .useFastInfoset = useFastInfoset;
0183: try {
0184: this .data = (ds == null) ? new NullContent() : getData(ds
0185: .getContentType(), ds.getInputStream());
0186: } catch (IOException ioe) {
0187: throw new WebServiceException(ioe);
0188: }
0189:
0190: String contentType = (ds != null) ? ds.getContentType() : null;
0191: contentType = (contentType == null) ? contentType = "text/xml"
0192: : contentType;
0193: this .data.getMimeHeaders().addHeader(
0194: "Content-Type",
0195: !useFastInfoset ? contentType : contentType
0196: .replaceFirst("text/xml",
0197: "application/fastinfoset"));
0198: }
0199:
0200: public XMLMessage(Object object, JAXBContext context,
0201: boolean useFastInfoset) {
0202: this .data = (object == null) ? new NullContent() : new XMLJaxb(
0203: object, context);
0204: this .useFastInfoset = useFastInfoset;
0205:
0206: this .data.getMimeHeaders()
0207: .addHeader(
0208: "Content-Type",
0209: useFastInfoset ? "application/fastinfoset"
0210: : "text/xml");
0211: }
0212:
0213: public XMLMessage(Source source,
0214: Map<String, DataHandler> attachments, boolean useFastInfoset) {
0215: if (attachments == null) {
0216: this .data = (source == null) ? new NullContent()
0217: : new XMLSource(source);
0218: } else {
0219: if (source == null) {
0220: this .data = new UnknownContent(attachments);
0221: } else {
0222: this .data = new XMLMultiPart(source, attachments,
0223: useFastInfoset);
0224: }
0225: }
0226:
0227: this .useFastInfoset = useFastInfoset;
0228: this .data.getMimeHeaders()
0229: .addHeader(
0230: "Content-Type",
0231: useFastInfoset ? "application/fastinfoset"
0232: : "text/xml");
0233: }
0234:
0235: public XMLMessage(Object object, JAXBContext context,
0236: Map<String, DataHandler> attachments, boolean useFastInfoset) {
0237: if (attachments == null) {
0238: this .data = (object == null) ? new NullContent()
0239: : new XMLJaxb(object, context);
0240: } else {
0241: if (object == null) {
0242: this .data = new UnknownContent(attachments);
0243: } else {
0244: this .data = new XMLMultiPart(JAXBTypeSerializer
0245: .serialize(object, context), attachments,
0246: useFastInfoset);
0247: }
0248: }
0249:
0250: this .useFastInfoset = useFastInfoset;
0251: this .data.getMimeHeaders()
0252: .addHeader(
0253: "Content-Type",
0254: useFastInfoset ? "application/fastinfoset"
0255: : "text/xml");
0256: }
0257:
0258: /**
0259: * Returns true if the underlying encoding of this message is FI.
0260: */
0261: public boolean isFastInfoset() {
0262: return data.isFastInfoset();
0263: }
0264:
0265: /**
0266: * Returns true if the FI encoding should be used.
0267: */
0268: public boolean useFastInfoset() {
0269: return useFastInfoset;
0270: }
0271:
0272: /**
0273: * Returns true if the sender of this message accepts FI. Slow, but
0274: * should only be called once.
0275: */
0276: public boolean acceptFastInfoset() {
0277: return FastInfosetUtil.isFastInfosetAccepted(getMimeHeaders()
0278: .getHeader("Accept"));
0279: }
0280:
0281: public Source getSource() {
0282: return data.getSource();
0283: }
0284:
0285: public DataSource getDataSource() {
0286: return data.getDataSource();
0287: }
0288:
0289: /**
0290: * Verify a contentType.
0291: *
0292: * @return
0293: * MIME_MULTIPART_FLAG | PLAIN_XML_FLAG
0294: * MIME_MULTIPART_FLAG | FI_ENCODED_FLAG;
0295: * PLAIN_XML_FLAG
0296: * FI_ENCODED_FLAG
0297: *
0298: */
0299: private static int identifyContentType(ContentType contentType) {
0300: String primary = contentType.getPrimaryType();
0301: String sub = contentType.getSubType();
0302:
0303: if (primary.equalsIgnoreCase("multipart")
0304: && sub.equalsIgnoreCase("related")) {
0305: String type = contentType.getParameter("type");
0306: if (type != null) {
0307: if (isXMLType(type)) {
0308: return MIME_MULTIPART_FLAG | PLAIN_XML_FLAG;
0309: } else if (isFastInfosetType(type)) {
0310: return MIME_MULTIPART_FLAG | FI_ENCODED_FLAG;
0311: }
0312: }
0313: return 0;
0314: } else if (isXMLType(primary, sub)) {
0315: return PLAIN_XML_FLAG;
0316: } else if (isFastInfosetType(primary, sub)) {
0317: return FI_ENCODED_FLAG;
0318: }
0319: return 0;
0320: }
0321:
0322: protected static boolean isXMLType(String primary, String sub) {
0323: return (primary.equalsIgnoreCase("text") || primary
0324: .equalsIgnoreCase("application"))
0325: && sub.equalsIgnoreCase("xml");
0326: }
0327:
0328: protected static boolean isXMLType(String type) {
0329: return type.toLowerCase().startsWith("text/xml")
0330: || type.toLowerCase().startsWith("application/xml");
0331: }
0332:
0333: protected static boolean isFastInfosetType(String primary,
0334: String sub) {
0335: return primary.equalsIgnoreCase("application")
0336: && sub.equalsIgnoreCase("fastinfoset");
0337: }
0338:
0339: protected static boolean isFastInfosetType(String type) {
0340: return type.toLowerCase().startsWith("application/fastinfoset");
0341: }
0342:
0343: /**
0344: * Ideally this should be called just before writing the message
0345: */
0346: public MimeHeaders getMimeHeaders() {
0347: return data.getMimeHeaders();
0348: }
0349:
0350: private static String getContentType(MimeHeaders headers) {
0351: String[] values = headers.getHeader("Content-Type");
0352: return (values == null) ? null : values[0];
0353: }
0354:
0355: public int getStatus() {
0356: return data.getStatus();
0357: }
0358:
0359: public void writeTo(OutputStream out) throws IOException {
0360: data.writeTo(out, useFastInfoset);
0361: }
0362:
0363: public Source getPayload() {
0364: return data.getPayload();
0365: }
0366:
0367: public Map<String, DataHandler> getAttachments() {
0368: return data.getAttachments();
0369: }
0370:
0371: public Object getPayload(JAXBContext context) {
0372: // Get a copy of Source using getPayload() and use it to deserialize
0373: // to JAXB object
0374: return JAXBTypeSerializer.deserialize(getPayload(), context);
0375: }
0376:
0377: /**
0378: * Defines operations available regardless of the actual in-memory data representation.
0379: */
0380: private static abstract class DataRepresentation {
0381: /**
0382: * Can be called multiple times. Typically from XMLLogicalMessageImpl
0383: */
0384: abstract Source getPayload();
0385:
0386: /**
0387: * Should be called only once. Once this is called, don't use this object anymore
0388: */
0389: abstract void writeTo(OutputStream out, boolean useFastInfoset)
0390: throws IOException;
0391:
0392: /**
0393: * Returns true whenever the underlying representation of this message
0394: * is a Fast Infoset stream.
0395: */
0396: abstract boolean isFastInfoset();
0397:
0398: /**
0399: * Should be called only once. Once this is called, don't use this object anymore
0400: */
0401: abstract Source getSource();
0402:
0403: /**
0404: * Should be called only once. Once this is called, don't use this object anymore
0405: */
0406: abstract DataSource getDataSource();
0407:
0408: /**
0409: * Should be called only once. Once this is called, don't use this object anymore
0410: */
0411: abstract Map<String, DataHandler> getAttachments();
0412:
0413: /**
0414: * Should contain Content-Type for this message.
0415: */
0416: abstract MimeHeaders getMimeHeaders();
0417:
0418: int getStatus() {
0419: return WSConnection.OK;
0420: }
0421: }
0422:
0423: /**
0424: * Data represented as a multi-part MIME message. It also has XML as
0425: * root part
0426: *
0427: * This class parses {@link MimeMultipart} lazily.
0428: */
0429: private static final class XMLMultiPart extends DataRepresentation {
0430: private DataSource dataSource;
0431: private MimeMultipart multipart;
0432: private XMLSource xmlSource;
0433: private boolean isFastInfoset;
0434: private final MimeHeaders headers = new MimeHeaders();
0435:
0436: public XMLMultiPart(final String contentType,
0437: final InputStream is, boolean isFastInfoset) {
0438: this .isFastInfoset = isFastInfoset;
0439: dataSource = new DataSource() {
0440: public InputStream getInputStream() {
0441: return is;
0442: }
0443:
0444: public OutputStream getOutputStream() {
0445: return null;
0446: }
0447:
0448: public String getContentType() {
0449: return contentType;
0450: }
0451:
0452: public String getName() {
0453: return "";
0454: }
0455: };
0456: }
0457:
0458: public XMLMultiPart(Source source,
0459: final Map<String, DataHandler> atts,
0460: boolean isFastInfoset) {
0461: this .isFastInfoset = isFastInfoset;
0462: multipart = new MimeMultipart("related");
0463: multipart.getContentType().setParameter("type", "text/xml");
0464:
0465: // Creates Primary part
0466: ByteOutputStream bos = new ByteOutputStream();
0467: new XMLSource(source).writeTo(bos, isFastInfoset);
0468: InternetHeaders headers = new InternetHeaders();
0469: headers.addHeader("Content-Type",
0470: isFastInfoset ? "application/fastinfoset"
0471: : "text/xml");
0472: MimeBodyPart rootPart = new MimeBodyPart(headers, bos
0473: .getBytes(), bos.getCount());
0474: multipart.addBodyPart(rootPart, 0);
0475:
0476: for (Map.Entry<String, DataHandler> e : atts.entrySet()) {
0477: MimeBodyPart part = new MimeBodyPart();
0478: part.setDataHandler(e.getValue());
0479: multipart.addBodyPart(part);
0480: }
0481: }
0482:
0483: public XMLMultiPart(DataSource dataSource, boolean isFastInfoset) {
0484: this .dataSource = dataSource;
0485: this .isFastInfoset = isFastInfoset;
0486: }
0487:
0488: public boolean isFastInfoset() {
0489: return isFastInfoset;
0490: }
0491:
0492: public DataSource getDataSource() {
0493: if (dataSource != null) {
0494: return dataSource;
0495: } else if (multipart != null) {
0496: return new DataSource() {
0497: public InputStream getInputStream() {
0498: try {
0499: if (xmlSource != null) {
0500: replaceRootPart(false);
0501: }
0502: ByteOutputStream bos = new ByteOutputStream();
0503: multipart.writeTo(bos);
0504: return bos.newInputStream();
0505: } catch (MessagingException me) {
0506: throw new XMLMessageException(
0507: "xml.get.ds.err", me);
0508: } catch (IOException ioe) {
0509: throw new XMLMessageException(
0510: "xml.get.ds.err", ioe);
0511: }
0512: }
0513:
0514: public OutputStream getOutputStream() {
0515: return null;
0516: }
0517:
0518: public String getContentType() {
0519: return multipart.getContentType().toString();
0520: }
0521:
0522: public String getName() {
0523: return "";
0524: }
0525: };
0526: }
0527: return null;
0528: }
0529:
0530: private MimeBodyPart getRootPart() {
0531: try {
0532: convertToMultipart();
0533: ContentType contentType = multipart.getContentType();
0534: String startParam = contentType.getParameter("start");
0535: MimeBodyPart sourcePart = (startParam == null) ? (MimeBodyPart) multipart
0536: .getBodyPart(0)
0537: : (MimeBodyPart) multipart
0538: .getBodyPart(startParam);
0539: return sourcePart;
0540: } catch (MessagingException ex) {
0541: throw new XMLMessageException("xml.get.source.err", ex);
0542: }
0543: }
0544:
0545: private void replaceRootPart(boolean useFastInfoset) {
0546: if (xmlSource == null) {
0547: return;
0548: }
0549: try {
0550: MimeBodyPart sourcePart = getRootPart();
0551: String ctype = sourcePart.getContentType();
0552: multipart.removeBodyPart(sourcePart);
0553:
0554: ByteOutputStream bos = new ByteOutputStream();
0555: xmlSource.writeTo(bos, useFastInfoset);
0556: InternetHeaders headers = new InternetHeaders();
0557: headers.addHeader("Content-Type",
0558: useFastInfoset ? "application/fastinfoset"
0559: : ctype);
0560:
0561: sourcePart = new MimeBodyPart(headers, bos.getBytes(),
0562: bos.getCount());
0563: multipart.addBodyPart(sourcePart, 0);
0564: } catch (MessagingException ex) {
0565: throw new XMLMessageException("xml.get.source.err", ex);
0566: }
0567: }
0568:
0569: private void convertToMultipart() {
0570: if (dataSource != null) {
0571: try {
0572: multipart = new MimeMultipart(dataSource, null);
0573: dataSource = null;
0574: } catch (MessagingException ex) {
0575: throw new XMLMessageException("xml.get.source.err",
0576: ex);
0577: }
0578: }
0579: }
0580:
0581: /**
0582: * Returns root part of the MIME message
0583: */
0584: public Source getSource() {
0585: try {
0586: // If there is an XMLSource, return that
0587: if (xmlSource != null) {
0588: return xmlSource.getPayload();
0589: }
0590:
0591: // Otherwise, parse MIME package and find root part
0592: convertToMultipart();
0593: MimeBodyPart sourcePart = getRootPart();
0594: ContentType ctype = new ContentType(sourcePart
0595: .getContentType());
0596: String baseType = ctype.getBaseType();
0597:
0598: // Return a StreamSource or FastInfosetSource depending on type
0599: if (isXMLType(baseType)) {
0600: return new StreamSource(sourcePart.getInputStream());
0601: } else if (isFastInfosetType(baseType)) {
0602: return FastInfosetReflection
0603: .FastInfosetSource_new(sourcePart
0604: .getInputStream());
0605: } else {
0606: throw new XMLMessageException(
0607: "xml.root.part.invalid.Content-Type",
0608: new Object[] { baseType });
0609: }
0610: } catch (MessagingException ex) {
0611: throw new XMLMessageException("xml.get.source.err", ex);
0612: } catch (Exception ioe) {
0613: throw new XMLMessageException("xml.get.source.err", ioe);
0614: }
0615: }
0616:
0617: public Source getPayload() {
0618: return getSource();
0619: }
0620:
0621: public void writeTo(OutputStream out, boolean useFastInfoset) {
0622: try {
0623: // If a source has been set, ensure MIME parsing
0624: if (xmlSource != null) {
0625: convertToMultipart();
0626: }
0627:
0628: // Try to use dataSource whenever possible
0629: if (dataSource != null) {
0630: // If already encoded correctly, just copy the bytes
0631: if (isFastInfoset == useFastInfoset) {
0632: InputStream is = dataSource.getInputStream();
0633: byte[] buf = new byte[1024];
0634: int len;
0635: while ((len = is.read(buf)) != -1) {
0636: out.write(buf, 0, len);
0637: }
0638: return; // we're done
0639: } else {
0640: // Parse MIME and create source for root part
0641: xmlSource = new XMLSource(getSource());
0642: }
0643: }
0644:
0645: // Finally, possibly re-encode root part and write it out
0646: replaceRootPart(useFastInfoset);
0647: multipart.writeTo(out);
0648: } catch (Exception e) {
0649: throw new WebServiceException(e);
0650: }
0651: }
0652:
0653: public Map<String, DataHandler> getAttachments() {
0654: // If a source has been set, ensure MIME parsing
0655: if (xmlSource != null) {
0656: convertToMultipart();
0657: }
0658: try {
0659: MimeBodyPart rootPart = getRootPart();
0660: Map<String, DataHandler> map = new HashMap<String, DataHandler>();
0661: int count = multipart.getCount();
0662: for (int i = 0; i < count; i++) {
0663: MimeBodyPart part = multipart.getBodyPart(i);
0664: if (part != rootPart) {
0665: map.put(part.getContentID(), part
0666: .getDataHandler());
0667: }
0668: }
0669: return map;
0670: } catch (MessagingException ex) {
0671: throw new XMLMessageException("xml.get.source.err", ex);
0672: }
0673: }
0674:
0675: MimeHeaders getMimeHeaders() {
0676: headers.removeHeader("Content-Type");
0677: if (dataSource != null) {
0678: headers.addHeader("Content-Type", dataSource
0679: .getContentType());
0680: } else {
0681: if (multipart != null) {
0682: headers.addHeader("Content-Type", multipart
0683: .getContentType().toString());
0684: }
0685: }
0686: return headers;
0687: }
0688:
0689: }
0690:
0691: /**
0692: * Data represented as {@link Source}.
0693: */
0694: public static class XMLSource extends DataRepresentation {
0695:
0696: private Source source;
0697: private boolean isFastInfoset;
0698: private final MimeHeaders headers = new MimeHeaders();
0699:
0700: public XMLSource(InputStream in, boolean isFastInfoset)
0701: throws Exception {
0702: this .source = isFastInfoset ? FastInfosetReflection
0703: .FastInfosetSource_new(in) : new StreamSource(in);
0704: this .isFastInfoset = isFastInfoset;
0705: }
0706:
0707: public XMLSource(Source source) {
0708: this .source = source;
0709: this .isFastInfoset = ((source != null) ? (source.getClass() == FastInfosetReflection.fiFastInfosetSource)
0710: : false);
0711: }
0712:
0713: public boolean isFastInfoset() {
0714: return isFastInfoset;
0715: }
0716:
0717: /*
0718: * If there is a ByteInputStream available, then write it to the output
0719: * stream. Otherwise, use Transformer to write Source to output stream.
0720: */
0721: public void writeTo(OutputStream out, boolean useFastInfoset) {
0722: try {
0723: InputStream is = null;
0724: boolean canAvoidTransform = false;
0725: if (source instanceof StreamSource) {
0726: is = ((StreamSource) source).getInputStream();
0727: // If use of FI is requested, need to transcode
0728: canAvoidTransform = !useFastInfoset;
0729: } else if (source.getClass() == FastInfosetReflection.fiFastInfosetSource) {
0730: is = FastInfosetReflection
0731: .FastInfosetSource_getInputStream(source);
0732: // If use of FI is not requested, need to transcode
0733: canAvoidTransform = useFastInfoset;
0734: }
0735:
0736: if (canAvoidTransform && is != null
0737: && is instanceof ByteInputStream) {
0738: ByteInputStream bis = (ByteInputStream) is;
0739: // Reset the stream
0740: byte[] buf = bis.getBytes();
0741: out.write(buf);
0742: bis.close();
0743: return;
0744: }
0745:
0746: // TODO: Use an efficient transformer from SAAJ that knows how to optimally
0747: // write to FI results
0748: Transformer transformer = XmlUtil.newTransformer();
0749: transformer.transform(source,
0750: useFastInfoset ? FastInfosetReflection
0751: .FastInfosetResult_new(out)
0752: : new StreamResult(out));
0753: } catch (Exception e) {
0754: throw new WebServiceException(e);
0755: }
0756: }
0757:
0758: public Source getSource() {
0759: return source;
0760: }
0761:
0762: DataSource getDataSource() {
0763: return new DataSource() {
0764: public InputStream getInputStream() {
0765: try {
0766: InputStream is = null;
0767: if (source instanceof StreamSource) {
0768: is = ((StreamSource) source)
0769: .getInputStream();
0770: } else if (source.getClass() == FastInfosetReflection.fiFastInfosetSource) {
0771: is = FastInfosetReflection
0772: .FastInfosetSource_getInputStream(source);
0773: }
0774: if (is != null) {
0775: return is;
0776: }
0777: // Copy source to result respecting desired encoding
0778: ByteArrayBuffer bab = new ByteArrayBuffer();
0779: Transformer transformer = XmlUtil
0780: .newTransformer();
0781: transformer.transform(source,
0782: isFastInfoset() ? FastInfosetReflection
0783: .FastInfosetResult_new(bab)
0784: : new StreamResult(bab));
0785: bab.close();
0786: return bab.newInputStream();
0787: } catch (Exception e) {
0788: throw new WebServiceException(e);
0789: }
0790: }
0791:
0792: public OutputStream getOutputStream() {
0793: return null;
0794: }
0795:
0796: public String getContentType() {
0797: return isFastInfoset() ? "application/fastinfoset"
0798: : "text/xml";
0799: }
0800:
0801: public String getName() {
0802: return "";
0803: }
0804: };
0805:
0806: }
0807:
0808: /*
0809: * Usually called from logical handler
0810: * If there is a DOMSource, return that. Otherwise, return a copy of
0811: * the existing source.
0812: */
0813: public Source getPayload() {
0814: try {
0815:
0816: if (source instanceof DOMSource) {
0817: return source;
0818: }
0819:
0820: InputStream is = null;
0821:
0822: if (source instanceof StreamSource) {
0823: is = ((StreamSource) source).getInputStream();
0824: } else if (source.getClass() == FastInfosetReflection.fiFastInfosetSource) {
0825: is = FastInfosetReflection
0826: .FastInfosetSource_getInputStream(source);
0827: }
0828:
0829: if (is != null && is instanceof ByteInputStream) {
0830: ByteInputStream bis = (ByteInputStream) is;
0831: // Reset the stream
0832: byte[] buf = bis.getBytes();
0833:
0834: ByteArrayInputStream bais = new ByteArrayInputStream(
0835: buf);
0836: bis.close();
0837: return isFastInfoset ? FastInfosetReflection
0838: .FastInfosetSource_new(is)
0839: : new StreamSource(bais);
0840:
0841: }
0842:
0843: // Copy source to result respecting desired encoding
0844: ByteArrayBuffer bab = new ByteArrayBuffer();
0845: Transformer transformer = XmlUtil.newTransformer();
0846: // Adding this to work with empty source. Is it JAXP bug ?
0847: Properties oprops = new Properties();
0848: oprops.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
0849: transformer.setOutputProperties(oprops);
0850: transformer.transform(source,
0851: isFastInfoset ? FastInfosetReflection
0852: .FastInfosetResult_new(bab)
0853: : new StreamResult(bab));
0854: bab.close();
0855:
0856: // Set internal source
0857: InputStream bis = (bab.size() == 0) ? null : bab
0858: .newInputStream();
0859: source = isFastInfoset ? FastInfosetReflection
0860: .FastInfosetSource_new(bis) : new StreamSource(
0861: bis);
0862:
0863: // Return fresh source back to handler
0864: bis = bab.newInputStream();
0865: return isFastInfoset ? FastInfosetReflection
0866: .FastInfosetSource_new(bis) : new StreamSource(
0867: bis);
0868: } catch (Exception e) {
0869: throw new WebServiceException(e);
0870: }
0871: }
0872:
0873: public Map<String, DataHandler> getAttachments() {
0874: return null;
0875: }
0876:
0877: MimeHeaders getMimeHeaders() {
0878: return headers;
0879: }
0880:
0881: }
0882:
0883: /**
0884: * Data represented as a JAXB object.
0885: */
0886: public static class XMLJaxb extends DataRepresentation {
0887: private final Object object;
0888: private final JAXBContext jaxbContext;
0889: private final MimeHeaders headers = new MimeHeaders();
0890:
0891: public XMLJaxb(Object object, JAXBContext jaxbContext) {
0892: this .object = object;
0893: this .jaxbContext = jaxbContext;
0894: }
0895:
0896: public void writeTo(OutputStream out, boolean useFastInfoset) {
0897: if (useFastInfoset) {
0898: JAXBTypeSerializer
0899: .serializeDocument(object,
0900: XMLStreamWriterFactory
0901: .createFIStreamWriter(out),
0902: jaxbContext);
0903: } else {
0904: JAXBTypeSerializer.serialize(object, out, jaxbContext);
0905: }
0906: }
0907:
0908: boolean isFastInfoset() {
0909: return false;
0910: }
0911:
0912: public Source getSource() {
0913: return JAXBTypeSerializer.serialize(object, jaxbContext);
0914: }
0915:
0916: DataSource getDataSource() {
0917: return new DataSource() {
0918: public InputStream getInputStream() {
0919: ByteOutputStream bos = new ByteOutputStream();
0920: JAXBTypeSerializer.serialize(object, bos,
0921: jaxbContext);
0922: return bos.newInputStream();
0923: }
0924:
0925: public OutputStream getOutputStream() {
0926: return null;
0927: }
0928:
0929: public String getContentType() {
0930: return isFastInfoset() ? "application/fastinfoset"
0931: : "text/xml";
0932: }
0933:
0934: public String getName() {
0935: return "";
0936: }
0937: };
0938: }
0939:
0940: /*
0941: * Usually called from logical handler
0942: * If there is a DOMSource, return that. Otherwise, return a copy of
0943: * the existing source.
0944: */
0945: public Source getPayload() {
0946: return getSource();
0947: }
0948:
0949: public Map<String, DataHandler> getAttachments() {
0950: return null;
0951: }
0952:
0953: MimeHeaders getMimeHeaders() {
0954: return headers;
0955: }
0956:
0957: }
0958:
0959: /**
0960: * Don't know about this content. It's conent-type is NOT the XML types
0961: * we recognize(text/xml, application/xml, multipart/related;text/xml etc).
0962: *
0963: * This could be used to represent image/jpeg etc
0964: */
0965: public static class UnknownContent extends DataRepresentation {
0966: private final String ct;
0967: private final InputStream in;
0968: private final MimeMultipart multipart;
0969: private final MimeHeaders headers = new MimeHeaders();
0970:
0971: public UnknownContent(String ct, InputStream in) {
0972: this .ct = ct;
0973: this .in = in;
0974: this .multipart = null;
0975: }
0976:
0977: public UnknownContent(Map<String, DataHandler> atts) {
0978: this .in = null;
0979: multipart = new MimeMultipart("mixed");
0980: for (Map.Entry<String, DataHandler> e : atts.entrySet()) {
0981: MimeBodyPart part = new MimeBodyPart();
0982: part.setDataHandler(e.getValue());
0983: multipart.addBodyPart(part);
0984: }
0985: this .ct = multipart.getContentType().toString();
0986: }
0987:
0988: public void writeTo(OutputStream out, boolean useFastInfoset) {
0989: try {
0990: if (multipart != null) {
0991: multipart.writeTo(out);
0992: }
0993: byte[] buf = new byte[1024];
0994: int len;
0995: while ((len = in.read(buf)) != -1) {
0996: out.write(buf, 0, len);
0997: }
0998: } catch (Exception ex) {
0999: throw new WebServiceException(ex);
1000: }
1001: }
1002:
1003: boolean isFastInfoset() {
1004: return false;
1005: }
1006:
1007: /**
1008: * NO XML content so return null
1009: */
1010: public Source getSource() {
1011: return null;
1012: }
1013:
1014: DataSource getDataSource() {
1015: return new DataSource() {
1016: public InputStream getInputStream() {
1017: if (multipart != null) {
1018: try {
1019: ByteOutputStream bos = new ByteOutputStream();
1020: multipart.writeTo(bos);
1021: return bos.newInputStream();
1022: } catch (Exception ioe) {
1023: throw new WebServiceException(ioe);
1024: }
1025: }
1026: return in;
1027: }
1028:
1029: public OutputStream getOutputStream() {
1030: return null;
1031: }
1032:
1033: public String getContentType() {
1034: assert ct != null;
1035: return ct;
1036: }
1037:
1038: public String getName() {
1039: return "";
1040: }
1041: };
1042: }
1043:
1044: /**
1045: * NO XML content so return null
1046: */
1047: public Source getPayload() {
1048: return null;
1049: }
1050:
1051: /**
1052: * JAXWS doesn't know about this conent. So we treate the whole content
1053: * as one payload.
1054: */
1055: public Map<String, DataHandler> getAttachments() {
1056: return null;
1057: }
1058:
1059: MimeHeaders getMimeHeaders() {
1060: headers.removeHeader("Content-Type");
1061: headers.addHeader("Content-Type", ct);
1062: return headers;
1063: }
1064:
1065: }
1066:
1067: /**
1068: * Represents HTTPException or anyother exception
1069: */
1070: private static final class XMLErr extends DataRepresentation {
1071: private final Exception err;
1072: private final MimeHeaders headers = new MimeHeaders();
1073:
1074: XMLErr(Exception err) {
1075: this .err = err;
1076: }
1077:
1078: public Source getPayload() {
1079: return null;
1080: }
1081:
1082: public Map<String, DataHandler> getAttachments() {
1083: return null;
1084: }
1085:
1086: public void writeTo(OutputStream out, boolean useFastInfoset)
1087: throws IOException {
1088: String msg = err.getMessage();
1089: if (msg == null) {
1090: msg = err.toString();
1091: }
1092: msg = "<err>" + msg + "</err>";
1093:
1094: if (useFastInfoset) {
1095: FastInfosetUtil.transcodeXMLStringToFI(msg, out);
1096: } else {
1097: out.write(msg.getBytes());
1098: }
1099: }
1100:
1101: boolean isFastInfoset() {
1102: return false;
1103: }
1104:
1105: Source getSource() {
1106: return null;
1107: }
1108:
1109: DataSource getDataSource() {
1110: return null;
1111: }
1112:
1113: @Override
1114: int getStatus() {
1115: if (err instanceof HTTPException) {
1116: return ((HTTPException) err).getStatusCode();
1117: }
1118: return WSConnection.INTERNAL_ERR;
1119: }
1120:
1121: MimeHeaders getMimeHeaders() {
1122: return headers;
1123: }
1124: }
1125:
1126: /**
1127: * There is no content to write.
1128: */
1129: private static final class NullContent extends DataRepresentation {
1130: private final MimeHeaders headers = new MimeHeaders();
1131:
1132: public Source getPayload() {
1133: return null;
1134: }
1135:
1136: public Map<String, DataHandler> getAttachments() {
1137: return null;
1138: }
1139:
1140: public void writeTo(OutputStream out, boolean useFastInfoset)
1141: throws IOException {
1142: // Nothing to do
1143: }
1144:
1145: boolean isFastInfoset() {
1146: return false;
1147: }
1148:
1149: Source getSource() {
1150: return null;
1151: }
1152:
1153: DataSource getDataSource() {
1154: return null;
1155: }
1156:
1157: MimeHeaders getMimeHeaders() {
1158: return headers;
1159: }
1160: }
1161: }
|