src/share/classes/com/sun/xml/internal/messaging/saaj/soap/MessageImpl.java

Mon, 04 May 2009 21:10:41 -0700

author
tbell
date
Mon, 04 May 2009 21:10:41 -0700
changeset 50
42dfec6871f6
parent 45
31822b475baa
child 78
860b95cc8d1d
permissions
-rw-r--r--

6658158: Mutable statics in SAAJ (findbugs)
6658163: txw2.DatatypeWriter.BUILDIN is a mutable static (findbugs)
Reviewed-by: darcy

     1 /*
     2  * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    25 /*
    26  *
    27  *
    28  *
    29  */
    32 package com.sun.xml.internal.messaging.saaj.soap;
    34 import java.io.*;
    35 import java.util.*;
    36 import java.util.logging.Level;
    37 import java.util.logging.Logger;
    39 import javax.activation.DataHandler;
    40 import javax.activation.DataSource;
    41 import javax.xml.soap.*;
    42 import javax.xml.transform.Source;
    43 import javax.xml.transform.stream.StreamSource;
    45 import com.sun.xml.internal.messaging.saaj.packaging.mime.Header;
    46 import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.*;
    47 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*;
    48 import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException;
    50 import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl;
    51 import com.sun.xml.internal.messaging.saaj.soap.impl.EnvelopeImpl;
    52 import com.sun.xml.internal.messaging.saaj.util.*;
    54 /**
    55  * The message implementation for SOAP messages with
    56  * attachments. Messages for specific profiles will likely extend this
    57  * MessageImpl class and add more value for that particular profile.
    58  *
    59  * @author Anil Vijendran (akv@eng.sun.com)
    60  * @author Rajiv Mordani (rajiv.mordani@sun.com)
    61  * @author Manveen Kaur (manveen.kaur@sun.com)
    62  */
    64 public abstract class MessageImpl
    65     extends SOAPMessage
    66     implements SOAPConstants {
    69     public static final String CONTENT_ID             = "Content-ID";
    70     public static final String CONTENT_LOCATION       = "Content-Location";
    72     protected static final Logger log =
    73         Logger.getLogger(LogDomainConstants.SOAP_DOMAIN,
    74                          "com.sun.xml.internal.messaging.saaj.soap.LocalStrings");
    76     protected static final int PLAIN_XML_FLAG      = 1;      // 00001
    77     protected static final int MIME_MULTIPART_FLAG = 2;      // 00010
    78     protected static final int SOAP1_1_FLAG = 4;             // 00100
    79     protected static final int SOAP1_2_FLAG = 8;             // 01000
    80     protected static final int MIME_MULTIPART_XOP_FLAG = 14; // 01110
    81     protected static final int XOP_FLAG = 13;                // 01101
    82     protected static final int FI_ENCODED_FLAG     = 16;     // 10000
    84     protected MimeHeaders headers;
    85     protected SOAPPartImpl soapPart;
    86     protected FinalArrayList attachments;
    87     protected boolean saved = false;
    88     protected byte[] messageBytes;
    89     protected int messageByteCount;
    90     protected HashMap properties = new HashMap();
    92     // used for lazy attachment initialization
    93     protected MimeMultipart multiPart = null;
    94     protected boolean attachmentsInitialized = false;
    96     /**
    97      * True if this part is encoded using Fast Infoset.
    98      * MIME -> application/fastinfoset
    99      */
   100     protected boolean isFastInfoset = false;
   102     /**
   103      * True if the Accept header of this message includes
   104      * application/fastinfoset
   105      */
   106     protected boolean acceptFastInfoset = false;
   108     protected MimeMultipart mmp = null;
   110     // if attachments are present, don't read the entire message in byte stream in saveTo()
   111     private boolean optimizeAttachmentProcessing = true;
   113     // switch back to old MimeMultipart incase of problem
   114     private static boolean switchOffBM = false;
   115     private static boolean switchOffLazyAttachment = false;
   117     static {
   118         try {
   119             String s = System.getProperty("saaj.mime.optimization");
   120             if ((s != null) && s.equals("false")) {
   121                 switchOffBM = true;
   122             }
   123             s = System.getProperty("saaj.lazy.mime.optimization");
   124             if ((s != null) && s.equals("false")) {
   125                 switchOffLazyAttachment = true;
   126             }
   127         } catch (SecurityException ex) {
   128             // ignore it
   129         }
   130     }
   132     //property to indicate optimized serialization for lazy attachments
   133     private boolean lazyAttachments = false;
   135     // most of the times, Content-Types are already all lower cased.
   136     // String.toLowerCase() works faster in this case, so even if you
   137     // are only doing one comparison, it pays off to use String.toLowerCase()
   138     // than String.equalsIgnoreCase(). When you do more than one comparison,
   139     // the benefits of String.toLowerCase() dominates.
   140     //
   141     //
   142     // for FI,
   143     //   use application/fastinfoset for SOAP 1.1
   144     //   use application/soap+fastinfoset for SOAP 1.2
   145     // to speed up comparisons, test methods always use lower cases.
   147     /**
   148      * @param primary
   149      *      must be all lower case
   150      * @param sub
   151      *      must be all lower case
   152      */
   153     private static boolean isSoap1_1Type(String primary, String sub) {
   154         return primary.equals("text") && sub.equals("xml")
   155             || primary.equals("application")
   156                && sub.equals("fastinfoset");
   157     }
   159     /**
   160      * @param type
   161      *      must be all lower case
   162      */
   163     private static boolean isEqualToSoap1_1Type(String type) {
   164         return type.startsWith("text/xml") ||
   165                type.startsWith("application/fastinfoset");
   166     }
   168     /**
   169      * @param primary
   170      *      must be all lower case
   171      * @param sub
   172      *      must be all lower case
   173      */
   174     private static boolean isSoap1_2Type(String primary, String sub) {
   175         return primary.equals("application")
   176                && (sub.equals("soap+xml")
   177                    || sub.equals("soap+fastinfoset"));
   178     }
   180     /**
   181      * @param type
   182      *      must be all lower case
   183      */
   184     private static boolean isEqualToSoap1_2Type(String type) {
   185         return type.startsWith("application/soap+xml") ||
   186                type.startsWith("application/soap+fastinfoset");
   187     }
   189     /**
   190       * Construct a new message. This will be invoked before message
   191       * sends.
   192       */
   193     protected MessageImpl() {
   194         this(false, false);
   195         attachmentsInitialized = true;
   196     }
   198     /**
   199       * Construct a new message. This will be invoked before message
   200       * sends.
   201       */
   202     protected MessageImpl(boolean isFastInfoset, boolean acceptFastInfoset) {
   203         this.isFastInfoset = isFastInfoset;
   204         this.acceptFastInfoset = acceptFastInfoset;
   206         headers = new MimeHeaders();
   207         headers.setHeader("Accept", getExpectedAcceptHeader());
   208     }
   210     /**
   211      * Shallow copy.
   212      */
   213     protected MessageImpl(SOAPMessage msg) {
   214         if (!(msg instanceof MessageImpl)) {
   215             // don't know how to handle this.
   216         }
   217         MessageImpl src = (MessageImpl) msg;
   218         this.headers = src.headers;
   219         this.soapPart = src.soapPart;
   220         this.attachments = src.attachments;
   221         this.saved = src.saved;
   222         this.messageBytes = src.messageBytes;
   223         this.messageByteCount = src.messageByteCount;
   224         this.properties = src.properties;
   225     }
   227     /**
   228      * @param stat
   229      *      the mask value obtained from {@link #identifyContentType(ContentType)}
   230      */
   231     protected static boolean isSoap1_1Content(int stat) {
   232         return (stat & SOAP1_1_FLAG) != 0;
   233     }
   235     /**
   236      * @param stat
   237      *      the mask value obtained from {@link #identifyContentType(ContentType)}
   238      */
   239     protected static boolean isSoap1_2Content(int stat) {
   240         return (stat & SOAP1_2_FLAG) != 0;
   241     }
   243      private static boolean isMimeMultipartXOPPackage(ContentType contentType) {
   244         String type = contentType.getParameter("type");
   245         if(type==null)
   246             return false;
   248         type = type.toLowerCase();
   249         if(!type.startsWith("application/xop+xml"))
   250             return false;
   252         String startinfo = contentType.getParameter("start-info");
   253         if(startinfo == null)
   254             return false;
   255         startinfo = startinfo.toLowerCase();
   256         return isEqualToSoap1_2Type(startinfo) || isEqualToSoap1_1Type(startinfo);
   257     }
   259     private static boolean isSOAPBodyXOPPackage(ContentType contentType){
   260         String primary = contentType.getPrimaryType();
   261         String sub = contentType.getSubType();
   263         if (primary.equalsIgnoreCase("application")) {
   264             if (sub.equalsIgnoreCase("xop+xml")) {
   265                 String type = getTypeParameter(contentType);
   266                 return isEqualToSoap1_2Type(type) || isEqualToSoap1_1Type(type);
   267             }
   268         }
   269         return false;
   270     }
   272     /**
   273      * Construct a message from an input stream. When messages are
   274      * received, there's two parts -- the transport headers and the
   275      * message content in a transport specific stream.
   276      */
   277     protected MessageImpl(MimeHeaders headers, final InputStream in)
   278         throws SOAPExceptionImpl {
   279         ContentType ct = parseContentType(headers);
   280         init(headers,identifyContentType(ct),ct,in);
   281     }
   283     private static ContentType parseContentType(MimeHeaders headers) throws SOAPExceptionImpl {
   284         final String ct;
   285         if (headers != null)
   286             ct = getContentType(headers);
   287         else {
   288             log.severe("SAAJ0550.soap.null.headers");
   289             throw new SOAPExceptionImpl("Cannot create message: " +
   290                                         "Headers can't be null");
   291         }
   293         if (ct == null) {
   294             log.severe("SAAJ0532.soap.no.Content-Type");
   295             throw new SOAPExceptionImpl("Absent Content-Type");
   296         }
   297         try {
   298             return new ContentType(ct);
   299         } catch (Throwable ex) {
   300             log.severe("SAAJ0535.soap.cannot.internalize.message");
   301             throw new SOAPExceptionImpl("Unable to internalize message", ex);
   302         }
   303     }
   305     /**
   306      * Construct a message from an input stream. When messages are
   307      * received, there's two parts -- the transport headers and the
   308      * message content in a transport specific stream.
   309      *
   310      * @param contentType
   311      *      The parsed content type header from the headers variable.
   312      *      This is redundant parameter, but it avoids reparsing this header again.
   313      * @param stat
   314      *      The result of {@link #identifyContentType(ContentType)} over
   315      *      the contentType parameter. This redundant parameter, but it avoids
   316      *      recomputing this information again.
   317      */
   318     protected MessageImpl(MimeHeaders headers, final ContentType contentType, int stat, final InputStream in) throws SOAPExceptionImpl {
   319         init(headers, stat, contentType, in);
   321     }
   323     private void init(MimeHeaders headers, int stat, final ContentType contentType, final InputStream in) throws SOAPExceptionImpl {
   324         this.headers = headers;
   326         try {
   328             // Set isFastInfoset/acceptFastInfoset flag based on MIME type
   329             if ((stat & FI_ENCODED_FLAG) > 0) {
   330                 isFastInfoset = acceptFastInfoset = true;
   331             }
   333             // If necessary, inspect Accept header to set acceptFastInfoset
   334             if (!isFastInfoset) {
   335                 String[] values = headers.getHeader("Accept");
   336                 if (values != null) {
   337                     for (int i = 0; i < values.length; i++) {
   338                         StringTokenizer st = new StringTokenizer(values[i], ",");
   339                         while (st.hasMoreTokens()) {
   340                             final String token = st.nextToken().trim();
   341                             if (token.equalsIgnoreCase("application/fastinfoset") ||
   342                                 token.equalsIgnoreCase("application/soap+fastinfoset")) {
   343                                 acceptFastInfoset = true;
   344                                 break;
   345                             }
   346                         }
   347                     }
   348                 }
   349             }
   351             if (!isCorrectSoapVersion(stat)) {
   352                 log.log(
   353                     Level.SEVERE,
   354                     "SAAJ0533.soap.incorrect.Content-Type",
   355                     new String[] {
   356                         contentType.toString(),
   357                         getExpectedContentType()});
   358                 throw new SOAPVersionMismatchException(
   359                     "Cannot create message: incorrect content-type for SOAP version. Got: "
   360                         + contentType
   361                         + " Expected: "
   362                         + getExpectedContentType());
   363             }
   365             if ((stat & PLAIN_XML_FLAG) != 0) {
   366                 if (isFastInfoset) {
   367                     getSOAPPart().setContent(
   368                         FastInfosetReflection.FastInfosetSource_new(in));
   369                 } else {
   370                     initCharsetProperty(contentType);
   371                     getSOAPPart().setContent(new StreamSource(in));
   372                 }
   373             }
   374             else if ((stat & MIME_MULTIPART_FLAG) != 0) {
   375                 DataSource ds = new DataSource() {
   376                     public InputStream getInputStream() {
   377                         return in;
   378                     }
   380                     public OutputStream getOutputStream() {
   381                         return null;
   382                     }
   384                     public String getContentType() {
   385                         return contentType.toString();
   386                     }
   388                     public String getName() {
   389                         return "";
   390                     }
   391                 };
   393                 multiPart = null;
   394                 if (switchOffBM) {
   395                     multiPart = new MimeMultipart(ds,contentType);
   396                 } else {
   397                     multiPart = new BMMimeMultipart(ds,contentType);
   398                 }
   400                 String startParam = contentType.getParameter("start");
   401                 MimeBodyPart soapMessagePart = null;
   402                 String contentID = null;
   403                 if (switchOffBM || switchOffLazyAttachment) {
   404                     if(startParam == null) {
   405                         soapMessagePart = multiPart.getBodyPart(0);
   406                         for (int i = 1; i < multiPart.getCount(); i++) {
   407                             initializeAttachment(multiPart, i);
   408                         }
   409                     } else {
   410                         soapMessagePart = multiPart.getBodyPart(startParam);
   411                         for (int i = 0; i < multiPart.getCount(); i++) {
   412                             contentID = multiPart.getBodyPart(i).getContentID();
   413                             if(!contentID.equals(startParam))
   414                                 initializeAttachment(multiPart, i);
   415                         }
   416                     }
   417                 } else {
   418                     BMMimeMultipart bmMultipart =
   419                         (BMMimeMultipart)multiPart;
   420                     InputStream stream = bmMultipart.initStream();
   422                     SharedInputStream sin = null;
   423                     if (stream instanceof SharedInputStream) {
   424                         sin = (SharedInputStream)stream;
   425                     }
   427                     String boundary = "--" +
   428                         contentType.getParameter("boundary");
   429                     byte[] bndbytes = ASCIIUtility.getBytes(boundary);
   430                     if (startParam == null) {
   431                         soapMessagePart =
   432                             bmMultipart.getNextPart(stream, bndbytes, sin);
   433                         bmMultipart.removeBodyPart(soapMessagePart);
   434                     } else {
   435                         MimeBodyPart bp = null;
   436                         try {
   437                             while(!startParam.equals(contentID)) {
   438                                 bp = bmMultipart.getNextPart(
   439                                     stream, bndbytes, sin);
   440                                 contentID = bp.getContentID();
   441                             }
   442                             soapMessagePart = bp;
   443                             bmMultipart.removeBodyPart(bp);
   444                         } catch (Exception e) {
   445                             throw new SOAPExceptionImpl(e);
   446                         }
   447                     }
   448                 }
   450                 ContentType soapPartCType = new ContentType(
   451                                             soapMessagePart.getContentType());
   452                 initCharsetProperty(soapPartCType);
   453                 String baseType = soapPartCType.getBaseType().toLowerCase();
   454                 if(!(isEqualToSoap1_1Type(baseType)
   455                   || isEqualToSoap1_2Type(baseType)
   456                   || isSOAPBodyXOPPackage(soapPartCType))) {
   457                     log.log(Level.SEVERE,
   458                             "SAAJ0549.soap.part.invalid.Content-Type",
   459                             new Object[] {baseType});
   460                     throw new SOAPExceptionImpl(
   461                             "Bad Content-Type for SOAP Part : " +
   462                             baseType);
   463                 }
   465                 SOAPPart soapPart = getSOAPPart();
   466                 setMimeHeaders(soapPart, soapMessagePart);
   467                 soapPart.setContent(isFastInfoset ?
   468                      (Source) FastInfosetReflection.FastInfosetSource_new(
   469                          soapMessagePart.getInputStream()) :
   470                      (Source) new StreamSource(soapMessagePart.getInputStream()));
   471             } else {
   472                 log.severe("SAAJ0534.soap.unknown.Content-Type");
   473                 throw new SOAPExceptionImpl("Unrecognized Content-Type");
   474             }
   475         } catch (Throwable ex) {
   476             log.severe("SAAJ0535.soap.cannot.internalize.message");
   477             throw new SOAPExceptionImpl("Unable to internalize message", ex);
   478         }
   479         needsSave();
   480     }
   482     public boolean isFastInfoset() {
   483         return isFastInfoset;
   484     }
   486     public boolean acceptFastInfoset() {
   487         return acceptFastInfoset;
   488     }
   490     public void setIsFastInfoset(boolean value) {
   491         if (value != isFastInfoset) {
   492             isFastInfoset = value;
   493             if (isFastInfoset) {
   494                 acceptFastInfoset = true;
   495             }
   496             saved = false;      // ensure transcoding if necessary
   497         }
   498     }
   500     public Object getProperty(String property) {
   501         return (String) properties.get(property);
   502     }
   504     public void setProperty(String property, Object value) {
   505         verify(property, value);
   506         properties.put(property, value);
   507     }
   509     private void verify(String property, Object value) {
   510         if (property.equalsIgnoreCase(SOAPMessage.WRITE_XML_DECLARATION)) {
   511             if (!("true".equals(value) || "false".equals(value)))
   512                 throw new RuntimeException(
   513                     property + " must have value false or true");
   515             try {
   516                 EnvelopeImpl env = (EnvelopeImpl) getSOAPPart().getEnvelope();
   517                 if ("true".equalsIgnoreCase((String)value)) {
   518                     env.setOmitXmlDecl("no");
   519                 } else if ("false".equalsIgnoreCase((String)value)) {
   520                     env.setOmitXmlDecl("yes");
   521                 }
   522             } catch (Exception e) {
   523                 log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property",
   524                     new Object[] {e.getMessage(), "javax.xml.soap.write-xml-declaration"});
   525                 throw new RuntimeException(e);
   526             }
   527             return;
   528         }
   530         if (property.equalsIgnoreCase(SOAPMessage.CHARACTER_SET_ENCODING)) {
   531             try {
   532                 ((EnvelopeImpl) getSOAPPart().getEnvelope()).setCharsetEncoding((String)value);
   533             } catch (Exception e) {
   534                 log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property",
   535                     new Object[] {e.getMessage(), "javax.xml.soap.character-set-encoding"});
   536                 throw new RuntimeException(e);
   537             }
   538         }
   539     }
   541     protected abstract boolean isCorrectSoapVersion(int contentTypeId);
   543     protected abstract String getExpectedContentType();
   544     protected abstract String getExpectedAcceptHeader();
   546     /**
   547      * Sniffs the Content-Type header so that we can determine how to process.
   548      *
   549      * <p>
   550      * In the absence of type attribute we assume it to be text/xml.
   551      * That would mean we're easy on accepting the message and
   552      * generate the correct thing (as the SWA spec also specifies
   553      * that the type parameter should always be text/xml)
   554      *
   555      * @return
   556      *      combination of flags, such as PLAIN_XML_CODE and MIME_MULTIPART_CODE.
   557      */
   558     // SOAP1.2 allow SOAP1.2 content type
   559     static int identifyContentType(ContentType contentType)
   560         throws SOAPExceptionImpl {
   561         // TBD
   562         //    Is there anything else we need to verify here?
   564         String primary = contentType.getPrimaryType().toLowerCase();
   565         String sub = contentType.getSubType().toLowerCase();
   567         if (primary.equals("multipart")) {
   568             if (sub.equals("related")) {
   569                 String type = getTypeParameter(contentType);
   570                 if (isEqualToSoap1_1Type(type)) {
   571                     return (type.equals("application/fastinfoset") ?
   572                            FI_ENCODED_FLAG : 0) | MIME_MULTIPART_FLAG | SOAP1_1_FLAG;
   573                 }
   574                 else if (isEqualToSoap1_2Type(type)) {
   575                     return (type.equals("application/soap+fastinfoset") ?
   576                            FI_ENCODED_FLAG : 0) | MIME_MULTIPART_FLAG | SOAP1_2_FLAG;
   577                 } else if (isMimeMultipartXOPPackage(contentType)) {
   578                     return MIME_MULTIPART_XOP_FLAG;
   579                 } else {
   580                     log.severe("SAAJ0536.soap.content-type.mustbe.multipart");
   581                     throw new SOAPExceptionImpl(
   582                         "Content-Type needs to be Multipart/Related "
   583                             + "and with \"type=text/xml\" "
   584                             + "or \"type=application/soap+xml\"");
   585                 }
   586             } else {
   587                 log.severe("SAAJ0537.soap.invalid.content-type");
   588                 throw new SOAPExceptionImpl(
   589                     "Invalid Content-Type: " + primary + '/' + sub);
   590             }
   591         }
   592         else if (isSoap1_1Type(primary, sub)) {
   593             return (primary.equalsIgnoreCase("application")
   594                     && sub.equalsIgnoreCase("fastinfoset") ?
   595                         FI_ENCODED_FLAG : 0)
   596                    | PLAIN_XML_FLAG | SOAP1_1_FLAG;
   597         }
   598         else if (isSoap1_2Type(primary, sub)) {
   599             return (primary.equalsIgnoreCase("application")
   600                     && sub.equalsIgnoreCase("soap+fastinfoset") ?
   601                         FI_ENCODED_FLAG : 0)
   602                    | PLAIN_XML_FLAG | SOAP1_2_FLAG;
   603         } else if(isSOAPBodyXOPPackage(contentType)){
   604             return XOP_FLAG;
   605         } else {
   606             log.severe("SAAJ0537.soap.invalid.content-type");
   607             throw new SOAPExceptionImpl(
   608                 "Invalid Content-Type:"
   609                     + primary
   610                     + '/'
   611                     + sub
   612                     + ". Is this an error message instead of a SOAP response?");
   613         }
   614     }
   616     /**
   617      * Obtains the type parameter of the Content-Type header. Defaults to "text/xml".
   618      */
   619     private static String getTypeParameter(ContentType contentType) {
   620         String p = contentType.getParameter("type");
   621         if(p!=null)
   622             return p.toLowerCase();
   623         else
   624             return "text/xml";
   625     }
   627     public MimeHeaders getMimeHeaders() {
   628         return this.headers;
   629     }
   631     final static String getContentType(MimeHeaders headers) {
   632         String[] values = headers.getHeader("Content-Type");
   633         if (values == null)
   634             return null;
   635         else
   636             return values[0];
   637     }
   639     /*
   640      * Get the complete ContentType value along with optional parameters.
   641      */
   642     public String getContentType() {
   643         return getContentType(this.headers);
   644     }
   646     public void setContentType(String type) {
   647         headers.setHeader("Content-Type", type);
   648         needsSave();
   649     }
   651     private ContentType ContentType() {
   652         ContentType ct = null;
   653         try {
   654             ct = new ContentType(getContentType());
   655         } catch (Exception e) {
   656             // what to do here?
   657         }
   658         return ct;
   659     }
   661     /*
   662      * Return the MIME type string, without the parameters.
   663      */
   664     public String getBaseType() {
   665         return ContentType().getBaseType();
   666     }
   668     public void setBaseType(String type) {
   669         ContentType ct = ContentType();
   670         ct.setParameter("type", type);
   671         headers.setHeader("Content-Type", ct.toString());
   672         needsSave();
   673     }
   675     public String getAction() {
   676         return ContentType().getParameter("action");
   677     }
   679     public void setAction(String action) {
   680         ContentType ct = ContentType();
   681         ct.setParameter("action", action);
   682         headers.setHeader("Content-Type", ct.toString());
   683         needsSave();
   684     }
   686     public String getCharset() {
   687         return ContentType().getParameter("charset");
   688     }
   690     public void setCharset(String charset) {
   691         ContentType ct = ContentType();
   692         ct.setParameter("charset", charset);
   693         headers.setHeader("Content-Type", ct.toString());
   694         needsSave();
   695     }
   697     /**
   698      * All write methods (i.e setters) should call this method in
   699      * order to make sure that a save is necessary since the state
   700      * has been modified.
   701      */
   702     private final void needsSave() {
   703         saved = false;
   704     }
   706     public  boolean saveRequired() {
   707         return saved != true;
   708     }
   710     public String getContentDescription() {
   711         String[] values = headers.getHeader("Content-Description");
   712         if (values != null && values.length > 0)
   713             return values[0];
   714         return null;
   715     }
   717     public void setContentDescription(String description) {
   718         headers.setHeader("Content-Description", description);
   719         needsSave();
   720     }
   722     public abstract SOAPPart getSOAPPart();
   724     public void removeAllAttachments() {
   725         try {
   726             initializeAllAttachments();
   727         } catch (Exception e) {
   728             throw new RuntimeException(e);
   729         }
   731         if (attachments != null) {
   732             attachments.clear();
   733             needsSave();
   734         }
   735     }
   737     public int countAttachments() {
   738         try {
   739             initializeAllAttachments();
   740         } catch (Exception e) {
   741             throw new RuntimeException(e);
   742         }
   743         if (attachments != null)
   744             return attachments.size();
   745         return 0;
   746     }
   748     public void addAttachmentPart(AttachmentPart attachment) {
   749         try {
   750             initializeAllAttachments();
   751         } catch (Exception e) {
   752             throw new RuntimeException(e);
   753         }
   754         if (attachments == null)
   755             attachments = new FinalArrayList();
   757         attachments.add(attachment);
   759         needsSave();
   760     }
   762     static private final Iterator nullIter = Collections.EMPTY_LIST.iterator();
   764     public Iterator getAttachments() {
   765         try {
   766             initializeAllAttachments();
   767         } catch (Exception e) {
   768             throw new RuntimeException(e);
   769         }
   770         if (attachments == null)
   771             return nullIter;
   772         return attachments.iterator();
   773     }
   775     private class MimeMatchingIterator implements Iterator {
   776         public MimeMatchingIterator(MimeHeaders headers) {
   777             this.headers = headers;
   778             this.iter = attachments.iterator();
   779         }
   781         private Iterator iter;
   782         private MimeHeaders headers;
   783         private Object nextAttachment;
   785         public boolean hasNext() {
   786             if (nextAttachment == null)
   787                 nextAttachment = nextMatch();
   788             return nextAttachment != null;
   789         }
   791         public Object next() {
   792             if (nextAttachment != null) {
   793                 Object ret = nextAttachment;
   794                 nextAttachment = null;
   795                 return ret;
   796             }
   798             if (hasNext())
   799                 return nextAttachment;
   801             return null;
   802         }
   804         Object nextMatch() {
   805             while (iter.hasNext()) {
   806                 AttachmentPartImpl ap = (AttachmentPartImpl) iter.next();
   807                 if (ap.hasAllHeaders(headers))
   808                     return ap;
   809             }
   810             return null;
   811         }
   813         public void remove() {
   814             iter.remove();
   815         }
   816     }
   818     public Iterator getAttachments(MimeHeaders headers) {
   819         try {
   820             initializeAllAttachments();
   821         } catch (Exception e) {
   822             throw new RuntimeException(e);
   823         }
   824         if (attachments == null)
   825             return nullIter;
   827         return new MimeMatchingIterator(headers);
   828     }
   830     public void removeAttachments(MimeHeaders headers) {
   831         try {
   832             initializeAllAttachments();
   833         } catch (Exception e) {
   834             throw new RuntimeException(e);
   835         }
   836         if (attachments == null)
   837             return ;
   839         Iterator it =  new MimeMatchingIterator(headers);
   840         while (it.hasNext()) {
   841             int index = attachments.indexOf(it.next());
   842             attachments.set(index, null);
   843         }
   844         FinalArrayList f = new FinalArrayList();
   845         for (int i = 0; i < attachments.size(); i++) {
   846             if (attachments.get(i) != null) {
   847                 f.add(attachments.get(i));
   848             }
   849         }
   850         attachments = f;
   851     }
   853     public AttachmentPart createAttachmentPart() {
   854         return new AttachmentPartImpl();
   855     }
   857     public  AttachmentPart getAttachment(SOAPElement element)
   858         throws SOAPException {
   859         try {
   860             initializeAllAttachments();
   861         } catch (Exception e) {
   862             throw new RuntimeException(e);
   863         }
   864         String uri;
   865         String hrefAttr = element.getAttribute("href");
   866         if ("".equals(hrefAttr)) {
   867             Node node = getValueNodeStrict(element);
   868             String swaRef = null;
   869             if (node != null) {
   870                 swaRef = node.getValue();
   871             }
   872             if (swaRef == null || "".equals(swaRef)) {
   873                 return null;
   874             } else {
   875                 uri = swaRef;
   876             }
   877         } else {
   878             uri = hrefAttr;
   879         }
   880         return getAttachmentPart(uri);
   881     }
   883     private Node getValueNodeStrict(SOAPElement element) {
   884         Node node = (Node)element.getFirstChild();
   885         if (node != null) {
   886             if (node.getNextSibling() == null
   887                 && node.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
   888                 return node;
   889             } else {
   890                 return null;
   891             }
   892         }
   893         return null;
   894     }
   897     private AttachmentPart getAttachmentPart(String uri) throws SOAPException {
   898         AttachmentPart _part;
   899         try {
   900             if (uri.startsWith("cid:")) {
   901                 // rfc2392
   902                 uri = '<'+uri.substring("cid:".length())+'>';
   904                 MimeHeaders headersToMatch = new MimeHeaders();
   905                 headersToMatch.addHeader(CONTENT_ID, uri);
   907                 Iterator i = this.getAttachments(headersToMatch);
   908                 _part = (i == null) ? null : (AttachmentPart)i.next();
   909             } else {
   910                 // try content-location
   911                 MimeHeaders headersToMatch = new MimeHeaders();
   912                 headersToMatch.addHeader(CONTENT_LOCATION, uri);
   914                 Iterator i = this.getAttachments(headersToMatch);
   915                 _part = (i == null) ? null : (AttachmentPart)i.next();
   916             }
   918             // try  auto-generated JAXRPC CID
   919             if (_part == null) {
   920                 Iterator j = this.getAttachments();
   922                 while (j.hasNext()) {
   923                     AttachmentPart p = (AttachmentPart)j.next();
   924                     String cl = p.getContentId();
   925                     if (cl != null) {
   926                         // obtain the partname
   927                         int eqIndex = cl.indexOf("=");
   928                         if (eqIndex > -1) {
   929                             cl = cl.substring(1, eqIndex);
   930                             if (cl.equalsIgnoreCase(uri)) {
   931                                 _part = p;
   932                                  break;
   933                             }
   934                         }
   935                     }
   936                 }
   937             }
   939         } catch (Exception se) {
   940             log.log(Level.SEVERE, "SAAJ0590.soap.unable.to.locate.attachment", new Object[] {uri});
   941             throw new SOAPExceptionImpl(se);
   942         }
   943         return _part;
   944     }
   946     private final ByteInputStream getHeaderBytes()
   947         throws IOException {
   948         SOAPPartImpl sp = (SOAPPartImpl) getSOAPPart();
   949         return sp.getContentAsStream();
   950     }
   952     private String convertToSingleLine(String contentType) {
   953         StringBuffer buffer = new StringBuffer();
   954         for (int i = 0; i < contentType.length(); i ++) {
   955             char c = contentType.charAt(i);
   956             if (c != '\r' && c != '\n' && c != '\t')
   957                 buffer.append(c);
   958         }
   959         return buffer.toString();
   960     }
   962     private MimeMultipart getMimeMessage() throws SOAPException {
   963         try {
   964             SOAPPartImpl soapPart = (SOAPPartImpl) getSOAPPart();
   965             MimeBodyPart mimeSoapPart = soapPart.getMimePart();
   967             /*
   968              * Get content type from this message instead of soapPart
   969              * to ensure agreement if soapPart is transcoded (XML <-> FI)
   970              */
   971             ContentType soapPartCtype = new ContentType(getExpectedContentType());
   973             if (!isFastInfoset) {
   974                 soapPartCtype.setParameter("charset", initCharset());
   975             }
   976             mimeSoapPart.setHeader("Content-Type", soapPartCtype.toString());
   978             MimeMultipart headerAndBody = null;
   980             if (!switchOffBM && !switchOffLazyAttachment &&
   981                    (multiPart != null) && !attachmentsInitialized) {
   982                 headerAndBody = new BMMimeMultipart();
   983                 headerAndBody.addBodyPart(mimeSoapPart);
   984                 if (attachments != null) {
   985                     for (Iterator eachAttachment = attachments.iterator();
   986                          eachAttachment.hasNext();) {
   987                         headerAndBody.addBodyPart(
   988                             ((AttachmentPartImpl) eachAttachment.next())
   989                                 .getMimePart());
   990                     }
   991                 }
   992                 InputStream in = ((BMMimeMultipart)multiPart).getInputStream();
   993                 if (!((BMMimeMultipart)multiPart).lastBodyPartFound() &&
   994                     !((BMMimeMultipart)multiPart).isEndOfStream()) {
   995                     ((BMMimeMultipart)headerAndBody).setInputStream(in);
   996                     ((BMMimeMultipart)headerAndBody).setBoundary(
   997                         ((BMMimeMultipart)multiPart).getBoundary());
   998                     ((BMMimeMultipart)headerAndBody).
   999                         setLazyAttachments(lazyAttachments);
  1002             } else {
  1003                 headerAndBody = new MimeMultipart();
  1004                 headerAndBody.addBodyPart(mimeSoapPart);
  1006                 for (Iterator eachAttachement = getAttachments();
  1007                     eachAttachement.hasNext();
  1008                     ) {
  1009                     headerAndBody.addBodyPart(
  1010                         ((AttachmentPartImpl) eachAttachement.next())
  1011                             .getMimePart());
  1015             ContentType contentType = headerAndBody.getContentType();
  1017             ParameterList l = contentType.getParameterList();
  1019             // set content type depending on SOAP version
  1020             l.set("type", getExpectedContentType());
  1021             l.set("boundary", contentType.getParameter("boundary"));
  1022             ContentType nct = new ContentType("multipart", "related", l);
  1024             headers.setHeader(
  1025                 "Content-Type",
  1026                 convertToSingleLine(nct.toString()));
  1027           // TBD
  1028           //    Set content length MIME header here.
  1030             return headerAndBody;
  1031         } catch (SOAPException ex) {
  1032             throw ex;
  1033         } catch (Throwable ex) {
  1034             log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
  1035             throw new SOAPExceptionImpl(
  1036                 "Unable to convert SOAP message into "
  1037                     + "a MimeMultipart object",
  1038                 ex);
  1042     private String initCharset() {
  1044         String charset = null;
  1046         String[] cts = getMimeHeaders().getHeader("Content-Type");
  1047         if ((cts != null) && (cts[0] != null)) {
  1048             charset = getCharsetString(cts[0]);
  1051         if (charset == null) {
  1052             charset = (String) getProperty(CHARACTER_SET_ENCODING);
  1055         if (charset != null) {
  1056             return charset;
  1059         return "utf-8";
  1062     private String getCharsetString(String s) {
  1063         try {
  1064             int index = s.indexOf(";");
  1065             if(index < 0)
  1066                 return null;
  1067             ParameterList pl = new ParameterList(s.substring(index));
  1068             return pl.get("charset");
  1069         } catch(Exception e) {
  1070             return null;
  1074     public void saveChanges() throws SOAPException {
  1076         // suck in all the data from the attachments and have it
  1077         // ready for writing/sending etc.
  1079         String charset = initCharset();
  1081         /*if (countAttachments() == 0) {*/
  1082         int attachmentCount = (attachments == null) ? 0 : attachments.size();
  1083         if (attachmentCount == 0) {
  1084             if (!switchOffBM && !switchOffLazyAttachment &&
  1085                 !attachmentsInitialized && (multiPart != null)) {
  1086                 // so there might be attachments
  1087                 attachmentCount = 1;
  1091         try {
  1092             if ((attachmentCount == 0) && !hasXOPContent()) {
  1093                 ByteInputStream in;
  1094                 try{
  1095                 /*
  1096                  * Not sure why this is called getHeaderBytes(), but it actually
  1097                  * returns the whole message as a byte stream. This stream could
  1098                  * be either XML of Fast depending on the mode.
  1099                  */
  1100                     in = getHeaderBytes();
  1101                     // no attachments, hence this property can be false
  1102                     this.optimizeAttachmentProcessing = false;
  1103                 } catch (IOException ex) {
  1104                     log.severe("SAAJ0539.soap.cannot.get.header.stream");
  1105                     throw new SOAPExceptionImpl(
  1106                             "Unable to get header stream in saveChanges: ",
  1107                             ex);
  1110                 messageBytes = in.getBytes();
  1111                 messageByteCount = in.getCount();
  1113                 headers.setHeader(
  1114                         "Content-Type",
  1115                         getExpectedContentType() +
  1116                         (isFastInfoset ? "" : "; charset=" + charset));
  1117                 headers.setHeader(
  1118                     "Content-Length",
  1119                     Integer.toString(messageByteCount));
  1120             } else {
  1121                 if(hasXOPContent())
  1122                     mmp = getXOPMessage();
  1123                 else
  1124                     mmp = getMimeMessage();
  1126         } catch (Throwable ex) {
  1127             log.severe("SAAJ0540.soap.err.saving.multipart.msg");
  1128             throw new SOAPExceptionImpl(
  1129                     "Error during saving a multipart message",
  1130                     ex);
  1133         // FIX ME -- SOAP Action replaced by Content-Type optional parameter action
  1134         /*
  1135         if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
  1137             String[] soapAction = headers.getHeader("SOAPAction");
  1139             if (soapAction == null || soapAction.length == 0)
  1140                 headers.setHeader("SOAPAction", "\"\"");
  1143         */
  1145         saved = true;
  1148     private MimeMultipart getXOPMessage() throws SOAPException {
  1149         try {
  1150             MimeMultipart headerAndBody = new MimeMultipart();
  1151             SOAPPartImpl soapPart =  (SOAPPartImpl)getSOAPPart();
  1152             MimeBodyPart mimeSoapPart = soapPart.getMimePart();
  1153             ContentType soapPartCtype =
  1154                 new ContentType("application/xop+xml");
  1155             soapPartCtype.setParameter("type", getExpectedContentType());
  1156             String charset = initCharset();
  1157             soapPartCtype.setParameter("charset", charset);
  1158             mimeSoapPart.setHeader("Content-Type", soapPartCtype.toString());
  1159             headerAndBody.addBodyPart(mimeSoapPart);
  1161             for (Iterator eachAttachement = getAttachments();
  1162                 eachAttachement.hasNext();
  1163                 ) {
  1164                 headerAndBody.addBodyPart(
  1165                     ((AttachmentPartImpl) eachAttachement.next())
  1166                         .getMimePart());
  1169             ContentType contentType = headerAndBody.getContentType();
  1171             ParameterList l = contentType.getParameterList();
  1173             //lets not write start-info for now till we get servlet fix done
  1174             l.set("start-info", getExpectedContentType());//+";charset="+initCharset());
  1176             // set content type depending on SOAP version
  1177             l.set("type", "application/xop+xml");
  1179             if (isCorrectSoapVersion(SOAP1_2_FLAG)) {
  1180                  String action = getAction();
  1181                  if(action != null)
  1182                      l.set("action", action);
  1185             l.set("boundary", contentType.getParameter("boundary"));
  1186             ContentType nct = new ContentType("Multipart", "Related", l);
  1187             headers.setHeader(
  1188                 "Content-Type",
  1189                 convertToSingleLine(nct.toString()));
  1190             // TBD
  1191             //    Set content length MIME header here.
  1193             return headerAndBody;
  1194         } catch (SOAPException ex) {
  1195             throw ex;
  1196         } catch (Throwable ex) {
  1197             log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
  1198             throw new SOAPExceptionImpl(
  1199                 "Unable to convert SOAP message into "
  1200                     + "a MimeMultipart object",
  1201                 ex);
  1206     private boolean hasXOPContent() throws ParseException {
  1207         String type = getContentType();
  1208         if(type == null)
  1209             return false;
  1210         ContentType ct = new ContentType(type);
  1211         return isMimeMultipartXOPPackage(ct) || isSOAPBodyXOPPackage(ct);
  1214     public void writeTo(OutputStream out) throws SOAPException, IOException {
  1215         if (saveRequired()){
  1216             this.optimizeAttachmentProcessing = true;
  1217             saveChanges();
  1220         if(!optimizeAttachmentProcessing){
  1221             out.write(messageBytes, 0, messageByteCount);
  1223         else{
  1224             try{
  1225                 if(hasXOPContent()){
  1226                     mmp.writeTo(out);
  1227                 }else{
  1228                     mmp.writeTo(out);
  1229                     if (!switchOffBM && !switchOffLazyAttachment &&
  1230                             (multiPart != null) && !attachmentsInitialized) {
  1231                         ((BMMimeMultipart)multiPart).setInputStream(
  1232                                 ((BMMimeMultipart)mmp).getInputStream());
  1235             } catch(Exception ex){
  1236                 log.severe("SAAJ0540.soap.err.saving.multipart.msg");
  1237                 throw new SOAPExceptionImpl(
  1238                         "Error during saving a multipart message",
  1239                         ex);
  1243         if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
  1245             String[] soapAction = headers.getHeader("SOAPAction");
  1247             if (soapAction == null || soapAction.length == 0)
  1248                 headers.setHeader("SOAPAction", "\"\"");
  1252         messageBytes = null;
  1253         needsSave();
  1256     public SOAPBody getSOAPBody() throws SOAPException {
  1257         SOAPBody body = getSOAPPart().getEnvelope().getBody();
  1258         /*if (body == null) {
  1259              throw new SOAPException("No SOAP Body was found in the SOAP Message");
  1260         }*/
  1261         return body;
  1264     public SOAPHeader getSOAPHeader() throws SOAPException {
  1265         SOAPHeader hdr = getSOAPPart().getEnvelope().getHeader();
  1266         /*if (hdr == null) {
  1267             throw new SOAPException("No SOAP Header was found in the SOAP Message");
  1268         }*/
  1269         return hdr;
  1272     private void initializeAllAttachments ()
  1273         throws MessagingException, SOAPException {
  1274         if (switchOffBM || switchOffLazyAttachment) {
  1275             return;
  1278         if (attachmentsInitialized || (multiPart == null)) {
  1279             return;
  1282         if (attachments == null)
  1283             attachments = new FinalArrayList();
  1285         int count = multiPart.getCount();
  1286         for (int i=0; i < count; i++ ) {
  1287             initializeAttachment(multiPart.getBodyPart(i));
  1289         attachmentsInitialized = true;
  1290         //multiPart = null;
  1291         needsSave();
  1294     private void initializeAttachment(MimeBodyPart mbp) throws SOAPException {
  1295         AttachmentPartImpl attachmentPart = new AttachmentPartImpl();
  1296         DataHandler attachmentHandler = mbp.getDataHandler();
  1297         attachmentPart.setDataHandler(attachmentHandler);
  1299         AttachmentPartImpl.copyMimeHeaders(mbp, attachmentPart);
  1300         attachments.add(attachmentPart);
  1303     private void initializeAttachment(MimeMultipart multiPart, int i)
  1304         throws Exception {
  1305         MimeBodyPart currentBodyPart = multiPart.getBodyPart(i);
  1306         AttachmentPartImpl attachmentPart = new AttachmentPartImpl();
  1308         DataHandler attachmentHandler = currentBodyPart.getDataHandler();
  1309         attachmentPart.setDataHandler(attachmentHandler);
  1311         AttachmentPartImpl.copyMimeHeaders(currentBodyPart, attachmentPart);
  1312         addAttachmentPart(attachmentPart);
  1315     private void setMimeHeaders(SOAPPart soapPart,
  1316             MimeBodyPart soapMessagePart) throws Exception {
  1318         // first remove the existing content-type
  1319         soapPart.removeAllMimeHeaders();
  1320         // add everything present in soapMessagePart
  1321         List headers = soapMessagePart.getAllHeaders();
  1322         int sz = headers.size();
  1323         for( int i=0; i<sz; i++ ) {
  1324             Header h = (Header) headers.get(i);
  1325             soapPart.addMimeHeader(h.getName(), h.getValue());
  1329     private void initCharsetProperty(ContentType contentType) {
  1330         String charset = contentType.getParameter("charset");
  1331         if (charset != null) {
  1332             ((SOAPPartImpl) getSOAPPart()).setSourceCharsetEncoding(charset);
  1333             if(!charset.equalsIgnoreCase("utf-8"))
  1334                 setProperty(CHARACTER_SET_ENCODING, charset);
  1338     public void setLazyAttachments(boolean flag) {
  1339         lazyAttachments = flag;

mercurial