< prev index next >

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

Print this page


   1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  78     protected static final int MIME_MULTIPART_XOP_SOAP1_1_FLAG = 6;  // 00110
  79     protected static final int MIME_MULTIPART_XOP_SOAP1_2_FLAG = 10; // 01010
  80     protected static final int XOP_FLAG = 13;                // 01101
  81     protected static final int FI_ENCODED_FLAG     = 16;     // 10000
  82 
  83     protected MimeHeaders headers;
  84     protected ContentType contentType;
  85     protected SOAPPartImpl soapPartImpl;
  86     protected FinalArrayList<AttachmentPart> attachments;
  87     protected boolean saved = false;
  88     protected byte[] messageBytes;
  89     protected int messageByteCount;
  90     protected HashMap properties = new HashMap();
  91 
  92     // used for lazy attachment initialization
  93     protected MimeMultipart multiPart = null;
  94     protected boolean attachmentsInitialized = false;
  95 
  96     /**
  97      * True if this part is encoded using Fast Infoset.
  98      * MIME -> application/fastinfoset
  99      */
 100     protected boolean isFastInfoset = false;
 101 
 102     /**
 103      * True if the Accept header of this message includes
 104      * application/fastinfoset
 105      */
 106     protected boolean acceptFastInfoset = false;
 107 
 108     protected MimeMultipart mmp = null;
 109 
 110     // if attachments are present, don't read the entire message in byte stream in saveTo()
 111     private boolean optimizeAttachmentProcessing = true;
 112 
 113     private InputStream inputStreamAfterSaveChanges = null;
 114 
 115     public static final String LAZY_SOAP_BODY_PARSING = "saaj.lazy.soap.body";
 116 
 117     // switch back to old MimeMultipart incase of problem
 118     private static boolean switchOffBM = false;


 185      * @param type
 186      *      must be all lower case
 187      */
 188     private static boolean isEqualToSoap1_2Type(String type) {
 189         return type.startsWith("application/soap+xml") ||
 190                type.startsWith("application/soap+fastinfoset");
 191     }
 192 
 193     /**
 194       * Construct a new message. This will be invoked before message
 195       * sends.
 196       */
 197     protected MessageImpl() {
 198         this(false, false);
 199         attachmentsInitialized = true;
 200     }
 201 
 202     /**
 203       * Construct a new message. This will be invoked before message
 204       * sends.



 205       */
 206     protected MessageImpl(boolean isFastInfoset, boolean acceptFastInfoset) {
 207         this.isFastInfoset = isFastInfoset;
 208         this.acceptFastInfoset = acceptFastInfoset;
 209 
 210         headers = new MimeHeaders();
 211         headers.setHeader("Accept", getExpectedAcceptHeader());
 212         contentType = new ContentType();
 213     }
 214 
 215     /**
 216      * Shallow copy.


 217      */
 218     protected MessageImpl(SOAPMessage msg) {
 219         if (!(msg instanceof MessageImpl)) {
 220             // don't know how to handle this.
 221         }
 222         MessageImpl src = (MessageImpl) msg;
 223         this.headers = src.headers;
 224         this.soapPartImpl = src.soapPartImpl;
 225         this.attachments = src.attachments;
 226         this.saved = src.saved;
 227         this.messageBytes = src.messageBytes;
 228         this.messageByteCount = src.messageByteCount;
 229         this.properties = src.properties;
 230         this.contentType = src.contentType;
 231     }
 232 
 233     /**
 234      * @param stat
 235      *      the mask value obtained from {@link #identifyContentType(ContentType)}

 236      */
 237     protected static boolean isSoap1_1Content(int stat) {
 238         return (stat & SOAP1_1_FLAG) != 0;
 239     }
 240 
 241     /**

 242      * @param stat
 243      *      the mask value obtained from {@link #identifyContentType(ContentType)}

 244      */
 245     protected static boolean isSoap1_2Content(int stat) {
 246         return (stat & SOAP1_2_FLAG) != 0;
 247     }
 248 
 249      private static boolean isMimeMultipartXOPSoap1_2Package(ContentType contentType) {
 250         String type = contentType.getParameter("type");
 251         if (type == null) {
 252             return false;
 253         }
 254         type = type.toLowerCase();
 255         if (!type.startsWith("application/xop+xml")) {
 256             return false;
 257         }
 258         String startinfo = contentType.getParameter("start-info");
 259         if (startinfo == null) {
 260             return false;
 261         }
 262         startinfo = startinfo.toLowerCase();
 263         return isEqualToSoap1_2Type(startinfo);


 281         return isEqualToSoap1_1Type(startinfo);
 282     }
 283 
 284     private static boolean isSOAPBodyXOPPackage(ContentType contentType){
 285         String primary = contentType.getPrimaryType();
 286         String sub = contentType.getSubType();
 287 
 288         if (primary.equalsIgnoreCase("application")) {
 289             if (sub.equalsIgnoreCase("xop+xml")) {
 290                 String type = getTypeParameter(contentType);
 291                 return isEqualToSoap1_2Type(type) || isEqualToSoap1_1Type(type);
 292             }
 293         }
 294         return false;
 295     }
 296 
 297     /**
 298      * Construct a message from an input stream. When messages are
 299      * received, there's two parts -- the transport headers and the
 300      * message content in a transport specific stream.



 301      */
 302     protected MessageImpl(MimeHeaders headers, final InputStream in)
 303         throws SOAPExceptionImpl {
 304         contentType = parseContentType(headers);
 305         init(headers,identifyContentType(contentType),contentType,in);
 306     }
 307 
 308     private static ContentType parseContentType(MimeHeaders headers) throws SOAPExceptionImpl {
 309         final String ct;
 310         if (headers != null)
 311             ct = getContentType(headers);
 312         else {
 313             log.severe("SAAJ0550.soap.null.headers");
 314             throw new SOAPExceptionImpl("Cannot create message: " +
 315                                         "Headers can't be null");
 316         }
 317 
 318         if (ct == null) {
 319             log.severe("SAAJ0532.soap.no.Content-Type");
 320             throw new SOAPExceptionImpl("Absent Content-Type");
 321         }
 322         try {
 323             return new ContentType(ct);
 324         } catch (Throwable ex) {
 325             log.severe("SAAJ0535.soap.cannot.internalize.message");
 326             throw new SOAPExceptionImpl("Unable to internalize message", ex);
 327         }
 328     }
 329 
 330     /**
 331      * Construct a message from an input stream. When messages are
 332      * received, there's two parts -- the transport headers and the
 333      * message content in a transport specific stream.
 334      *

 335      * @param contentType
 336      *      The parsed content type header from the headers variable.
 337      *      This is redundant parameter, but it avoids reparsing this header again.
 338      * @param stat
 339      *      The result of {@link #identifyContentType(ContentType)} over
 340      *      the contentType parameter. This redundant parameter, but it avoids
 341      *      recomputing this information again.


 342      */
 343     protected MessageImpl(MimeHeaders headers, final ContentType contentType, int stat, final InputStream in) throws SOAPExceptionImpl {
 344         init(headers, stat, contentType, in);
 345 
 346     }
 347 
 348     public MessageImpl(MimeHeaders headers, ContentType ct, int stat,
 349             XMLStreamReader reader) throws SOAPExceptionImpl {
 350         init(headers, stat, ct, reader);
 351     }
 352 
 353     private void init(MimeHeaders headers, int stat, final ContentType contentType, final Object input) throws SOAPExceptionImpl {
 354         this.headers = headers;
 355 
 356         try {
 357 
 358             // Set isFastInfoset/acceptFastInfoset flag based on MIME type
 359             if ((stat & FI_ENCODED_FLAG) > 0) {
 360                 isFastInfoset = acceptFastInfoset = true;
 361             }


 408                         initCharsetProperty(contentType);
 409                         getSOAPPart().setContent(new StreamSource(in));
 410                     }
 411                 } else {
 412                     //is a StAX reader
 413                     if (isFastInfoset) {
 414                         //need to get FI stax reader
 415                     } else {
 416                         initCharsetProperty(contentType);
 417                         getSOAPPart().setContent(new StAXSource(rdr));
 418                     }
 419                 }
 420             }
 421             else if ((stat & MIME_MULTIPART_FLAG) != 0 && in == null) {
 422                 //only parse multipart in the inputstream case
 423                 //in stax reader case, we would be given the attachments separately
 424                 getSOAPPart().setContent(new StAXSource(rdr));
 425             } else if ((stat & MIME_MULTIPART_FLAG) != 0) {
 426                 final InputStream finalIn = in;
 427                 DataSource ds = new DataSource() {

 428                     public InputStream getInputStream() {
 429                         return finalIn;
 430                     }
 431 

 432                     public OutputStream getOutputStream() {
 433                         return null;
 434                     }
 435 

 436                     public String getContentType() {
 437                         return contentType.toString();
 438                     }
 439 

 440                     public String getName() {
 441                         return "";
 442                     }
 443                 };
 444 
 445                 multiPart = null;
 446                 if (useMimePull) {
 447                     multiPart = new MimePullMultipart(ds,contentType);
 448                 } else if (switchOffBM) {
 449                     multiPart = new MimeMultipart(ds,contentType);
 450                 } else {
 451                     multiPart = new BMMimeMultipart(ds,contentType);
 452                 }
 453 
 454                 String startParam = contentType.getParameter("start");
 455                 MimeBodyPart soapMessagePart = null;
 456                 InputStream soapPartInputStream = null;
 457                 String contentID = null;
 458                 String contentIDNoAngle = null;
 459                 if (switchOffBM || switchOffLazyAttachment) {


 574 
 575     public void setIsFastInfoset(boolean value) {
 576         if (value != isFastInfoset) {
 577             isFastInfoset = value;
 578             if (isFastInfoset) {
 579                 acceptFastInfoset = true;
 580             }
 581             saved = false;      // ensure transcoding if necessary
 582         }
 583     }
 584 
 585     public boolean isLazySoapBodyParsing() {
 586         Object lazyParsingProp = getProperty(LAZY_SOAP_BODY_PARSING);
 587         if (lazyParsingProp == null) return false;
 588         if (lazyParsingProp instanceof Boolean) {
 589             return ((Boolean) lazyParsingProp).booleanValue();
 590         } else {
 591             return Boolean.valueOf(lazyParsingProp.toString());
 592         }
 593     }

 594     public Object getProperty(String property) {
 595         return (String) properties.get(property);
 596     }
 597 

 598     public void setProperty(String property, Object value) {
 599         verify(property, value);
 600         properties.put(property, value);
 601     }
 602 
 603     private void verify(String property, Object value) {
 604         if (property.equalsIgnoreCase(SOAPMessage.WRITE_XML_DECLARATION)) {
 605             if (!("true".equals(value) || "false".equals(value)))
 606                 throw new RuntimeException(
 607                     property + " must have value false or true");
 608 
 609             try {
 610                 EnvelopeImpl env = (EnvelopeImpl) getSOAPPart().getEnvelope();
 611                 if ("true".equalsIgnoreCase((String)value)) {
 612                     env.setOmitXmlDecl("no");
 613                 } else if ("false".equalsIgnoreCase((String)value)) {
 614                     env.setOmitXmlDecl("yes");
 615                 }
 616             } catch (Exception e) {
 617                 log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property",


 705             throw new SOAPExceptionImpl(
 706                 "Invalid Content-Type:"
 707                     + primary
 708                     + '/'
 709                     + sub
 710                     + ". Is this an error message instead of a SOAP response?");
 711         }
 712     }
 713 
 714     /**
 715      * Obtains the type parameter of the Content-Type header. Defaults to "text/xml".
 716      */
 717     private static String getTypeParameter(ContentType contentType) {
 718         String p = contentType.getParameter("type");
 719         if(p!=null)
 720             return p.toLowerCase();
 721         else
 722             return "text/xml";
 723     }
 724 

 725     public MimeHeaders getMimeHeaders() {
 726         return this.headers;
 727     }
 728 
 729     final static String getContentType(MimeHeaders headers) {
 730         String[] values = headers.getHeader("Content-Type");
 731         if (values == null)
 732             return null;
 733         else
 734             return values[0];
 735     }
 736 
 737     /*
 738      * Get the complete ContentType value along with optional parameters.
 739      */
 740     public String getContentType() {
 741         return getContentType(this.headers);
 742     }
 743 
 744     public void setContentType(String type) {


 788     public String getCharset() {
 789         return contentType().getParameter("charset");
 790     }
 791 
 792     public void setCharset(String charset) {
 793         ContentType ct = contentType();
 794         ct.setParameter("charset", charset);
 795         headers.setHeader("Content-Type", ct.toString());
 796         needsSave();
 797     }
 798 
 799     /**
 800      * All write methods (i.e setters) should call this method in
 801      * order to make sure that a save is necessary since the state
 802      * has been modified.
 803      */
 804     private final void needsSave() {
 805         saved = false;
 806     }
 807 

 808     public  boolean saveRequired() {
 809         return saved != true;
 810     }
 811 

 812     public String getContentDescription() {
 813         String[] values = headers.getHeader("Content-Description");
 814         if (values != null && values.length > 0)
 815             return values[0];
 816         return null;
 817     }
 818 

 819     public void setContentDescription(String description) {
 820         headers.setHeader("Content-Description", description);
 821         needsSave();
 822     }
 823 

 824     public abstract SOAPPart getSOAPPart();
 825 

 826     public void removeAllAttachments() {
 827         try {
 828             initializeAllAttachments();
 829         } catch (Exception e) {
 830             throw new RuntimeException(e);
 831         }
 832 
 833         if (attachments != null) {
 834             attachments.clear();
 835             needsSave();
 836         }
 837     }
 838 

 839     public int countAttachments() {
 840         try {
 841             initializeAllAttachments();
 842         } catch (Exception e) {
 843             throw new RuntimeException(e);
 844         }
 845         if (attachments != null)
 846             return attachments.size();
 847         return 0;
 848     }
 849 

 850     public void addAttachmentPart(AttachmentPart attachment) {
 851         try {
 852             initializeAllAttachments();
 853             this.optimizeAttachmentProcessing = true;
 854         } catch (Exception e) {
 855             throw new RuntimeException(e);
 856         }
 857         if (attachments == null)
 858             attachments = new FinalArrayList<AttachmentPart>();
 859 
 860         attachments.add(attachment);
 861 
 862         needsSave();
 863     }
 864 
 865     static private final Iterator nullIter = Collections.EMPTY_LIST.iterator();
 866 

 867     public Iterator getAttachments() {
 868         try {
 869             initializeAllAttachments();
 870         } catch (Exception e) {
 871             throw new RuntimeException(e);
 872         }
 873         if (attachments == null)
 874             return nullIter;
 875         return attachments.iterator();
 876     }
 877 
 878     private void setFinalContentType(String charset) {
 879         ContentType ct = contentType();
 880         if (ct == null) {
 881             ct = new ContentType();
 882         }
 883         String[] split = getExpectedContentType().split("/");
 884         ct.setPrimaryType(split[0]);
 885         ct.setSubType(split[1]);
 886         ct.setParameter("charset", charset);
 887         headers.setHeader("Content-Type", ct.toString());
 888     }
 889 
 890     private class MimeMatchingIterator implements Iterator<AttachmentPart> {
 891         public MimeMatchingIterator(MimeHeaders headers) {
 892             this.headers = headers;
 893             this.iter = attachments.iterator();
 894         }
 895 
 896         private Iterator<AttachmentPart> iter;
 897         private MimeHeaders headers;
 898         private AttachmentPart nextAttachment;
 899 

 900         public boolean hasNext() {
 901             if (nextAttachment == null)
 902                 nextAttachment = nextMatch();
 903             return nextAttachment != null;
 904         }
 905 

 906         public AttachmentPart next() {
 907             if (nextAttachment != null) {
 908                 AttachmentPart ret = nextAttachment;
 909                 nextAttachment = null;
 910                 return ret;
 911             }
 912 
 913             if (hasNext())
 914                 return nextAttachment;
 915 
 916             return null;
 917         }
 918 
 919         AttachmentPart nextMatch() {
 920             while (iter.hasNext()) {
 921                 AttachmentPartImpl ap = (AttachmentPartImpl) iter.next();
 922                 if (ap.hasAllHeaders(headers))
 923                     return ap;
 924             }
 925             return null;
 926         }
 927 

 928         public void remove() {
 929             iter.remove();
 930         }
 931     }
 932 

 933     public Iterator getAttachments(MimeHeaders headers) {
 934         try {
 935             initializeAllAttachments();
 936         } catch (Exception e) {
 937             throw new RuntimeException(e);
 938         }
 939         if (attachments == null)
 940             return nullIter;
 941 
 942         return new MimeMatchingIterator(headers);
 943     }
 944 

 945     public void removeAttachments(MimeHeaders headers) {
 946         try {
 947             initializeAllAttachments();
 948         } catch (Exception e) {
 949             throw new RuntimeException(e);
 950         }
 951         if (attachments == null)
 952             return ;
 953 
 954         Iterator<AttachmentPart> it =  new MimeMatchingIterator(headers);
 955         while (it.hasNext()) {
 956             int index = attachments.indexOf(it.next());
 957             attachments.set(index, null);
 958         }
 959         FinalArrayList<AttachmentPart> f = new FinalArrayList<AttachmentPart>();
 960         for (int i = 0; i < attachments.size(); i++) {
 961             if (attachments.get(i) != null) {
 962                 f.add(attachments.get(i));
 963             }
 964         }
 965         attachments = f;
 966        // needsSave();
 967     }
 968 

 969     public AttachmentPart createAttachmentPart() {
 970         return new AttachmentPartImpl();
 971     }
 972 

 973     public  AttachmentPart getAttachment(SOAPElement element)
 974         throws SOAPException {
 975         try {
 976             initializeAllAttachments();
 977         } catch (Exception e) {
 978             throw new RuntimeException(e);
 979         }
 980         String uri;
 981         String hrefAttr = element.getAttribute("href");
 982         if ("".equals(hrefAttr)) {
 983             Node node = getValueNodeStrict(element);
 984             String swaRef = null;
 985             if (node != null) {
 986                 swaRef = node.getValue();
 987             }
 988             if (swaRef == null || "".equals(swaRef)) {
 989                 return null;
 990             } else {
 991                 uri = swaRef;
 992             }


1170 
1171         if (charset != null) {
1172             return charset;
1173         }
1174 
1175         return "utf-8";
1176     }
1177 
1178     private String getCharsetString(String s) {
1179         try {
1180             int index = s.indexOf(";");
1181             if(index < 0)
1182                 return null;
1183             ParameterList pl = new ParameterList(s.substring(index));
1184             return pl.get("charset");
1185         } catch(Exception e) {
1186             return null;
1187         }
1188     }
1189 

1190     public void saveChanges() throws SOAPException {
1191 
1192         // suck in all the data from the attachments and have it
1193         // ready for writing/sending etc.
1194 
1195         String charset = initCharset();
1196 
1197         /*if (countAttachments() == 0) {*/
1198         int attachmentCount = (attachments == null) ? 0 : attachments.size();
1199         if (attachmentCount == 0) {
1200             if (!switchOffBM && !switchOffLazyAttachment &&
1201                 !attachmentsInitialized && (multiPart != null)) {
1202                 // so there might be attachments
1203                 attachmentCount = 1;
1204             }
1205         }
1206 
1207         try {
1208             if ((attachmentCount == 0) && !hasXOPContent()) {
1209                 InputStream in;


1323             log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
1324             throw new SOAPExceptionImpl(
1325                 "Unable to convert SOAP message into "
1326                     + "a MimeMultipart object",
1327                 ex);
1328         }
1329 
1330     }
1331 
1332     private boolean hasXOPContent() throws ParseException {
1333         String type = getContentType();
1334         if(type == null)
1335             return false;
1336         ContentType ct = new ContentType(type);
1337         //return isMimeMultipartXOPPackage(ct) || isSOAPBodyXOPPackage(ct);
1338         return isMimeMultipartXOPSoap1_1Package(ct) ||
1339             isMimeMultipartXOPSoap1_2Package(ct) || isSOAPBodyXOPPackage(ct);
1340 
1341     }
1342 

1343     public void writeTo(OutputStream out) throws SOAPException, IOException {
1344         if (saveRequired()){
1345             this.optimizeAttachmentProcessing = true;
1346             saveChanges();
1347         }
1348 
1349         if(!optimizeAttachmentProcessing){
1350             if (SOAPPartImpl.lazyContentLength && messageByteCount <= 0) {
1351                 byte[] buf = new byte[1024];
1352 
1353                 int length = 0;
1354                 while( (length = inputStreamAfterSaveChanges.read(buf)) != -1) {
1355                     out.write(buf,0, length);
1356                     messageByteCount += length;
1357                 }
1358                 if (messageByteCount > 0) {
1359                     headers.setHeader(
1360                             "Content-Length",
1361                             Integer.toString(messageByteCount));
1362                 }


1380                 log.severe("SAAJ0540.soap.err.saving.multipart.msg");
1381                 throw new SOAPExceptionImpl(
1382                         "Error during saving a multipart message",
1383                         ex);
1384             }
1385         }
1386 
1387         if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
1388 
1389             String[] soapAction = headers.getHeader("SOAPAction");
1390 
1391             if (soapAction == null || soapAction.length == 0)
1392                 headers.setHeader("SOAPAction", "\"\"");
1393 
1394         }
1395 
1396         messageBytes = null;
1397         needsSave();
1398     }
1399 

1400     public SOAPBody getSOAPBody() throws SOAPException {
1401         SOAPBody body = getSOAPPart().getEnvelope().getBody();
1402         /*if (body == null) {
1403              throw new SOAPException("No SOAP Body was found in the SOAP Message");
1404         }*/
1405         return body;
1406     }
1407 

1408     public SOAPHeader getSOAPHeader() throws SOAPException {
1409         SOAPHeader hdr = getSOAPPart().getEnvelope().getHeader();
1410         /*if (hdr == null) {
1411             throw new SOAPException("No SOAP Header was found in the SOAP Message");
1412         }*/
1413         return hdr;
1414     }
1415 
1416     private void initializeAllAttachments ()
1417         throws MessagingException, SOAPException {
1418         if (switchOffBM || switchOffLazyAttachment) {
1419             return;
1420         }
1421 
1422         if (attachmentsInitialized || (multiPart == null)) {
1423             return;
1424         }
1425 
1426         if (attachments == null)
1427             attachments = new FinalArrayList<AttachmentPart>();


   1 /*
   2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  78     protected static final int MIME_MULTIPART_XOP_SOAP1_1_FLAG = 6;  // 00110
  79     protected static final int MIME_MULTIPART_XOP_SOAP1_2_FLAG = 10; // 01010
  80     protected static final int XOP_FLAG = 13;                // 01101
  81     protected static final int FI_ENCODED_FLAG     = 16;     // 10000
  82 
  83     protected MimeHeaders headers;
  84     protected ContentType contentType;
  85     protected SOAPPartImpl soapPartImpl;
  86     protected FinalArrayList<AttachmentPart> attachments;
  87     protected boolean saved = false;
  88     protected byte[] messageBytes;
  89     protected int messageByteCount;
  90     protected HashMap properties = new HashMap();
  91 
  92     // used for lazy attachment initialization
  93     protected MimeMultipart multiPart = null;
  94     protected boolean attachmentsInitialized = false;
  95 
  96     /**
  97      * True if this part is encoded using Fast Infoset.
  98      * MIME -&gt; application/fastinfoset
  99      */
 100     protected boolean isFastInfoset = false;
 101 
 102     /**
 103      * True if the Accept header of this message includes
 104      * application/fastinfoset
 105      */
 106     protected boolean acceptFastInfoset = false;
 107 
 108     protected MimeMultipart mmp = null;
 109 
 110     // if attachments are present, don't read the entire message in byte stream in saveTo()
 111     private boolean optimizeAttachmentProcessing = true;
 112 
 113     private InputStream inputStreamAfterSaveChanges = null;
 114 
 115     public static final String LAZY_SOAP_BODY_PARSING = "saaj.lazy.soap.body";
 116 
 117     // switch back to old MimeMultipart incase of problem
 118     private static boolean switchOffBM = false;


 185      * @param type
 186      *      must be all lower case
 187      */
 188     private static boolean isEqualToSoap1_2Type(String type) {
 189         return type.startsWith("application/soap+xml") ||
 190                type.startsWith("application/soap+fastinfoset");
 191     }
 192 
 193     /**
 194       * Construct a new message. This will be invoked before message
 195       * sends.
 196       */
 197     protected MessageImpl() {
 198         this(false, false);
 199         attachmentsInitialized = true;
 200     }
 201 
 202     /**
 203       * Construct a new message. This will be invoked before message
 204       * sends.
 205       *
 206       * @param isFastInfoset whether it is fast infoset
 207       * @param acceptFastInfoset whether to accept fast infoset
 208       */
 209     protected MessageImpl(boolean isFastInfoset, boolean acceptFastInfoset) {
 210         this.isFastInfoset = isFastInfoset;
 211         this.acceptFastInfoset = acceptFastInfoset;
 212 
 213         headers = new MimeHeaders();
 214         headers.setHeader("Accept", getExpectedAcceptHeader());
 215         contentType = new ContentType();
 216     }
 217 
 218     /**
 219      * Shallow copy.
 220      *
 221      * @param msg SoapMessage
 222      */
 223     protected MessageImpl(SOAPMessage msg) {
 224         if (!(msg instanceof MessageImpl)) {
 225             // don't know how to handle this.
 226         }
 227         MessageImpl src = (MessageImpl) msg;
 228         this.headers = src.headers;
 229         this.soapPartImpl = src.soapPartImpl;
 230         this.attachments = src.attachments;
 231         this.saved = src.saved;
 232         this.messageBytes = src.messageBytes;
 233         this.messageByteCount = src.messageByteCount;
 234         this.properties = src.properties;
 235         this.contentType = src.contentType;
 236     }
 237 
 238     /**
 239      * @param stat
 240      *      the mask value obtained from {@link #identifyContentType(ContentType)}
 241      * @return true if SOAP 1.1 Content
 242      */
 243     protected static boolean isSoap1_1Content(int stat) {
 244         return (stat & SOAP1_1_FLAG) != 0;
 245     }
 246 
 247     /**
 248      * Check whether it is SOAP 1.2 content.
 249      * @param stat
 250      *      the mask value obtained from {@link #identifyContentType(ContentType)}
 251      * @return true if it is SOAP 1.2 content
 252      */
 253     protected static boolean isSoap1_2Content(int stat) {
 254         return (stat & SOAP1_2_FLAG) != 0;
 255     }
 256 
 257      private static boolean isMimeMultipartXOPSoap1_2Package(ContentType contentType) {
 258         String type = contentType.getParameter("type");
 259         if (type == null) {
 260             return false;
 261         }
 262         type = type.toLowerCase();
 263         if (!type.startsWith("application/xop+xml")) {
 264             return false;
 265         }
 266         String startinfo = contentType.getParameter("start-info");
 267         if (startinfo == null) {
 268             return false;
 269         }
 270         startinfo = startinfo.toLowerCase();
 271         return isEqualToSoap1_2Type(startinfo);


 289         return isEqualToSoap1_1Type(startinfo);
 290     }
 291 
 292     private static boolean isSOAPBodyXOPPackage(ContentType contentType){
 293         String primary = contentType.getPrimaryType();
 294         String sub = contentType.getSubType();
 295 
 296         if (primary.equalsIgnoreCase("application")) {
 297             if (sub.equalsIgnoreCase("xop+xml")) {
 298                 String type = getTypeParameter(contentType);
 299                 return isEqualToSoap1_2Type(type) || isEqualToSoap1_1Type(type);
 300             }
 301         }
 302         return false;
 303     }
 304 
 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      * @param headers MimeHeaders
 310      * @param in InputStream
 311      * @exception SOAPExceptionImpl in case of I/O error
 312      */
 313     protected MessageImpl(MimeHeaders headers, final InputStream in)
 314         throws SOAPExceptionImpl {
 315         contentType = parseContentType(headers);
 316         init(headers,identifyContentType(contentType),contentType,in);
 317     }
 318 
 319     private static ContentType parseContentType(MimeHeaders headers) throws SOAPExceptionImpl {
 320         final String ct;
 321         if (headers != null)
 322             ct = getContentType(headers);
 323         else {
 324             log.severe("SAAJ0550.soap.null.headers");
 325             throw new SOAPExceptionImpl("Cannot create message: " +
 326                                         "Headers can't be null");
 327         }
 328 
 329         if (ct == null) {
 330             log.severe("SAAJ0532.soap.no.Content-Type");
 331             throw new SOAPExceptionImpl("Absent Content-Type");
 332         }
 333         try {
 334             return new ContentType(ct);
 335         } catch (Throwable ex) {
 336             log.severe("SAAJ0535.soap.cannot.internalize.message");
 337             throw new SOAPExceptionImpl("Unable to internalize message", ex);
 338         }
 339     }
 340 
 341     /**
 342      * Construct a message from an input stream. When messages are
 343      * received, there's two parts -- the transport headers and the
 344      * message content in a transport specific stream.
 345      *
 346      * @param headers headers
 347      * @param contentType
 348      *      The parsed content type header from the headers variable.
 349      *      This is redundant parameter, but it avoids reparsing this header again.
 350      * @param stat
 351      *      The result of {@link #identifyContentType(ContentType)} over
 352      *      the contentType parameter. This redundant parameter, but it avoids
 353      *      recomputing this information again.
 354      * @param in input stream
 355      * @exception SOAPExceptionImpl in case of an error
 356      */
 357     protected MessageImpl(MimeHeaders headers, final ContentType contentType, int stat, final InputStream in) throws SOAPExceptionImpl {
 358         init(headers, stat, contentType, in);
 359 
 360     }
 361 
 362     public MessageImpl(MimeHeaders headers, ContentType ct, int stat,
 363             XMLStreamReader reader) throws SOAPExceptionImpl {
 364         init(headers, stat, ct, reader);
 365     }
 366 
 367     private void init(MimeHeaders headers, int stat, final ContentType contentType, final Object input) throws SOAPExceptionImpl {
 368         this.headers = headers;
 369 
 370         try {
 371 
 372             // Set isFastInfoset/acceptFastInfoset flag based on MIME type
 373             if ((stat & FI_ENCODED_FLAG) > 0) {
 374                 isFastInfoset = acceptFastInfoset = true;
 375             }


 422                         initCharsetProperty(contentType);
 423                         getSOAPPart().setContent(new StreamSource(in));
 424                     }
 425                 } else {
 426                     //is a StAX reader
 427                     if (isFastInfoset) {
 428                         //need to get FI stax reader
 429                     } else {
 430                         initCharsetProperty(contentType);
 431                         getSOAPPart().setContent(new StAXSource(rdr));
 432                     }
 433                 }
 434             }
 435             else if ((stat & MIME_MULTIPART_FLAG) != 0 && in == null) {
 436                 //only parse multipart in the inputstream case
 437                 //in stax reader case, we would be given the attachments separately
 438                 getSOAPPart().setContent(new StAXSource(rdr));
 439             } else if ((stat & MIME_MULTIPART_FLAG) != 0) {
 440                 final InputStream finalIn = in;
 441                 DataSource ds = new DataSource() {
 442                     @Override
 443                     public InputStream getInputStream() {
 444                         return finalIn;
 445                     }
 446 
 447                     @Override
 448                     public OutputStream getOutputStream() {
 449                         return null;
 450                     }
 451 
 452                     @Override
 453                     public String getContentType() {
 454                         return contentType.toString();
 455                     }
 456 
 457                     @Override
 458                     public String getName() {
 459                         return "";
 460                     }
 461                 };
 462 
 463                 multiPart = null;
 464                 if (useMimePull) {
 465                     multiPart = new MimePullMultipart(ds,contentType);
 466                 } else if (switchOffBM) {
 467                     multiPart = new MimeMultipart(ds,contentType);
 468                 } else {
 469                     multiPart = new BMMimeMultipart(ds,contentType);
 470                 }
 471 
 472                 String startParam = contentType.getParameter("start");
 473                 MimeBodyPart soapMessagePart = null;
 474                 InputStream soapPartInputStream = null;
 475                 String contentID = null;
 476                 String contentIDNoAngle = null;
 477                 if (switchOffBM || switchOffLazyAttachment) {


 592 
 593     public void setIsFastInfoset(boolean value) {
 594         if (value != isFastInfoset) {
 595             isFastInfoset = value;
 596             if (isFastInfoset) {
 597                 acceptFastInfoset = true;
 598             }
 599             saved = false;      // ensure transcoding if necessary
 600         }
 601     }
 602 
 603     public boolean isLazySoapBodyParsing() {
 604         Object lazyParsingProp = getProperty(LAZY_SOAP_BODY_PARSING);
 605         if (lazyParsingProp == null) return false;
 606         if (lazyParsingProp instanceof Boolean) {
 607             return ((Boolean) lazyParsingProp).booleanValue();
 608         } else {
 609             return Boolean.valueOf(lazyParsingProp.toString());
 610         }
 611     }
 612     @Override
 613     public Object getProperty(String property) {
 614         return properties.get(property);
 615     }
 616 
 617     @Override
 618     public void setProperty(String property, Object value) {
 619         verify(property, value);
 620         properties.put(property, value);
 621     }
 622 
 623     private void verify(String property, Object value) {
 624         if (property.equalsIgnoreCase(SOAPMessage.WRITE_XML_DECLARATION)) {
 625             if (!("true".equals(value) || "false".equals(value)))
 626                 throw new RuntimeException(
 627                     property + " must have value false or true");
 628 
 629             try {
 630                 EnvelopeImpl env = (EnvelopeImpl) getSOAPPart().getEnvelope();
 631                 if ("true".equalsIgnoreCase((String)value)) {
 632                     env.setOmitXmlDecl("no");
 633                 } else if ("false".equalsIgnoreCase((String)value)) {
 634                     env.setOmitXmlDecl("yes");
 635                 }
 636             } catch (Exception e) {
 637                 log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property",


 725             throw new SOAPExceptionImpl(
 726                 "Invalid Content-Type:"
 727                     + primary
 728                     + '/'
 729                     + sub
 730                     + ". Is this an error message instead of a SOAP response?");
 731         }
 732     }
 733 
 734     /**
 735      * Obtains the type parameter of the Content-Type header. Defaults to "text/xml".
 736      */
 737     private static String getTypeParameter(ContentType contentType) {
 738         String p = contentType.getParameter("type");
 739         if(p!=null)
 740             return p.toLowerCase();
 741         else
 742             return "text/xml";
 743     }
 744 
 745     @Override
 746     public MimeHeaders getMimeHeaders() {
 747         return this.headers;
 748     }
 749 
 750     final static String getContentType(MimeHeaders headers) {
 751         String[] values = headers.getHeader("Content-Type");
 752         if (values == null)
 753             return null;
 754         else
 755             return values[0];
 756     }
 757 
 758     /*
 759      * Get the complete ContentType value along with optional parameters.
 760      */
 761     public String getContentType() {
 762         return getContentType(this.headers);
 763     }
 764 
 765     public void setContentType(String type) {


 809     public String getCharset() {
 810         return contentType().getParameter("charset");
 811     }
 812 
 813     public void setCharset(String charset) {
 814         ContentType ct = contentType();
 815         ct.setParameter("charset", charset);
 816         headers.setHeader("Content-Type", ct.toString());
 817         needsSave();
 818     }
 819 
 820     /**
 821      * All write methods (i.e setters) should call this method in
 822      * order to make sure that a save is necessary since the state
 823      * has been modified.
 824      */
 825     private final void needsSave() {
 826         saved = false;
 827     }
 828 
 829     @Override
 830     public  boolean saveRequired() {
 831         return saved != true;
 832     }
 833 
 834     @Override
 835     public String getContentDescription() {
 836         String[] values = headers.getHeader("Content-Description");
 837         if (values != null && values.length > 0)
 838             return values[0];
 839         return null;
 840     }
 841 
 842     @Override
 843     public void setContentDescription(String description) {
 844         headers.setHeader("Content-Description", description);
 845         needsSave();
 846     }
 847 
 848     @Override
 849     public abstract SOAPPart getSOAPPart();
 850 
 851     @Override
 852     public void removeAllAttachments() {
 853         try {
 854             initializeAllAttachments();
 855         } catch (Exception e) {
 856             throw new RuntimeException(e);
 857         }
 858 
 859         if (attachments != null) {
 860             attachments.clear();
 861             needsSave();
 862         }
 863     }
 864 
 865     @Override
 866     public int countAttachments() {
 867         try {
 868             initializeAllAttachments();
 869         } catch (Exception e) {
 870             throw new RuntimeException(e);
 871         }
 872         if (attachments != null)
 873             return attachments.size();
 874         return 0;
 875     }
 876 
 877     @Override
 878     public void addAttachmentPart(AttachmentPart attachment) {
 879         try {
 880             initializeAllAttachments();
 881             this.optimizeAttachmentProcessing = true;
 882         } catch (Exception e) {
 883             throw new RuntimeException(e);
 884         }
 885         if (attachments == null)
 886             attachments = new FinalArrayList<AttachmentPart>();
 887 
 888         attachments.add(attachment);
 889 
 890         needsSave();
 891     }
 892 
 893     static private final Iterator nullIter = Collections.EMPTY_LIST.iterator();
 894 
 895     @Override
 896     public Iterator getAttachments() {
 897         try {
 898             initializeAllAttachments();
 899         } catch (Exception e) {
 900             throw new RuntimeException(e);
 901         }
 902         if (attachments == null)
 903             return nullIter;
 904         return attachments.iterator();
 905     }
 906 
 907     private void setFinalContentType(String charset) {
 908         ContentType ct = contentType();
 909         if (ct == null) {
 910             ct = new ContentType();
 911         }
 912         String[] split = getExpectedContentType().split("/");
 913         ct.setPrimaryType(split[0]);
 914         ct.setSubType(split[1]);
 915         ct.setParameter("charset", charset);
 916         headers.setHeader("Content-Type", ct.toString());
 917     }
 918 
 919     private class MimeMatchingIterator implements Iterator<AttachmentPart> {
 920         public MimeMatchingIterator(MimeHeaders headers) {
 921             this.headers = headers;
 922             this.iter = attachments.iterator();
 923         }
 924 
 925         private Iterator<AttachmentPart> iter;
 926         private MimeHeaders headers;
 927         private AttachmentPart nextAttachment;
 928 
 929         @Override
 930         public boolean hasNext() {
 931             if (nextAttachment == null)
 932                 nextAttachment = nextMatch();
 933             return nextAttachment != null;
 934         }
 935 
 936         @Override
 937         public AttachmentPart next() {
 938             if (nextAttachment != null) {
 939                 AttachmentPart ret = nextAttachment;
 940                 nextAttachment = null;
 941                 return ret;
 942             }
 943 
 944             if (hasNext())
 945                 return nextAttachment;
 946 
 947             return null;
 948         }
 949 
 950         AttachmentPart nextMatch() {
 951             while (iter.hasNext()) {
 952                 AttachmentPartImpl ap = (AttachmentPartImpl) iter.next();
 953                 if (ap.hasAllHeaders(headers))
 954                     return ap;
 955             }
 956             return null;
 957         }
 958 
 959         @Override
 960         public void remove() {
 961             iter.remove();
 962         }
 963     }
 964 
 965     @Override
 966     public Iterator getAttachments(MimeHeaders headers) {
 967         try {
 968             initializeAllAttachments();
 969         } catch (Exception e) {
 970             throw new RuntimeException(e);
 971         }
 972         if (attachments == null)
 973             return nullIter;
 974 
 975         return new MimeMatchingIterator(headers);
 976     }
 977 
 978     @Override
 979     public void removeAttachments(MimeHeaders headers) {
 980         try {
 981             initializeAllAttachments();
 982         } catch (Exception e) {
 983             throw new RuntimeException(e);
 984         }
 985         if (attachments == null)
 986             return ;
 987 
 988         Iterator<AttachmentPart> it =  new MimeMatchingIterator(headers);
 989         while (it.hasNext()) {
 990             int index = attachments.indexOf(it.next());
 991             attachments.set(index, null);
 992         }
 993         FinalArrayList<AttachmentPart> f = new FinalArrayList<AttachmentPart>();
 994         for (int i = 0; i < attachments.size(); i++) {
 995             if (attachments.get(i) != null) {
 996                 f.add(attachments.get(i));
 997             }
 998         }
 999         attachments = f;
1000        // needsSave();
1001     }
1002 
1003     @Override
1004     public AttachmentPart createAttachmentPart() {
1005         return new AttachmentPartImpl();
1006     }
1007 
1008     @Override
1009     public  AttachmentPart getAttachment(SOAPElement element)
1010         throws SOAPException {
1011         try {
1012             initializeAllAttachments();
1013         } catch (Exception e) {
1014             throw new RuntimeException(e);
1015         }
1016         String uri;
1017         String hrefAttr = element.getAttribute("href");
1018         if ("".equals(hrefAttr)) {
1019             Node node = getValueNodeStrict(element);
1020             String swaRef = null;
1021             if (node != null) {
1022                 swaRef = node.getValue();
1023             }
1024             if (swaRef == null || "".equals(swaRef)) {
1025                 return null;
1026             } else {
1027                 uri = swaRef;
1028             }


1206 
1207         if (charset != null) {
1208             return charset;
1209         }
1210 
1211         return "utf-8";
1212     }
1213 
1214     private String getCharsetString(String s) {
1215         try {
1216             int index = s.indexOf(";");
1217             if(index < 0)
1218                 return null;
1219             ParameterList pl = new ParameterList(s.substring(index));
1220             return pl.get("charset");
1221         } catch(Exception e) {
1222             return null;
1223         }
1224     }
1225 
1226     @Override
1227     public void saveChanges() throws SOAPException {
1228 
1229         // suck in all the data from the attachments and have it
1230         // ready for writing/sending etc.
1231 
1232         String charset = initCharset();
1233 
1234         /*if (countAttachments() == 0) {*/
1235         int attachmentCount = (attachments == null) ? 0 : attachments.size();
1236         if (attachmentCount == 0) {
1237             if (!switchOffBM && !switchOffLazyAttachment &&
1238                 !attachmentsInitialized && (multiPart != null)) {
1239                 // so there might be attachments
1240                 attachmentCount = 1;
1241             }
1242         }
1243 
1244         try {
1245             if ((attachmentCount == 0) && !hasXOPContent()) {
1246                 InputStream in;


1360             log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
1361             throw new SOAPExceptionImpl(
1362                 "Unable to convert SOAP message into "
1363                     + "a MimeMultipart object",
1364                 ex);
1365         }
1366 
1367     }
1368 
1369     private boolean hasXOPContent() throws ParseException {
1370         String type = getContentType();
1371         if(type == null)
1372             return false;
1373         ContentType ct = new ContentType(type);
1374         //return isMimeMultipartXOPPackage(ct) || isSOAPBodyXOPPackage(ct);
1375         return isMimeMultipartXOPSoap1_1Package(ct) ||
1376             isMimeMultipartXOPSoap1_2Package(ct) || isSOAPBodyXOPPackage(ct);
1377 
1378     }
1379 
1380     @Override
1381     public void writeTo(OutputStream out) throws SOAPException, IOException {
1382         if (saveRequired()){
1383             this.optimizeAttachmentProcessing = true;
1384             saveChanges();
1385         }
1386 
1387         if(!optimizeAttachmentProcessing){
1388             if (SOAPPartImpl.lazyContentLength && messageByteCount <= 0) {
1389                 byte[] buf = new byte[1024];
1390 
1391                 int length = 0;
1392                 while( (length = inputStreamAfterSaveChanges.read(buf)) != -1) {
1393                     out.write(buf,0, length);
1394                     messageByteCount += length;
1395                 }
1396                 if (messageByteCount > 0) {
1397                     headers.setHeader(
1398                             "Content-Length",
1399                             Integer.toString(messageByteCount));
1400                 }


1418                 log.severe("SAAJ0540.soap.err.saving.multipart.msg");
1419                 throw new SOAPExceptionImpl(
1420                         "Error during saving a multipart message",
1421                         ex);
1422             }
1423         }
1424 
1425         if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
1426 
1427             String[] soapAction = headers.getHeader("SOAPAction");
1428 
1429             if (soapAction == null || soapAction.length == 0)
1430                 headers.setHeader("SOAPAction", "\"\"");
1431 
1432         }
1433 
1434         messageBytes = null;
1435         needsSave();
1436     }
1437 
1438     @Override
1439     public SOAPBody getSOAPBody() throws SOAPException {
1440         SOAPBody body = getSOAPPart().getEnvelope().getBody();
1441         /*if (body == null) {
1442              throw new SOAPException("No SOAP Body was found in the SOAP Message");
1443         }*/
1444         return body;
1445     }
1446 
1447     @Override
1448     public SOAPHeader getSOAPHeader() throws SOAPException {
1449         SOAPHeader hdr = getSOAPPart().getEnvelope().getHeader();
1450         /*if (hdr == null) {
1451             throw new SOAPException("No SOAP Header was found in the SOAP Message");
1452         }*/
1453         return hdr;
1454     }
1455 
1456     private void initializeAllAttachments ()
1457         throws MessagingException, SOAPException {
1458         if (switchOffBM || switchOffLazyAttachment) {
1459             return;
1460         }
1461 
1462         if (attachmentsInitialized || (multiPart == null)) {
1463             return;
1464         }
1465 
1466         if (attachments == null)
1467             attachments = new FinalArrayList<AttachmentPart>();


< prev index next >