0001: /*
0002: * The contents of this file are subject to the terms
0003: * of the Common Development and Distribution License
0004: * (the "License"). You may not use this file except
0005: * in compliance with the License.
0006: *
0007: * You can obtain a copy of the license at
0008: * https://jwsdp.dev.java.net/CDDLv1.0.html
0009: * See the License for the specific language governing
0010: * permissions and limitations under the License.
0011: *
0012: * When distributing Covered Code, include this CDDL
0013: * HEADER in each file and include the License file at
0014: * https://jwsdp.dev.java.net/CDDLv1.0.html If applicable,
0015: * add the following below this CDDL HEADER, with the
0016: * fields enclosed by brackets "[]" replaced with your
0017: * own identifying information: Portions Copyright [yyyy]
0018: * [name of copyright owner]
0019: */
0020: /*
0021: * $Id: MessageImpl.java,v 1.8 2007/09/11 07:43:48 kumarjayanti Exp $
0022: * $Revision: 1.8 $
0023: * $Date: 2007/09/11 07:43:48 $
0024: */
0025:
0026: /*
0027: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0028: *
0029: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0030: *
0031: * The contents of this file are subject to the terms of either the GNU
0032: * General Public License Version 2 only ("GPL") or the Common Development
0033: * and Distribution License("CDDL") (collectively, the "License"). You
0034: * may not use this file except in compliance with the License. You can obtain
0035: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
0036: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
0037: * language governing permissions and limitations under the License.
0038: *
0039: * When distributing the software, include this License Header Notice in each
0040: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
0041: * Sun designates this particular file as subject to the "Classpath" exception
0042: * as provided by Sun in the GPL Version 2 section of the License file that
0043: * accompanied this code. If applicable, add the following below the License
0044: * Header, with the fields enclosed by brackets [] replaced by your own
0045: * identifying information: "Portions Copyrighted [year]
0046: * [name of copyright owner]"
0047: *
0048: * Contributor(s):
0049: *
0050: * If you wish your version of this file to be governed by only the CDDL or
0051: * only the GPL Version 2, indicate your decision by adding "[Contributor]
0052: * elects to include this software in this distribution under the [CDDL or GPL
0053: * Version 2] license." If you don't indicate a single choice of license, a
0054: * recipient has the option to distribute your version of this file under
0055: * either the CDDL, the GPL Version 2 or to extend the choice of license to
0056: * its licensees as provided above. However, if you add GPL Version 2 code
0057: * and therefore, elected the GPL Version 2 license, then the option applies
0058: * only if the new code is made subject to such option by the copyright
0059: * holder.
0060: */
0061: package com.sun.xml.messaging.saaj.soap;
0062:
0063: import java.io.*;
0064: import java.util.*;
0065: import java.util.logging.Level;
0066: import java.util.logging.Logger;
0067:
0068: import javax.activation.DataHandler;
0069: import javax.activation.DataSource;
0070: import javax.xml.soap.*;
0071: import javax.xml.transform.Source;
0072: import javax.xml.transform.stream.StreamSource;
0073:
0074: import com.sun.xml.messaging.saaj.packaging.mime.Header;
0075: import com.sun.xml.messaging.saaj.packaging.mime.internet.*;
0076: import com.sun.xml.messaging.saaj.packaging.mime.util.*;
0077: import com.sun.xml.messaging.saaj.packaging.mime.MessagingException;
0078:
0079: import com.sun.xml.messaging.saaj.SOAPExceptionImpl;
0080: import com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl;
0081: import com.sun.xml.messaging.saaj.util.*;
0082:
0083: /**
0084: * The message implementation for SOAP messages with
0085: * attachments. Messages for specific profiles will likely extend this
0086: * MessageImpl class and add more value for that particular profile.
0087: *
0088: * @author Anil Vijendran (akv@eng.sun.com)
0089: * @author Rajiv Mordani (rajiv.mordani@sun.com)
0090: * @author Manveen Kaur (manveen.kaur@sun.com)
0091: */
0092:
0093: public abstract class MessageImpl extends SOAPMessage implements
0094: SOAPConstants {
0095:
0096: public static final String CONTENT_ID = "Content-ID";
0097: public static final String CONTENT_LOCATION = "Content-Location";
0098:
0099: protected static Logger log = Logger.getLogger(
0100: LogDomainConstants.SOAP_DOMAIN,
0101: "com.sun.xml.messaging.saaj.soap.LocalStrings");
0102:
0103: protected static final int PLAIN_XML_FLAG = 1; // 00001
0104: protected static final int MIME_MULTIPART_FLAG = 2; // 00010
0105: protected static final int SOAP1_1_FLAG = 4; // 00100
0106: protected static final int SOAP1_2_FLAG = 8; // 01000
0107: protected static final int MIME_MULTIPART_XOP_FLAG = 14; // 01110
0108: protected static final int XOP_FLAG = 13; // 01101
0109: protected static final int FI_ENCODED_FLAG = 16; // 10000
0110:
0111: protected MimeHeaders headers;
0112: protected SOAPPartImpl soapPart;
0113: protected FinalArrayList attachments;
0114: protected boolean saved = false;
0115: protected byte[] messageBytes;
0116: protected int messageByteCount;
0117: protected HashMap properties = new HashMap();
0118:
0119: // used for lazy attachment initialization
0120: protected MimeMultipart multiPart = null;
0121: protected boolean attachmentsInitialized = false;
0122:
0123: /**
0124: * True if this part is encoded using Fast Infoset.
0125: * MIME -> application/fastinfoset
0126: */
0127: protected boolean isFastInfoset = false;
0128:
0129: /**
0130: * True if the Accept header of this message includes
0131: * application/fastinfoset
0132: */
0133: protected boolean acceptFastInfoset = false;
0134:
0135: protected MimeMultipart mmp = null;
0136:
0137: // if attachments are present, don't read the entire message in byte stream in saveTo()
0138: private boolean optimizeAttachmentProcessing = true;
0139:
0140: // switch back to old MimeMultipart incase of problem
0141: private static boolean switchOffBM = false;
0142: private static boolean switchOffLazyAttachment = false;
0143:
0144: static {
0145: try {
0146: String s = System.getProperty("saaj.mime.optimization");
0147: if ((s != null) && s.equals("false")) {
0148: switchOffBM = true;
0149: }
0150: s = System.getProperty("saaj.lazy.mime.optimization");
0151: if ((s != null) && s.equals("false")) {
0152: switchOffLazyAttachment = true;
0153: }
0154: } catch (SecurityException ex) {
0155: // ignore it
0156: }
0157: }
0158:
0159: //property to indicate optimized serialization for lazy attachments
0160: private boolean lazyAttachments = false;
0161:
0162: // most of the times, Content-Types are already all lower cased.
0163: // String.toLowerCase() works faster in this case, so even if you
0164: // are only doing one comparison, it pays off to use String.toLowerCase()
0165: // than String.equalsIgnoreCase(). When you do more than one comparison,
0166: // the benefits of String.toLowerCase() dominates.
0167: //
0168: //
0169: // for FI,
0170: // use application/fastinfoset for SOAP 1.1
0171: // use application/soap+fastinfoset for SOAP 1.2
0172: // to speed up comparisons, test methods always use lower cases.
0173:
0174: /**
0175: * @param primary
0176: * must be all lower case
0177: * @param sub
0178: * must be all lower case
0179: */
0180: private static boolean isSoap1_1Type(String primary, String sub) {
0181: return primary.equals("text") && sub.equals("xml")
0182: || primary.equals("application")
0183: && sub.equals("fastinfoset");
0184: }
0185:
0186: /**
0187: * @param type
0188: * must be all lower case
0189: */
0190: private static boolean isEqualToSoap1_1Type(String type) {
0191: return type.startsWith("text/xml")
0192: || type.startsWith("application/fastinfoset");
0193: }
0194:
0195: /**
0196: * @param primary
0197: * must be all lower case
0198: * @param sub
0199: * must be all lower case
0200: */
0201: private static boolean isSoap1_2Type(String primary, String sub) {
0202: return primary.equals("application")
0203: && (sub.equals("soap+xml") || sub
0204: .equals("soap+fastinfoset"));
0205: }
0206:
0207: /**
0208: * @param type
0209: * must be all lower case
0210: */
0211: private static boolean isEqualToSoap1_2Type(String type) {
0212: return type.startsWith("application/soap+xml")
0213: || type.startsWith("application/soap+fastinfoset");
0214: }
0215:
0216: /**
0217: * Construct a new message. This will be invoked before message
0218: * sends.
0219: */
0220: protected MessageImpl() {
0221: this (false, false);
0222: attachmentsInitialized = true;
0223: }
0224:
0225: /**
0226: * Construct a new message. This will be invoked before message
0227: * sends.
0228: */
0229: protected MessageImpl(boolean isFastInfoset,
0230: boolean acceptFastInfoset) {
0231: this .isFastInfoset = isFastInfoset;
0232: this .acceptFastInfoset = acceptFastInfoset;
0233:
0234: headers = new MimeHeaders();
0235: headers.setHeader("Accept", getExpectedAcceptHeader());
0236: }
0237:
0238: /**
0239: * Shallow copy.
0240: */
0241: protected MessageImpl(SOAPMessage msg) {
0242: if (!(msg instanceof MessageImpl)) {
0243: // don't know how to handle this.
0244: }
0245: MessageImpl src = (MessageImpl) msg;
0246: this .headers = src.headers;
0247: this .soapPart = src.soapPart;
0248: this .attachments = src.attachments;
0249: this .saved = src.saved;
0250: this .messageBytes = src.messageBytes;
0251: this .messageByteCount = src.messageByteCount;
0252: this .properties = src.properties;
0253: }
0254:
0255: /**
0256: * @param stat
0257: * the mask value obtained from {@link #identifyContentType(ContentType)}
0258: */
0259: protected static boolean isSoap1_1Content(int stat) {
0260: return (stat & SOAP1_1_FLAG) != 0;
0261: }
0262:
0263: /**
0264: * @param stat
0265: * the mask value obtained from {@link #identifyContentType(ContentType)}
0266: */
0267: protected static boolean isSoap1_2Content(int stat) {
0268: return (stat & SOAP1_2_FLAG) != 0;
0269: }
0270:
0271: private static boolean isMimeMultipartXOPPackage(
0272: ContentType contentType) {
0273: String type = contentType.getParameter("type");
0274: if (type == null)
0275: return false;
0276:
0277: type = type.toLowerCase();
0278: if (!type.startsWith("application/xop+xml"))
0279: return false;
0280:
0281: String startinfo = contentType.getParameter("start-info");
0282: if (startinfo == null)
0283: return false;
0284: startinfo = startinfo.toLowerCase();
0285: return isEqualToSoap1_2Type(startinfo)
0286: || isEqualToSoap1_1Type(startinfo);
0287: }
0288:
0289: private static boolean isSOAPBodyXOPPackage(ContentType contentType) {
0290: String primary = contentType.getPrimaryType();
0291: String sub = contentType.getSubType();
0292:
0293: if (primary.equalsIgnoreCase("application")) {
0294: if (sub.equalsIgnoreCase("xop+xml")) {
0295: String type = getTypeParameter(contentType);
0296: return isEqualToSoap1_2Type(type)
0297: || isEqualToSoap1_1Type(type);
0298: }
0299: }
0300: return false;
0301: }
0302:
0303: /**
0304: * Construct a message from an input stream. When messages are
0305: * received, there's two parts -- the transport headers and the
0306: * message content in a transport specific stream.
0307: */
0308: protected MessageImpl(MimeHeaders headers, final InputStream in)
0309: throws SOAPExceptionImpl {
0310: ContentType ct = parseContentType(headers);
0311: init(headers, identifyContentType(ct), ct, in);
0312: }
0313:
0314: private static ContentType parseContentType(MimeHeaders headers)
0315: throws SOAPExceptionImpl {
0316: final String ct;
0317: if (headers != null)
0318: ct = getContentType(headers);
0319: else {
0320: log.severe("SAAJ0550.soap.null.headers");
0321: throw new SOAPExceptionImpl("Cannot create message: "
0322: + "Headers can't be null");
0323: }
0324:
0325: if (ct == null) {
0326: log.severe("SAAJ0532.soap.no.Content-Type");
0327: throw new SOAPExceptionImpl("Absent Content-Type");
0328: }
0329: try {
0330: return new ContentType(ct);
0331: } catch (Throwable ex) {
0332: log.severe("SAAJ0535.soap.cannot.internalize.message");
0333: throw new SOAPExceptionImpl(
0334: "Unable to internalize message", ex);
0335: }
0336: }
0337:
0338: /**
0339: * Construct a message from an input stream. When messages are
0340: * received, there's two parts -- the transport headers and the
0341: * message content in a transport specific stream.
0342: *
0343: * @param contentType
0344: * The parsed content type header from the headers variable.
0345: * This is redundant parameter, but it avoids reparsing this header again.
0346: * @param stat
0347: * The result of {@link #identifyContentType(ContentType)} over
0348: * the contentType parameter. This redundant parameter, but it avoids
0349: * recomputing this information again.
0350: */
0351: protected MessageImpl(MimeHeaders headers,
0352: final ContentType contentType, int stat,
0353: final InputStream in) throws SOAPExceptionImpl {
0354: init(headers, stat, contentType, in);
0355:
0356: }
0357:
0358: private void init(MimeHeaders headers, int stat,
0359: final ContentType contentType, final InputStream in)
0360: throws SOAPExceptionImpl {
0361: this .headers = headers;
0362:
0363: try {
0364:
0365: // Set isFastInfoset/acceptFastInfoset flag based on MIME type
0366: if ((stat & FI_ENCODED_FLAG) > 0) {
0367: isFastInfoset = acceptFastInfoset = true;
0368: }
0369:
0370: // If necessary, inspect Accept header to set acceptFastInfoset
0371: if (!isFastInfoset) {
0372: String[] values = headers.getHeader("Accept");
0373: if (values != null) {
0374: for (int i = 0; i < values.length; i++) {
0375: StringTokenizer st = new StringTokenizer(
0376: values[i], ",");
0377: while (st.hasMoreTokens()) {
0378: final String token = st.nextToken().trim();
0379: if (token
0380: .equalsIgnoreCase("application/fastinfoset")
0381: || token
0382: .equalsIgnoreCase("application/soap+fastinfoset")) {
0383: acceptFastInfoset = true;
0384: break;
0385: }
0386: }
0387: }
0388: }
0389: }
0390:
0391: if (!isCorrectSoapVersion(stat)) {
0392: log.log(Level.SEVERE,
0393: "SAAJ0533.soap.incorrect.Content-Type",
0394: new String[] { contentType.toString(),
0395: getExpectedContentType() });
0396: throw new SOAPVersionMismatchException(
0397: "Cannot create message: incorrect content-type for SOAP version. Got: "
0398: + contentType + " Expected: "
0399: + getExpectedContentType());
0400: }
0401:
0402: if ((stat & PLAIN_XML_FLAG) != 0) {
0403: if (isFastInfoset) {
0404: getSOAPPart().setContent(
0405: FastInfosetReflection
0406: .FastInfosetSource_new(in));
0407: } else {
0408: initCharsetProperty(contentType);
0409: getSOAPPart().setContent(new StreamSource(in));
0410: }
0411: } else if ((stat & MIME_MULTIPART_FLAG) != 0) {
0412: DataSource ds = new DataSource() {
0413: public InputStream getInputStream() {
0414: return in;
0415: }
0416:
0417: public OutputStream getOutputStream() {
0418: return null;
0419: }
0420:
0421: public String getContentType() {
0422: return contentType.toString();
0423: }
0424:
0425: public String getName() {
0426: return "";
0427: }
0428: };
0429:
0430: multiPart = null;
0431: if (switchOffBM) {
0432: multiPart = new MimeMultipart(ds, contentType);
0433: } else {
0434: multiPart = new BMMimeMultipart(ds, contentType);
0435: }
0436:
0437: String startParam = contentType.getParameter("start");
0438: MimeBodyPart soapMessagePart = null;
0439: String contentID = null;
0440: if (switchOffBM || switchOffLazyAttachment) {
0441: if (startParam == null) {
0442: soapMessagePart = multiPart.getBodyPart(0);
0443: for (int i = 1; i < multiPart.getCount(); i++) {
0444: initializeAttachment(multiPart, i);
0445: }
0446: } else {
0447: soapMessagePart = multiPart
0448: .getBodyPart(startParam);
0449: for (int i = 0; i < multiPart.getCount(); i++) {
0450: contentID = multiPart.getBodyPart(i)
0451: .getContentID();
0452: if (!contentID.equals(startParam))
0453: initializeAttachment(multiPart, i);
0454: }
0455: }
0456: } else {
0457: BMMimeMultipart bmMultipart = (BMMimeMultipart) multiPart;
0458: InputStream stream = bmMultipart.initStream();
0459:
0460: SharedInputStream sin = null;
0461: if (stream instanceof SharedInputStream) {
0462: sin = (SharedInputStream) stream;
0463: }
0464:
0465: String boundary = "--"
0466: + contentType.getParameter("boundary");
0467: byte[] bndbytes = ASCIIUtility.getBytes(boundary);
0468: if (startParam == null) {
0469: soapMessagePart = bmMultipart.getNextPart(
0470: stream, bndbytes, sin);
0471: bmMultipart.removeBodyPart(soapMessagePart);
0472: } else {
0473: MimeBodyPart bp = null;
0474: try {
0475: while (!startParam.equals(contentID)) {
0476: bp = bmMultipart.getNextPart(stream,
0477: bndbytes, sin);
0478: contentID = bp.getContentID();
0479: }
0480: soapMessagePart = bp;
0481: bmMultipart.removeBodyPart(bp);
0482: } catch (Exception e) {
0483: throw new SOAPExceptionImpl(e);
0484: }
0485: }
0486: }
0487:
0488: ContentType soapPartCType = new ContentType(
0489: soapMessagePart.getContentType());
0490: initCharsetProperty(soapPartCType);
0491: String baseType = soapPartCType.getBaseType()
0492: .toLowerCase();
0493: if (!(isEqualToSoap1_1Type(baseType)
0494: || isEqualToSoap1_2Type(baseType) || isSOAPBodyXOPPackage(soapPartCType))) {
0495: log.log(Level.SEVERE,
0496: "SAAJ0549.soap.part.invalid.Content-Type",
0497: new Object[] { baseType });
0498: throw new SOAPExceptionImpl(
0499: "Bad Content-Type for SOAP Part : "
0500: + baseType);
0501: }
0502:
0503: SOAPPart soapPart = getSOAPPart();
0504: setMimeHeaders(soapPart, soapMessagePart);
0505: soapPart
0506: .setContent(isFastInfoset ? (Source) FastInfosetReflection
0507: .FastInfosetSource_new(soapMessagePart
0508: .getInputStream())
0509: : (Source) new StreamSource(
0510: soapMessagePart
0511: .getInputStream()));
0512: } else {
0513: log.severe("SAAJ0534.soap.unknown.Content-Type");
0514: throw new SOAPExceptionImpl("Unrecognized Content-Type");
0515: }
0516: } catch (Throwable ex) {
0517: log.severe("SAAJ0535.soap.cannot.internalize.message");
0518: throw new SOAPExceptionImpl(
0519: "Unable to internalize message", ex);
0520: }
0521: needsSave();
0522: }
0523:
0524: public boolean isFastInfoset() {
0525: return isFastInfoset;
0526: }
0527:
0528: public boolean acceptFastInfoset() {
0529: return acceptFastInfoset;
0530: }
0531:
0532: public void setIsFastInfoset(boolean value) {
0533: if (value != isFastInfoset) {
0534: isFastInfoset = value;
0535: if (isFastInfoset) {
0536: acceptFastInfoset = true;
0537: }
0538: saved = false; // ensure transcoding if necessary
0539: }
0540: }
0541:
0542: public Object getProperty(String property) {
0543: return (String) properties.get(property);
0544: }
0545:
0546: public void setProperty(String property, Object value) {
0547: verify(property, value);
0548: properties.put(property, value);
0549: }
0550:
0551: private void verify(String property, Object value) {
0552: if (property
0553: .equalsIgnoreCase(SOAPMessage.WRITE_XML_DECLARATION)) {
0554: if (!("true".equals(value) || "false".equals(value)))
0555: throw new RuntimeException(property
0556: + " must have value false or true");
0557:
0558: try {
0559: EnvelopeImpl env = (EnvelopeImpl) getSOAPPart()
0560: .getEnvelope();
0561: if ("true".equalsIgnoreCase((String) value)) {
0562: env.setOmitXmlDecl("no");
0563: } else if ("false".equalsIgnoreCase((String) value)) {
0564: env.setOmitXmlDecl("yes");
0565: }
0566: } catch (Exception e) {
0567: log
0568: .log(
0569: Level.SEVERE,
0570: "SAAJ0591.soap.exception.in.set.property",
0571: new Object[] { e.getMessage(),
0572: "javax.xml.soap.write-xml-declaration" });
0573: throw new RuntimeException(e);
0574: }
0575: return;
0576: }
0577:
0578: if (property
0579: .equalsIgnoreCase(SOAPMessage.CHARACTER_SET_ENCODING)) {
0580: try {
0581: ((EnvelopeImpl) getSOAPPart().getEnvelope())
0582: .setCharsetEncoding((String) value);
0583: } catch (Exception e) {
0584: log
0585: .log(
0586: Level.SEVERE,
0587: "SAAJ0591.soap.exception.in.set.property",
0588: new Object[] { e.getMessage(),
0589: "javax.xml.soap.character-set-encoding" });
0590: throw new RuntimeException(e);
0591: }
0592: }
0593: }
0594:
0595: protected abstract boolean isCorrectSoapVersion(int contentTypeId);
0596:
0597: protected abstract String getExpectedContentType();
0598:
0599: protected abstract String getExpectedAcceptHeader();
0600:
0601: /**
0602: * Sniffs the Content-Type header so that we can determine how to process.
0603: *
0604: * <p>
0605: * In the absence of type attribute we assume it to be text/xml.
0606: * That would mean we're easy on accepting the message and
0607: * generate the correct thing (as the SWA spec also specifies
0608: * that the type parameter should always be text/xml)
0609: *
0610: * @return
0611: * combination of flags, such as PLAIN_XML_CODE and MIME_MULTIPART_CODE.
0612: */
0613: // SOAP1.2 allow SOAP1.2 content type
0614: static int identifyContentType(ContentType contentType)
0615: throws SOAPExceptionImpl {
0616: // TBD
0617: // Is there anything else we need to verify here?
0618:
0619: String primary = contentType.getPrimaryType().toLowerCase();
0620: String sub = contentType.getSubType().toLowerCase();
0621:
0622: if (primary.equals("multipart")) {
0623: if (sub.equals("related")) {
0624: String type = getTypeParameter(contentType);
0625: if (isEqualToSoap1_1Type(type)) {
0626: return (type.equals("application/fastinfoset") ? FI_ENCODED_FLAG
0627: : 0)
0628: | MIME_MULTIPART_FLAG | SOAP1_1_FLAG;
0629: } else if (isEqualToSoap1_2Type(type)) {
0630: return (type.equals("application/soap+fastinfoset") ? FI_ENCODED_FLAG
0631: : 0)
0632: | MIME_MULTIPART_FLAG | SOAP1_2_FLAG;
0633: } else if (isMimeMultipartXOPPackage(contentType)) {
0634: return MIME_MULTIPART_XOP_FLAG;
0635: } else {
0636: log
0637: .severe("SAAJ0536.soap.content-type.mustbe.multipart");
0638: throw new SOAPExceptionImpl(
0639: "Content-Type needs to be Multipart/Related "
0640: + "and with \"type=text/xml\" "
0641: + "or \"type=application/soap+xml\"");
0642: }
0643: } else {
0644: log.severe("SAAJ0537.soap.invalid.content-type");
0645: throw new SOAPExceptionImpl("Invalid Content-Type: "
0646: + primary + '/' + sub);
0647: }
0648: } else if (isSoap1_1Type(primary, sub)) {
0649: return (primary.equalsIgnoreCase("application")
0650: && sub.equalsIgnoreCase("fastinfoset") ? FI_ENCODED_FLAG
0651: : 0)
0652: | PLAIN_XML_FLAG | SOAP1_1_FLAG;
0653: } else if (isSoap1_2Type(primary, sub)) {
0654: return (primary.equalsIgnoreCase("application")
0655: && sub.equalsIgnoreCase("soap+fastinfoset") ? FI_ENCODED_FLAG
0656: : 0)
0657: | PLAIN_XML_FLAG | SOAP1_2_FLAG;
0658: } else if (isSOAPBodyXOPPackage(contentType)) {
0659: return XOP_FLAG;
0660: } else {
0661: log.severe("SAAJ0537.soap.invalid.content-type");
0662: throw new SOAPExceptionImpl(
0663: "Invalid Content-Type:"
0664: + primary
0665: + '/'
0666: + sub
0667: + ". Is this an error message instead of a SOAP response?");
0668: }
0669: }
0670:
0671: /**
0672: * Obtains the type parameter of the Content-Type header. Defaults to "text/xml".
0673: */
0674: private static String getTypeParameter(ContentType contentType) {
0675: String p = contentType.getParameter("type");
0676: if (p != null)
0677: return p.toLowerCase();
0678: else
0679: return "text/xml";
0680: }
0681:
0682: public MimeHeaders getMimeHeaders() {
0683: return this .headers;
0684: }
0685:
0686: final static String getContentType(MimeHeaders headers) {
0687: String[] values = headers.getHeader("Content-Type");
0688: if (values == null)
0689: return null;
0690: else
0691: return values[0];
0692: }
0693:
0694: /*
0695: * Get the complete ContentType value along with optional parameters.
0696: */
0697: public String getContentType() {
0698: return getContentType(this .headers);
0699: }
0700:
0701: public void setContentType(String type) {
0702: headers.setHeader("Content-Type", type);
0703: needsSave();
0704: }
0705:
0706: private ContentType ContentType() {
0707: ContentType ct = null;
0708: try {
0709: ct = new ContentType(getContentType());
0710: } catch (Exception e) {
0711: // what to do here?
0712: }
0713: return ct;
0714: }
0715:
0716: /*
0717: * Return the MIME type string, without the parameters.
0718: */
0719: public String getBaseType() {
0720: return ContentType().getBaseType();
0721: }
0722:
0723: public void setBaseType(String type) {
0724: ContentType ct = ContentType();
0725: ct.setParameter("type", type);
0726: headers.setHeader("Content-Type", ct.toString());
0727: needsSave();
0728: }
0729:
0730: public String getAction() {
0731: return ContentType().getParameter("action");
0732: }
0733:
0734: public void setAction(String action) {
0735: ContentType ct = ContentType();
0736: ct.setParameter("action", action);
0737: headers.setHeader("Content-Type", ct.toString());
0738: needsSave();
0739: }
0740:
0741: public String getCharset() {
0742: return ContentType().getParameter("charset");
0743: }
0744:
0745: public void setCharset(String charset) {
0746: ContentType ct = ContentType();
0747: ct.setParameter("charset", charset);
0748: headers.setHeader("Content-Type", ct.toString());
0749: needsSave();
0750: }
0751:
0752: /**
0753: * All write methods (i.e setters) should call this method in
0754: * order to make sure that a save is necessary since the state
0755: * has been modified.
0756: */
0757: private final void needsSave() {
0758: saved = false;
0759: }
0760:
0761: public boolean saveRequired() {
0762: return saved != true;
0763: }
0764:
0765: public String getContentDescription() {
0766: String[] values = headers.getHeader("Content-Description");
0767: if (values != null && values.length > 0)
0768: return values[0];
0769: return null;
0770: }
0771:
0772: public void setContentDescription(String description) {
0773: headers.setHeader("Content-Description", description);
0774: needsSave();
0775: }
0776:
0777: public abstract SOAPPart getSOAPPart();
0778:
0779: public void removeAllAttachments() {
0780: try {
0781: initializeAllAttachments();
0782: } catch (Exception e) {
0783: throw new RuntimeException(e);
0784: }
0785:
0786: if (attachments != null) {
0787: attachments.clear();
0788: needsSave();
0789: }
0790: }
0791:
0792: public int countAttachments() {
0793: try {
0794: initializeAllAttachments();
0795: } catch (Exception e) {
0796: throw new RuntimeException(e);
0797: }
0798: if (attachments != null)
0799: return attachments.size();
0800: return 0;
0801: }
0802:
0803: public void addAttachmentPart(AttachmentPart attachment) {
0804: try {
0805: initializeAllAttachments();
0806: } catch (Exception e) {
0807: throw new RuntimeException(e);
0808: }
0809: if (attachments == null)
0810: attachments = new FinalArrayList();
0811:
0812: attachments.add(attachment);
0813:
0814: needsSave();
0815: }
0816:
0817: static private final Iterator nullIter = Collections.EMPTY_LIST
0818: .iterator();
0819:
0820: public Iterator getAttachments() {
0821: try {
0822: initializeAllAttachments();
0823: } catch (Exception e) {
0824: throw new RuntimeException(e);
0825: }
0826: if (attachments == null)
0827: return nullIter;
0828: return attachments.iterator();
0829: }
0830:
0831: private class MimeMatchingIterator implements Iterator {
0832: public MimeMatchingIterator(MimeHeaders headers) {
0833: this .headers = headers;
0834: this .iter = attachments.iterator();
0835: }
0836:
0837: private Iterator iter;
0838: private MimeHeaders headers;
0839: private Object nextAttachment;
0840:
0841: public boolean hasNext() {
0842: if (nextAttachment == null)
0843: nextAttachment = nextMatch();
0844: return nextAttachment != null;
0845: }
0846:
0847: public Object next() {
0848: if (nextAttachment != null) {
0849: Object ret = nextAttachment;
0850: nextAttachment = null;
0851: return ret;
0852: }
0853:
0854: if (hasNext())
0855: return nextAttachment;
0856:
0857: return null;
0858: }
0859:
0860: Object nextMatch() {
0861: while (iter.hasNext()) {
0862: AttachmentPartImpl ap = (AttachmentPartImpl) iter
0863: .next();
0864: if (ap.hasAllHeaders(headers))
0865: return ap;
0866: }
0867: return null;
0868: }
0869:
0870: public void remove() {
0871: iter.remove();
0872: }
0873: }
0874:
0875: public Iterator getAttachments(MimeHeaders headers) {
0876: try {
0877: initializeAllAttachments();
0878: } catch (Exception e) {
0879: throw new RuntimeException(e);
0880: }
0881: if (attachments == null)
0882: return nullIter;
0883:
0884: return new MimeMatchingIterator(headers);
0885: }
0886:
0887: public void removeAttachments(MimeHeaders headers) {
0888: try {
0889: initializeAllAttachments();
0890: } catch (Exception e) {
0891: throw new RuntimeException(e);
0892: }
0893: if (attachments == null)
0894: return;
0895:
0896: Iterator it = new MimeMatchingIterator(headers);
0897: while (it.hasNext()) {
0898: int index = attachments.indexOf(it.next());
0899: attachments.set(index, null);
0900: }
0901: FinalArrayList f = new FinalArrayList();
0902: for (int i = 0; i < attachments.size(); i++) {
0903: if (attachments.get(i) != null) {
0904: f.add(attachments.get(i));
0905: }
0906: }
0907: attachments = f;
0908: // needsSave();
0909: }
0910:
0911: public AttachmentPart createAttachmentPart() {
0912: return new AttachmentPartImpl();
0913: }
0914:
0915: public AttachmentPart getAttachment(SOAPElement element)
0916: throws SOAPException {
0917: try {
0918: initializeAllAttachments();
0919: } catch (Exception e) {
0920: throw new RuntimeException(e);
0921: }
0922: String uri;
0923: String hrefAttr = element.getAttribute("href");
0924: if ("".equals(hrefAttr)) {
0925: Node node = getValueNodeStrict(element);
0926: String swaRef = null;
0927: if (node != null) {
0928: swaRef = node.getValue();
0929: }
0930: if (swaRef == null || "".equals(swaRef)) {
0931: return null;
0932: } else {
0933: uri = swaRef;
0934: }
0935: } else {
0936: uri = hrefAttr;
0937: }
0938: return getAttachmentPart(uri);
0939: }
0940:
0941: private Node getValueNodeStrict(SOAPElement element) {
0942: Node node = (Node) element.getFirstChild();
0943: if (node != null) {
0944: if (node.getNextSibling() == null
0945: && node.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
0946: return node;
0947: } else {
0948: return null;
0949: }
0950: }
0951: return null;
0952: }
0953:
0954: private AttachmentPart getAttachmentPart(String uri)
0955: throws SOAPException {
0956: AttachmentPart _part;
0957: try {
0958: if (uri.startsWith("cid:")) {
0959: // rfc2392
0960: uri = '<' + uri.substring("cid:".length()) + '>';
0961:
0962: MimeHeaders headersToMatch = new MimeHeaders();
0963: headersToMatch.addHeader(CONTENT_ID, uri);
0964:
0965: Iterator i = this .getAttachments(headersToMatch);
0966: _part = (i == null) ? null : (AttachmentPart) i.next();
0967: } else {
0968: // try content-location
0969: MimeHeaders headersToMatch = new MimeHeaders();
0970: headersToMatch.addHeader(CONTENT_LOCATION, uri);
0971:
0972: Iterator i = this .getAttachments(headersToMatch);
0973: _part = (i == null) ? null : (AttachmentPart) i.next();
0974: }
0975:
0976: // try auto-generated JAXRPC CID
0977: if (_part == null) {
0978: Iterator j = this .getAttachments();
0979:
0980: while (j.hasNext()) {
0981: AttachmentPart p = (AttachmentPart) j.next();
0982: String cl = p.getContentId();
0983: if (cl != null) {
0984: // obtain the partname
0985: int eqIndex = cl.indexOf("=");
0986: if (eqIndex > -1) {
0987: cl = cl.substring(1, eqIndex);
0988: if (cl.equalsIgnoreCase(uri)) {
0989: _part = p;
0990: break;
0991: }
0992: }
0993: }
0994: }
0995: }
0996:
0997: } catch (Exception se) {
0998: log.log(Level.SEVERE,
0999: "SAAJ0590.soap.unable.to.locate.attachment",
1000: new Object[] { uri });
1001: throw new SOAPExceptionImpl(se);
1002: }
1003: return _part;
1004: }
1005:
1006: private final ByteInputStream getHeaderBytes() throws IOException {
1007: SOAPPartImpl sp = (SOAPPartImpl) getSOAPPart();
1008: return sp.getContentAsStream();
1009: }
1010:
1011: private String convertToSingleLine(String contentType) {
1012: StringBuffer buffer = new StringBuffer();
1013: for (int i = 0; i < contentType.length(); i++) {
1014: char c = contentType.charAt(i);
1015: if (c != '\r' && c != '\n' && c != '\t')
1016: buffer.append(c);
1017: }
1018: return buffer.toString();
1019: }
1020:
1021: private MimeMultipart getMimeMessage() throws SOAPException {
1022: try {
1023: SOAPPartImpl soapPart = (SOAPPartImpl) getSOAPPart();
1024: MimeBodyPart mimeSoapPart = soapPart.getMimePart();
1025:
1026: /*
1027: * Get content type from this message instead of soapPart
1028: * to ensure agreement if soapPart is transcoded (XML <-> FI)
1029: */
1030: ContentType soapPartCtype = new ContentType(
1031: getExpectedContentType());
1032:
1033: if (!isFastInfoset) {
1034: soapPartCtype.setParameter("charset", initCharset());
1035: }
1036: mimeSoapPart.setHeader("Content-Type", soapPartCtype
1037: .toString());
1038:
1039: MimeMultipart headerAndBody = null;
1040:
1041: if (!switchOffBM && !switchOffLazyAttachment
1042: && (multiPart != null) && !attachmentsInitialized) {
1043: headerAndBody = new BMMimeMultipart();
1044: headerAndBody.addBodyPart(mimeSoapPart);
1045: if (attachments != null) {
1046: for (Iterator eachAttachment = attachments
1047: .iterator(); eachAttachment.hasNext();) {
1048: headerAndBody
1049: .addBodyPart(((AttachmentPartImpl) eachAttachment
1050: .next()).getMimePart());
1051: }
1052: }
1053: InputStream in = ((BMMimeMultipart) multiPart)
1054: .getInputStream();
1055: if (!((BMMimeMultipart) multiPart).lastBodyPartFound()
1056: && !((BMMimeMultipart) multiPart)
1057: .isEndOfStream()) {
1058: ((BMMimeMultipart) headerAndBody)
1059: .setInputStream(in);
1060: ((BMMimeMultipart) headerAndBody)
1061: .setBoundary(((BMMimeMultipart) multiPart)
1062: .getBoundary());
1063: ((BMMimeMultipart) headerAndBody)
1064: .setLazyAttachments(lazyAttachments);
1065: }
1066:
1067: } else {
1068: headerAndBody = new MimeMultipart();
1069: headerAndBody.addBodyPart(mimeSoapPart);
1070:
1071: for (Iterator eachAttachement = getAttachments(); eachAttachement
1072: .hasNext();) {
1073: headerAndBody
1074: .addBodyPart(((AttachmentPartImpl) eachAttachement
1075: .next()).getMimePart());
1076: }
1077: }
1078:
1079: ContentType contentType = headerAndBody.getContentType();
1080:
1081: ParameterList l = contentType.getParameterList();
1082:
1083: // set content type depending on SOAP version
1084: l.set("type", getExpectedContentType());
1085: l.set("boundary", contentType.getParameter("boundary"));
1086: ContentType nct = new ContentType("multipart", "related", l);
1087:
1088: headers.setHeader("Content-Type", convertToSingleLine(nct
1089: .toString()));
1090: // TBD
1091: // Set content length MIME header here.
1092:
1093: return headerAndBody;
1094: } catch (SOAPException ex) {
1095: throw ex;
1096: } catch (Throwable ex) {
1097: log
1098: .severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
1099: throw new SOAPExceptionImpl(
1100: "Unable to convert SOAP message into "
1101: + "a MimeMultipart object", ex);
1102: }
1103: }
1104:
1105: private String initCharset() {
1106:
1107: String charset = null;
1108:
1109: String[] cts = getMimeHeaders().getHeader("Content-Type");
1110: if ((cts != null) && (cts[0] != null)) {
1111: charset = getCharsetString(cts[0]);
1112: }
1113:
1114: if (charset == null) {
1115: charset = (String) getProperty(CHARACTER_SET_ENCODING);
1116: }
1117:
1118: if (charset != null) {
1119: return charset;
1120: }
1121:
1122: return "utf-8";
1123: }
1124:
1125: private String getCharsetString(String s) {
1126: try {
1127: int index = s.indexOf(";");
1128: if (index < 0)
1129: return null;
1130: ParameterList pl = new ParameterList(s.substring(index));
1131: return pl.get("charset");
1132: } catch (Exception e) {
1133: return null;
1134: }
1135: }
1136:
1137: public void saveChanges() throws SOAPException {
1138:
1139: // suck in all the data from the attachments and have it
1140: // ready for writing/sending etc.
1141:
1142: String charset = initCharset();
1143:
1144: /*if (countAttachments() == 0) {*/
1145: int attachmentCount = (attachments == null) ? 0 : attachments
1146: .size();
1147: if (attachmentCount == 0) {
1148: if (!switchOffBM && !switchOffLazyAttachment
1149: && !attachmentsInitialized && (multiPart != null)) {
1150: // so there might be attachments
1151: attachmentCount = 1;
1152: }
1153: }
1154:
1155: try {
1156: if ((attachmentCount == 0) && !hasXOPContent()) {
1157: ByteInputStream in;
1158: try {
1159: /*
1160: * Not sure why this is called getHeaderBytes(), but it actually
1161: * returns the whole message as a byte stream. This stream could
1162: * be either XML of Fast depending on the mode.
1163: */
1164: in = getHeaderBytes();
1165: // no attachments, hence this property can be false
1166: this .optimizeAttachmentProcessing = false;
1167: } catch (IOException ex) {
1168: log
1169: .severe("SAAJ0539.soap.cannot.get.header.stream");
1170: throw new SOAPExceptionImpl(
1171: "Unable to get header stream in saveChanges: ",
1172: ex);
1173: }
1174:
1175: messageBytes = in.getBytes();
1176: messageByteCount = in.getCount();
1177:
1178: headers.setHeader("Content-Type",
1179: getExpectedContentType()
1180: + (isFastInfoset ? "" : "; charset="
1181: + charset));
1182: headers.setHeader("Content-Length", Integer
1183: .toString(messageByteCount));
1184: } else {
1185: if (hasXOPContent())
1186: mmp = getXOPMessage();
1187: else
1188: mmp = getMimeMessage();
1189: }
1190: } catch (Throwable ex) {
1191: log.severe("SAAJ0540.soap.err.saving.multipart.msg");
1192: throw new SOAPExceptionImpl(
1193: "Error during saving a multipart message", ex);
1194: }
1195:
1196: // FIX ME -- SOAP Action replaced by Content-Type optional parameter action
1197: /*
1198: if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
1199:
1200: String[] soapAction = headers.getHeader("SOAPAction");
1201:
1202: if (soapAction == null || soapAction.length == 0)
1203: headers.setHeader("SOAPAction", "\"\"");
1204:
1205: }
1206: */
1207:
1208: saved = true;
1209: }
1210:
1211: private MimeMultipart getXOPMessage() throws SOAPException {
1212: try {
1213: MimeMultipart headerAndBody = new MimeMultipart();
1214: SOAPPartImpl soapPart = (SOAPPartImpl) getSOAPPart();
1215: MimeBodyPart mimeSoapPart = soapPart.getMimePart();
1216: ContentType soapPartCtype = new ContentType(
1217: "application/xop+xml");
1218: soapPartCtype
1219: .setParameter("type", getExpectedContentType());
1220: String charset = initCharset();
1221: soapPartCtype.setParameter("charset", charset);
1222: mimeSoapPart.setHeader("Content-Type", soapPartCtype
1223: .toString());
1224: headerAndBody.addBodyPart(mimeSoapPart);
1225:
1226: for (Iterator eachAttachement = getAttachments(); eachAttachement
1227: .hasNext();) {
1228: headerAndBody
1229: .addBodyPart(((AttachmentPartImpl) eachAttachement
1230: .next()).getMimePart());
1231: }
1232:
1233: ContentType contentType = headerAndBody.getContentType();
1234:
1235: ParameterList l = contentType.getParameterList();
1236:
1237: //lets not write start-info for now till we get servlet fix done
1238: l.set("start-info", getExpectedContentType());//+";charset="+initCharset());
1239:
1240: // set content type depending on SOAP version
1241: l.set("type", "application/xop+xml");
1242:
1243: if (isCorrectSoapVersion(SOAP1_2_FLAG)) {
1244: String action = getAction();
1245: if (action != null)
1246: l.set("action", action);
1247: }
1248:
1249: l.set("boundary", contentType.getParameter("boundary"));
1250: ContentType nct = new ContentType("Multipart", "Related", l);
1251: headers.setHeader("Content-Type", convertToSingleLine(nct
1252: .toString()));
1253: // TBD
1254: // Set content length MIME header here.
1255:
1256: return headerAndBody;
1257: } catch (SOAPException ex) {
1258: throw ex;
1259: } catch (Throwable ex) {
1260: log
1261: .severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
1262: throw new SOAPExceptionImpl(
1263: "Unable to convert SOAP message into "
1264: + "a MimeMultipart object", ex);
1265: }
1266:
1267: }
1268:
1269: private boolean hasXOPContent() throws ParseException {
1270: String type = getContentType();
1271: if (type == null)
1272: return false;
1273: ContentType ct = new ContentType(type);
1274: return isMimeMultipartXOPPackage(ct)
1275: || isSOAPBodyXOPPackage(ct);
1276: }
1277:
1278: public void writeTo(OutputStream out) throws SOAPException,
1279: IOException {
1280: if (saveRequired()) {
1281: this .optimizeAttachmentProcessing = true;
1282: saveChanges();
1283: }
1284:
1285: if (!optimizeAttachmentProcessing) {
1286: out.write(messageBytes, 0, messageByteCount);
1287: } else {
1288: try {
1289: if (hasXOPContent()) {
1290: mmp.writeTo(out);
1291: } else {
1292: mmp.writeTo(out);
1293: if (!switchOffBM && !switchOffLazyAttachment
1294: && (multiPart != null)
1295: && !attachmentsInitialized) {
1296: ((BMMimeMultipart) multiPart)
1297: .setInputStream(((BMMimeMultipart) mmp)
1298: .getInputStream());
1299: }
1300: }
1301: } catch (Exception ex) {
1302: log.severe("SAAJ0540.soap.err.saving.multipart.msg");
1303: throw new SOAPExceptionImpl(
1304: "Error during saving a multipart message", ex);
1305: }
1306: }
1307:
1308: if (isCorrectSoapVersion(SOAP1_1_FLAG)) {
1309:
1310: String[] soapAction = headers.getHeader("SOAPAction");
1311:
1312: if (soapAction == null || soapAction.length == 0)
1313: headers.setHeader("SOAPAction", "\"\"");
1314:
1315: }
1316:
1317: messageBytes = null;
1318: needsSave();
1319: }
1320:
1321: public SOAPBody getSOAPBody() throws SOAPException {
1322: SOAPBody body = getSOAPPart().getEnvelope().getBody();
1323: /*if (body == null) {
1324: throw new SOAPException("No SOAP Body was found in the SOAP Message");
1325: }*/
1326: return body;
1327: }
1328:
1329: public SOAPHeader getSOAPHeader() throws SOAPException {
1330: SOAPHeader hdr = getSOAPPart().getEnvelope().getHeader();
1331: /*if (hdr == null) {
1332: throw new SOAPException("No SOAP Header was found in the SOAP Message");
1333: }*/
1334: return hdr;
1335: }
1336:
1337: private void initializeAllAttachments() throws MessagingException,
1338: SOAPException {
1339: if (switchOffBM || switchOffLazyAttachment) {
1340: return;
1341: }
1342:
1343: if (attachmentsInitialized || (multiPart == null)) {
1344: return;
1345: }
1346:
1347: if (attachments == null)
1348: attachments = new FinalArrayList();
1349:
1350: int count = multiPart.getCount();
1351: for (int i = 0; i < count; i++) {
1352: initializeAttachment(multiPart.getBodyPart(i));
1353: }
1354: attachmentsInitialized = true;
1355: //multiPart = null;
1356: needsSave();
1357: }
1358:
1359: private void initializeAttachment(MimeBodyPart mbp)
1360: throws SOAPException {
1361: AttachmentPartImpl attachmentPart = new AttachmentPartImpl();
1362: DataHandler attachmentHandler = mbp.getDataHandler();
1363: attachmentPart.setDataHandler(attachmentHandler);
1364:
1365: AttachmentPartImpl.copyMimeHeaders(mbp, attachmentPart);
1366: attachments.add(attachmentPart);
1367: }
1368:
1369: private void initializeAttachment(MimeMultipart multiPart, int i)
1370: throws Exception {
1371: MimeBodyPart currentBodyPart = multiPart.getBodyPart(i);
1372: AttachmentPartImpl attachmentPart = new AttachmentPartImpl();
1373:
1374: DataHandler attachmentHandler = currentBodyPart
1375: .getDataHandler();
1376: attachmentPart.setDataHandler(attachmentHandler);
1377:
1378: AttachmentPartImpl.copyMimeHeaders(currentBodyPart,
1379: attachmentPart);
1380: addAttachmentPart(attachmentPart);
1381: }
1382:
1383: private void setMimeHeaders(SOAPPart soapPart,
1384: MimeBodyPart soapMessagePart) throws Exception {
1385:
1386: // first remove the existing content-type
1387: soapPart.removeAllMimeHeaders();
1388: // add everything present in soapMessagePart
1389: List headers = soapMessagePart.getAllHeaders();
1390: int sz = headers.size();
1391: for (int i = 0; i < sz; i++) {
1392: Header h = (Header) headers.get(i);
1393: soapPart.addMimeHeader(h.getName(), h.getValue());
1394: }
1395: }
1396:
1397: private void initCharsetProperty(ContentType contentType) {
1398: String charset = contentType.getParameter("charset");
1399: if (charset != null) {
1400: ((SOAPPartImpl) getSOAPPart())
1401: .setSourceCharsetEncoding(charset);
1402: if (!charset.equalsIgnoreCase("utf-8"))
1403: setProperty(CHARACTER_SET_ENCODING, charset);
1404: }
1405: }
1406:
1407: public void setLazyAttachments(boolean flag) {
1408: lazyAttachments = flag;
1409: }
1410:
1411: }
|