/* * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.xml.internal.messaging.saaj.soap; import java.io.*; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.xml.soap.*; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.Source; import javax.xml.transform.stax.StAXSource; import javax.xml.transform.stream.StreamSource; import com.sun.xml.internal.messaging.saaj.packaging.mime.Header; import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.*; import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*; import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException; import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl; import com.sun.xml.internal.messaging.saaj.soap.impl.EnvelopeImpl; import com.sun.xml.internal.messaging.saaj.util.*; import com.sun.xml.internal.org.jvnet.mimepull.MIMEPart; /** * The message implementation for SOAP messages with * attachments. Messages for specific profiles will likely extend this * MessageImpl class and add more value for that particular profile. * * @author Anil Vijendran (akv@eng.sun.com) * @author Rajiv Mordani (rajiv.mordani@sun.com) * @author Manveen Kaur (manveen.kaur@sun.com) */ public abstract class MessageImpl extends SOAPMessage implements SOAPConstants { public static final String CONTENT_ID = "Content-ID"; public static final String CONTENT_LOCATION = "Content-Location"; protected static final Logger log = Logger.getLogger(LogDomainConstants.SOAP_DOMAIN, "com.sun.xml.internal.messaging.saaj.soap.LocalStrings"); protected static final int PLAIN_XML_FLAG = 1; // 00001 protected static final int MIME_MULTIPART_FLAG = 2; // 00010 protected static final int SOAP1_1_FLAG = 4; // 00100 protected static final int SOAP1_2_FLAG = 8; // 01000 //protected static final int MIME_MULTIPART_XOP_FLAG = 14; // 01110 protected static final int MIME_MULTIPART_XOP_SOAP1_1_FLAG = 6; // 00110 protected static final int MIME_MULTIPART_XOP_SOAP1_2_FLAG = 10; // 01010 protected static final int XOP_FLAG = 13; // 01101 protected static final int FI_ENCODED_FLAG = 16; // 10000 protected MimeHeaders headers; protected ContentType contentType; protected SOAPPartImpl soapPartImpl; protected FinalArrayList attachments; protected boolean saved = false; protected byte[] messageBytes; protected int messageByteCount; protected HashMap properties = new HashMap(); // used for lazy attachment initialization protected MimeMultipart multiPart = null; protected boolean attachmentsInitialized = false; /** * True if this part is encoded using Fast Infoset. * MIME -> application/fastinfoset */ protected boolean isFastInfoset = false; /** * True if the Accept header of this message includes * application/fastinfoset */ protected boolean acceptFastInfoset = false; protected MimeMultipart mmp = null; // if attachments are present, don't read the entire message in byte stream in saveTo() private boolean optimizeAttachmentProcessing = true; private InputStream inputStreamAfterSaveChanges = null; public static final String LAZY_SOAP_BODY_PARSING = "saaj.lazy.soap.body"; // switch back to old MimeMultipart incase of problem private static boolean switchOffBM = false; private static boolean switchOffLazyAttachment = false; private static boolean useMimePull = false; static { String s = SAAJUtil.getSystemProperty("saaj.mime.optimization"); if ((s != null) && s.equals("false")) { switchOffBM = true; } s = SAAJUtil.getSystemProperty("saaj.lazy.mime.optimization"); if ((s != null) && s.equals("false")) { switchOffLazyAttachment = true; } useMimePull = SAAJUtil.getSystemBoolean("saaj.use.mimepull"); } //property to indicate optimized serialization for lazy attachments private boolean lazyAttachments = false; // most of the times, Content-Types are already all lower cased. // String.toLowerCase() works faster in this case, so even if you // are only doing one comparison, it pays off to use String.toLowerCase() // than String.equalsIgnoreCase(). When you do more than one comparison, // the benefits of String.toLowerCase() dominates. // // // for FI, // use application/fastinfoset for SOAP 1.1 // use application/soap+fastinfoset for SOAP 1.2 // to speed up comparisons, test methods always use lower cases. /** * @param primary * must be all lower case * @param sub * must be all lower case */ private static boolean isSoap1_1Type(String primary, String sub) { return primary.equalsIgnoreCase("text") && sub.equalsIgnoreCase("xml") || primary.equalsIgnoreCase("text") && sub.equalsIgnoreCase("xml-soap") || primary.equals("application") && sub.equals("fastinfoset"); } /** * @param type * must be all lower case */ private static boolean isEqualToSoap1_1Type(String type) { return type.startsWith("text/xml") || type.startsWith("application/fastinfoset"); } /** * @param primary * must be all lower case * @param sub * must be all lower case */ private static boolean isSoap1_2Type(String primary, String sub) { return primary.equals("application") && (sub.equals("soap+xml") || sub.equals("soap+fastinfoset")); } /** * @param type * must be all lower case */ private static boolean isEqualToSoap1_2Type(String type) { return type.startsWith("application/soap+xml") || type.startsWith("application/soap+fastinfoset"); } /** * Construct a new message. This will be invoked before message * sends. */ protected MessageImpl() { this(false, false); attachmentsInitialized = true; } /** * Construct a new message. This will be invoked before message * sends. */ protected MessageImpl(boolean isFastInfoset, boolean acceptFastInfoset) { this.isFastInfoset = isFastInfoset; this.acceptFastInfoset = acceptFastInfoset; headers = new MimeHeaders(); headers.setHeader("Accept", getExpectedAcceptHeader()); contentType = new ContentType(); } /** * Shallow copy. */ protected MessageImpl(SOAPMessage msg) { if (!(msg instanceof MessageImpl)) { // don't know how to handle this. } MessageImpl src = (MessageImpl) msg; this.headers = src.headers; this.soapPartImpl = src.soapPartImpl; this.attachments = src.attachments; this.saved = src.saved; this.messageBytes = src.messageBytes; this.messageByteCount = src.messageByteCount; this.properties = src.properties; this.contentType = src.contentType; } /** * @param stat * the mask value obtained from {@link #identifyContentType(ContentType)} */ protected static boolean isSoap1_1Content(int stat) { return (stat & SOAP1_1_FLAG) != 0; } /** * @param stat * the mask value obtained from {@link #identifyContentType(ContentType)} */ protected static boolean isSoap1_2Content(int stat) { return (stat & SOAP1_2_FLAG) != 0; } private static boolean isMimeMultipartXOPSoap1_2Package(ContentType contentType) { String type = contentType.getParameter("type"); if (type == null) { return false; } type = type.toLowerCase(); if (!type.startsWith("application/xop+xml")) { return false; } String startinfo = contentType.getParameter("start-info"); if (startinfo == null) { return false; } startinfo = startinfo.toLowerCase(); return isEqualToSoap1_2Type(startinfo); } //private static boolean isMimeMultipartXOPPackage(ContentType contentType) { private static boolean isMimeMultipartXOPSoap1_1Package(ContentType contentType) { String type = contentType.getParameter("type"); if(type==null) return false; type = type.toLowerCase(); if(!type.startsWith("application/xop+xml")) return false; String startinfo = contentType.getParameter("start-info"); if(startinfo == null) return false; startinfo = startinfo.toLowerCase(); return isEqualToSoap1_1Type(startinfo); } private static boolean isSOAPBodyXOPPackage(ContentType contentType){ String primary = contentType.getPrimaryType(); String sub = contentType.getSubType(); if (primary.equalsIgnoreCase("application")) { if (sub.equalsIgnoreCase("xop+xml")) { String type = getTypeParameter(contentType); return isEqualToSoap1_2Type(type) || isEqualToSoap1_1Type(type); } } return false; } /** * Construct a message from an input stream. When messages are * received, there's two parts -- the transport headers and the * message content in a transport specific stream. */ protected MessageImpl(MimeHeaders headers, final InputStream in) throws SOAPExceptionImpl { contentType = parseContentType(headers); init(headers,identifyContentType(contentType),contentType,in); } private static ContentType parseContentType(MimeHeaders headers) throws SOAPExceptionImpl { final String ct; if (headers != null) ct = getContentType(headers); else { log.severe("SAAJ0550.soap.null.headers"); throw new SOAPExceptionImpl("Cannot create message: " + "Headers can't be null"); } if (ct == null) { log.severe("SAAJ0532.soap.no.Content-Type"); throw new SOAPExceptionImpl("Absent Content-Type"); } try { return new ContentType(ct); } catch (Throwable ex) { log.severe("SAAJ0535.soap.cannot.internalize.message"); throw new SOAPExceptionImpl("Unable to internalize message", ex); } } /** * Construct a message from an input stream. When messages are * received, there's two parts -- the transport headers and the * message content in a transport specific stream. * * @param contentType * The parsed content type header from the headers variable. * This is redundant parameter, but it avoids reparsing this header again. * @param stat * The result of {@link #identifyContentType(ContentType)} over * the contentType parameter. This redundant parameter, but it avoids * recomputing this information again. */ protected MessageImpl(MimeHeaders headers, final ContentType contentType, int stat, final InputStream in) throws SOAPExceptionImpl { init(headers, stat, contentType, in); } public MessageImpl(MimeHeaders headers, ContentType ct, int stat, XMLStreamReader reader) throws SOAPExceptionImpl { init(headers, stat, ct, reader); } private void init(MimeHeaders headers, int stat, final ContentType contentType, final Object input) throws SOAPExceptionImpl { this.headers = headers; try { // Set isFastInfoset/acceptFastInfoset flag based on MIME type if ((stat & FI_ENCODED_FLAG) > 0) { isFastInfoset = acceptFastInfoset = true; } // If necessary, inspect Accept header to set acceptFastInfoset if (!isFastInfoset) { String[] values = headers.getHeader("Accept"); if (values != null) { for (int i = 0; i < values.length; i++) { StringTokenizer st = new StringTokenizer(values[i], ","); while (st.hasMoreTokens()) { final String token = st.nextToken().trim(); if (token.equalsIgnoreCase("application/fastinfoset") || token.equalsIgnoreCase("application/soap+fastinfoset")) { acceptFastInfoset = true; break; } } } } } if (!isCorrectSoapVersion(stat)) { log.log( Level.SEVERE, "SAAJ0533.soap.incorrect.Content-Type", new String[] { contentType.toString(), getExpectedContentType()}); throw new SOAPVersionMismatchException( "Cannot create message: incorrect content-type for SOAP version. Got: " + contentType + " Expected: " + getExpectedContentType()); } InputStream in = null; XMLStreamReader rdr = null; if (input instanceof InputStream) { in = (InputStream) input; } else { //is a StAX reader rdr = (XMLStreamReader) input; } if ((stat & PLAIN_XML_FLAG) != 0) { if (in != null) { if (isFastInfoset) { getSOAPPart().setContent( FastInfosetReflection.FastInfosetSource_new(in)); } else { initCharsetProperty(contentType); getSOAPPart().setContent(new StreamSource(in)); } } else { //is a StAX reader if (isFastInfoset) { //need to get FI stax reader } else { initCharsetProperty(contentType); getSOAPPart().setContent(new StAXSource(rdr)); } } } else if ((stat & MIME_MULTIPART_FLAG) != 0 && in == null) { //only parse multipart in the inputstream case //in stax reader case, we would be given the attachments separately getSOAPPart().setContent(new StAXSource(rdr)); } else if ((stat & MIME_MULTIPART_FLAG) != 0) { final InputStream finalIn = in; DataSource ds = new DataSource() { public InputStream getInputStream() { return finalIn; } public OutputStream getOutputStream() { return null; } public String getContentType() { return contentType.toString(); } public String getName() { return ""; } }; multiPart = null; if (useMimePull) { multiPart = new MimePullMultipart(ds,contentType); } else if (switchOffBM) { multiPart = new MimeMultipart(ds,contentType); } else { multiPart = new BMMimeMultipart(ds,contentType); } String startParam = contentType.getParameter("start"); MimeBodyPart soapMessagePart = null; InputStream soapPartInputStream = null; String contentID = null; String contentIDNoAngle = null; if (switchOffBM || switchOffLazyAttachment) { if(startParam == null) { soapMessagePart = multiPart.getBodyPart(0); for (int i = 1; i < multiPart.getCount(); i++) { initializeAttachment(multiPart, i); } } else { soapMessagePart = multiPart.getBodyPart(startParam); for (int i = 0; i < multiPart.getCount(); i++) { contentID = multiPart.getBodyPart(i).getContentID(); // Old versions of AXIS2 put angle brackets around the content // id but not the start param contentIDNoAngle = (contentID != null) ? contentID.replaceFirst("^<", "").replaceFirst(">$", "") : null; if(!startParam.equals(contentID) && !startParam.equals(contentIDNoAngle)) initializeAttachment(multiPart, i); } } } else { if (useMimePull) { MimePullMultipart mpMultipart = (MimePullMultipart)multiPart; MIMEPart sp = mpMultipart.readAndReturnSOAPPart(); soapMessagePart = new MimeBodyPart(sp); soapPartInputStream = sp.readOnce(); } else { BMMimeMultipart bmMultipart = (BMMimeMultipart) multiPart; InputStream stream = bmMultipart.initStream(); SharedInputStream sin = null; if (stream instanceof SharedInputStream) { sin = (SharedInputStream) stream; } String boundary = "--" + contentType.getParameter("boundary"); byte[] bndbytes = ASCIIUtility.getBytes(boundary); if (startParam == null) { soapMessagePart = bmMultipart.getNextPart(stream, bndbytes, sin); bmMultipart.removeBodyPart(soapMessagePart); } else { MimeBodyPart bp = null; try { while (!startParam.equals(contentID) && !startParam.equals(contentIDNoAngle)) { bp = bmMultipart.getNextPart( stream, bndbytes, sin); contentID = bp.getContentID(); // Old versions of AXIS2 put angle brackets around the content // id but not the start param contentIDNoAngle = (contentID != null) ? contentID.replaceFirst("^<", "").replaceFirst(">$", "") : null; } soapMessagePart = bp; bmMultipart.removeBodyPart(bp); } catch (Exception e) { throw new SOAPExceptionImpl(e); } } } } // findbugs correctly points out that we'd NPE instantiating // the ContentType (just below here) if soapMessagePart were // null. Hence are better off throwing a controlled exception // at this point if it is null. if (soapMessagePart == null) { log.severe("SAAJ0510.soap.cannot.create.envelope"); throw new SOAPExceptionImpl( "Unable to create envelope from given source: SOAP part not found"); } if (soapPartInputStream == null) { soapPartInputStream = soapMessagePart.getInputStream(); } ContentType soapPartCType = new ContentType( soapMessagePart.getContentType()); initCharsetProperty(soapPartCType); String baseType = soapPartCType.getBaseType().toLowerCase(); if(!(isEqualToSoap1_1Type(baseType) || isEqualToSoap1_2Type(baseType) || isSOAPBodyXOPPackage(soapPartCType))) { log.log(Level.SEVERE, "SAAJ0549.soap.part.invalid.Content-Type", new Object[] {baseType}); throw new SOAPExceptionImpl( "Bad Content-Type for SOAP Part : " + baseType); } SOAPPart soapPart = getSOAPPart(); setMimeHeaders(soapPart, soapMessagePart); soapPart.setContent(isFastInfoset ? (Source) FastInfosetReflection.FastInfosetSource_new( soapPartInputStream) : (Source) new StreamSource(soapPartInputStream)); } else { log.severe("SAAJ0534.soap.unknown.Content-Type"); throw new SOAPExceptionImpl("Unrecognized Content-Type"); } } catch (Throwable ex) { log.severe("SAAJ0535.soap.cannot.internalize.message"); throw new SOAPExceptionImpl("Unable to internalize message", ex); } needsSave(); } public boolean isFastInfoset() { return isFastInfoset; } public boolean acceptFastInfoset() { return acceptFastInfoset; } public void setIsFastInfoset(boolean value) { if (value != isFastInfoset) { isFastInfoset = value; if (isFastInfoset) { acceptFastInfoset = true; } saved = false; // ensure transcoding if necessary } } public boolean isLazySoapBodyParsing() { Object lazyParsingProp = getProperty(LAZY_SOAP_BODY_PARSING); if (lazyParsingProp == null) return false; if (lazyParsingProp instanceof Boolean) { return ((Boolean) lazyParsingProp).booleanValue(); } else { return Boolean.valueOf(lazyParsingProp.toString()); } } public Object getProperty(String property) { return (String) properties.get(property); } public void setProperty(String property, Object value) { verify(property, value); properties.put(property, value); } private void verify(String property, Object value) { if (property.equalsIgnoreCase(SOAPMessage.WRITE_XML_DECLARATION)) { if (!("true".equals(value) || "false".equals(value))) throw new RuntimeException( property + " must have value false or true"); try { EnvelopeImpl env = (EnvelopeImpl) getSOAPPart().getEnvelope(); if ("true".equalsIgnoreCase((String)value)) { env.setOmitXmlDecl("no"); } else if ("false".equalsIgnoreCase((String)value)) { env.setOmitXmlDecl("yes"); } } catch (Exception e) { log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property", new Object[] {e.getMessage(), "javax.xml.soap.write-xml-declaration"}); throw new RuntimeException(e); } return; } if (property.equalsIgnoreCase(SOAPMessage.CHARACTER_SET_ENCODING)) { try { ((EnvelopeImpl) getSOAPPart().getEnvelope()).setCharsetEncoding((String)value); } catch (Exception e) { log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property", new Object[] {e.getMessage(), "javax.xml.soap.character-set-encoding"}); throw new RuntimeException(e); } } } protected abstract boolean isCorrectSoapVersion(int contentTypeId); protected abstract String getExpectedContentType(); protected abstract String getExpectedAcceptHeader(); /** * Sniffs the Content-Type header so that we can determine how to process. * *
* In the absence of type attribute we assume it to be text/xml.
* That would mean we're easy on accepting the message and
* generate the correct thing (as the SWA spec also specifies
* that the type parameter should always be text/xml)
*
* @return
* combination of flags, such as PLAIN_XML_CODE and MIME_MULTIPART_CODE.
*/
// SOAP1.2 allow SOAP1.2 content type
static int identifyContentType(ContentType ct)
throws SOAPExceptionImpl {
// TBD
// Is there anything else we need to verify here?
String primary = ct.getPrimaryType().toLowerCase();
String sub = ct.getSubType().toLowerCase();
if (primary.equals("multipart")) {
if (sub.equals("related")) {
String type = getTypeParameter(ct);
if (isEqualToSoap1_1Type(type)) {
return (type.equals("application/fastinfoset") ?
FI_ENCODED_FLAG : 0) | MIME_MULTIPART_FLAG | SOAP1_1_FLAG;
}
else if (isEqualToSoap1_2Type(type)) {
return (type.equals("application/soap+fastinfoset") ?
FI_ENCODED_FLAG : 0) | MIME_MULTIPART_FLAG | SOAP1_2_FLAG;
/*} else if (isMimeMultipartXOPPackage(ct)) {
return MIME_MULTIPART_XOP_FLAG;*/
} else if (isMimeMultipartXOPSoap1_1Package(ct)) {
return MIME_MULTIPART_XOP_SOAP1_1_FLAG;
} else if (isMimeMultipartXOPSoap1_2Package(ct)) {
return MIME_MULTIPART_XOP_SOAP1_2_FLAG;
} else {
log.severe("SAAJ0536.soap.content-type.mustbe.multipart");
throw new SOAPExceptionImpl(
"Content-Type needs to be Multipart/Related "
+ "and with \"type=text/xml\" "
+ "or \"type=application/soap+xml\"");
}
} else {
log.severe("SAAJ0537.soap.invalid.content-type");
throw new SOAPExceptionImpl(
"Invalid Content-Type: " + primary + '/' + sub);
}
}
else if (isSoap1_1Type(primary, sub)) {
return (primary.equalsIgnoreCase("application")
&& sub.equalsIgnoreCase("fastinfoset") ?
FI_ENCODED_FLAG : 0)
| PLAIN_XML_FLAG | SOAP1_1_FLAG;
}
else if (isSoap1_2Type(primary, sub)) {
return (primary.equalsIgnoreCase("application")
&& sub.equalsIgnoreCase("soap+fastinfoset") ?
FI_ENCODED_FLAG : 0)
| PLAIN_XML_FLAG | SOAP1_2_FLAG;
} else if(isSOAPBodyXOPPackage(ct)){
return XOP_FLAG;
} else {
log.severe("SAAJ0537.soap.invalid.content-type");
throw new SOAPExceptionImpl(
"Invalid Content-Type:"
+ primary
+ '/'
+ sub
+ ". Is this an error message instead of a SOAP response?");
}
}
/**
* Obtains the type parameter of the Content-Type header. Defaults to "text/xml".
*/
private static String getTypeParameter(ContentType contentType) {
String p = contentType.getParameter("type");
if(p!=null)
return p.toLowerCase();
else
return "text/xml";
}
public MimeHeaders getMimeHeaders() {
return this.headers;
}
final static String getContentType(MimeHeaders headers) {
String[] values = headers.getHeader("Content-Type");
if (values == null)
return null;
else
return values[0];
}
/*
* Get the complete ContentType value along with optional parameters.
*/
public String getContentType() {
return getContentType(this.headers);
}
public void setContentType(String type) {
headers.setHeader("Content-Type", type);
needsSave();
}
private ContentType contentType() {
ContentType ct = null;
try {
String currentContent = getContentType();
if (currentContent == null) {
return this.contentType;
}
ct = new ContentType(currentContent);
} catch (Exception e) {
// what to do here?
}
return ct;
}
/*
* Return the MIME type string, without the parameters.
*/
public String getBaseType() {
return contentType().getBaseType();
}
public void setBaseType(String type) {
ContentType ct = contentType();
ct.setParameter("type", type);
headers.setHeader("Content-Type", ct.toString());
needsSave();
}
public String getAction() {
return contentType().getParameter("action");
}
public void setAction(String action) {
ContentType ct = contentType();
ct.setParameter("action", action);
headers.setHeader("Content-Type", ct.toString());
needsSave();
}
public String getCharset() {
return contentType().getParameter("charset");
}
public void setCharset(String charset) {
ContentType ct = contentType();
ct.setParameter("charset", charset);
headers.setHeader("Content-Type", ct.toString());
needsSave();
}
/**
* All write methods (i.e setters) should call this method in
* order to make sure that a save is necessary since the state
* has been modified.
*/
private final void needsSave() {
saved = false;
}
public boolean saveRequired() {
return saved != true;
}
public String getContentDescription() {
String[] values = headers.getHeader("Content-Description");
if (values != null && values.length > 0)
return values[0];
return null;
}
public void setContentDescription(String description) {
headers.setHeader("Content-Description", description);
needsSave();
}
public abstract SOAPPart getSOAPPart();
public void removeAllAttachments() {
try {
initializeAllAttachments();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (attachments != null) {
attachments.clear();
needsSave();
}
}
public int countAttachments() {
try {
initializeAllAttachments();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (attachments != null)
return attachments.size();
return 0;
}
public void addAttachmentPart(AttachmentPart attachment) {
try {
initializeAllAttachments();
this.optimizeAttachmentProcessing = true;
} catch (Exception e) {
throw new RuntimeException(e);
}
if (attachments == null)
attachments = new FinalArrayList();
attachments.add(attachment);
needsSave();
}
static private final Iterator nullIter = Collections.EMPTY_LIST.iterator();
public Iterator getAttachments() {
try {
initializeAllAttachments();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (attachments == null)
return nullIter;
return attachments.iterator();
}
private void setFinalContentType(String charset) {
ContentType ct = contentType();
if (ct == null) {
ct = new ContentType();
}
String[] split = getExpectedContentType().split("/");
ct.setPrimaryType(split[0]);
ct.setSubType(split[1]);
ct.setParameter("charset", charset);
headers.setHeader("Content-Type", ct.toString());
}
private class MimeMatchingIterator implements Iterator {
public MimeMatchingIterator(MimeHeaders headers) {
this.headers = headers;
this.iter = attachments.iterator();
}
private Iterator iter;
private MimeHeaders headers;
private Object nextAttachment;
public boolean hasNext() {
if (nextAttachment == null)
nextAttachment = nextMatch();
return nextAttachment != null;
}
public Object next() {
if (nextAttachment != null) {
Object ret = nextAttachment;
nextAttachment = null;
return ret;
}
if (hasNext())
return nextAttachment;
return null;
}
Object nextMatch() {
while (iter.hasNext()) {
AttachmentPartImpl ap = (AttachmentPartImpl) iter.next();
if (ap.hasAllHeaders(headers))
return ap;
}
return null;
}
public void remove() {
iter.remove();
}
}
public Iterator getAttachments(MimeHeaders headers) {
try {
initializeAllAttachments();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (attachments == null)
return nullIter;
return new MimeMatchingIterator(headers);
}
public void removeAttachments(MimeHeaders headers) {
try {
initializeAllAttachments();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (attachments == null)
return ;
Iterator it = new MimeMatchingIterator(headers);
while (it.hasNext()) {
int index = attachments.indexOf(it.next());
attachments.set(index, null);
}
FinalArrayList f = new FinalArrayList();
for (int i = 0; i < attachments.size(); i++) {
if (attachments.get(i) != null) {
f.add(attachments.get(i));
}
}
attachments = f;
// needsSave();
}
public AttachmentPart createAttachmentPart() {
return new AttachmentPartImpl();
}
public AttachmentPart getAttachment(SOAPElement element)
throws SOAPException {
try {
initializeAllAttachments();
} catch (Exception e) {
throw new RuntimeException(e);
}
String uri;
String hrefAttr = element.getAttribute("href");
if ("".equals(hrefAttr)) {
Node node = getValueNodeStrict(element);
String swaRef = null;
if (node != null) {
swaRef = node.getValue();
}
if (swaRef == null || "".equals(swaRef)) {
return null;
} else {
uri = swaRef;
}
} else {
uri = hrefAttr;
}
return getAttachmentPart(uri);
}
private Node getValueNodeStrict(SOAPElement element) {
Node node = (Node)element.getFirstChild();
if (node != null) {
if (node.getNextSibling() == null
&& node.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
return node;
} else {
return null;
}
}
return null;
}
private AttachmentPart getAttachmentPart(String uri) throws SOAPException {
AttachmentPart _part;
try {
if (uri.startsWith("cid:")) {
// rfc2392
uri = '<'+uri.substring("cid:".length())+'>';
MimeHeaders headersToMatch = new MimeHeaders();
headersToMatch.addHeader(CONTENT_ID, uri);
Iterator i = this.getAttachments(headersToMatch);
_part = (i == null) ? null : (AttachmentPart)i.next();
} else {
// try content-location
MimeHeaders headersToMatch = new MimeHeaders();
headersToMatch.addHeader(CONTENT_LOCATION, uri);
Iterator i = this.getAttachments(headersToMatch);
_part = (i == null) ? null : (AttachmentPart)i.next();
}
// try auto-generated JAXRPC CID
if (_part == null) {
Iterator j = this.getAttachments();
while (j.hasNext()) {
AttachmentPart p = (AttachmentPart)j.next();
String cl = p.getContentId();
if (cl != null) {
// obtain the partname
int eqIndex = cl.indexOf("=");
if (eqIndex > -1) {
cl = cl.substring(1, eqIndex);
if (cl.equalsIgnoreCase(uri)) {
_part = p;
break;
}
}
}
}
}
} catch (Exception se) {
log.log(Level.SEVERE, "SAAJ0590.soap.unable.to.locate.attachment", new Object[] {uri});
throw new SOAPExceptionImpl(se);
}
return _part;
}
private final InputStream getHeaderBytes()
throws IOException {
SOAPPartImpl sp = (SOAPPartImpl) getSOAPPart();
return sp.getContentAsStream();
}
private String convertToSingleLine(String contentType) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < contentType.length(); i ++) {
char c = contentType.charAt(i);
if (c != '\r' && c != '\n' && c != '\t')
buffer.append(c);
}
return buffer.toString();
}
private MimeMultipart getMimeMessage() throws SOAPException {
try {
SOAPPartImpl soapPart = (SOAPPartImpl) getSOAPPart();
MimeBodyPart mimeSoapPart = soapPart.getMimePart();
/*
* Get content type from this message instead of soapPart
* to ensure agreement if soapPart is transcoded (XML <-> FI)
*/
ContentType soapPartCtype = new ContentType(getExpectedContentType());
if (!isFastInfoset) {
soapPartCtype.setParameter("charset", initCharset());
}
mimeSoapPart.setHeader("Content-Type", soapPartCtype.toString());
MimeMultipart headerAndBody = null;
if (!switchOffBM && !switchOffLazyAttachment &&
(multiPart != null) && !attachmentsInitialized) {
headerAndBody = new BMMimeMultipart();
headerAndBody.addBodyPart(mimeSoapPart);
if (attachments != null) {
for (Iterator eachAttachment = attachments.iterator();
eachAttachment.hasNext();) {
headerAndBody.addBodyPart(
((AttachmentPartImpl) eachAttachment.next())
.getMimePart());
}
}
InputStream in = ((BMMimeMultipart)multiPart).getInputStream();
if (!((BMMimeMultipart)multiPart).lastBodyPartFound() &&
!((BMMimeMultipart)multiPart).isEndOfStream()) {
((BMMimeMultipart)headerAndBody).setInputStream(in);
((BMMimeMultipart)headerAndBody).setBoundary(
((BMMimeMultipart)multiPart).getBoundary());
((BMMimeMultipart)headerAndBody).
setLazyAttachments(lazyAttachments);
}
} else {
headerAndBody = new MimeMultipart();
headerAndBody.addBodyPart(mimeSoapPart);
for (Iterator eachAttachement = getAttachments();
eachAttachement.hasNext();
) {
headerAndBody.addBodyPart(
((AttachmentPartImpl) eachAttachement.next())
.getMimePart());
}
}
ContentType contentType = headerAndBody.getContentType();
ParameterList l = contentType.getParameterList();
// set content type depending on SOAP version
l.set("type", getExpectedContentType());
l.set("boundary", contentType.getParameter("boundary"));
ContentType nct = new ContentType("multipart", "related", l);
headers.setHeader(
"Content-Type",
convertToSingleLine(nct.toString()));
// TBD
// Set content length MIME header here.
return headerAndBody;
} catch (SOAPException ex) {
throw ex;
} catch (Throwable ex) {
log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
throw new SOAPExceptionImpl(
"Unable to convert SOAP message into "
+ "a MimeMultipart object",
ex);
}
}
private String initCharset() {
String charset = null;
String[] cts = getMimeHeaders().getHeader("Content-Type");
if ((cts != null) && (cts[0] != null)) {
charset = getCharsetString(cts[0]);
}
if (charset == null) {
charset = (String) getProperty(CHARACTER_SET_ENCODING);
}
if (charset != null) {
return charset;
}
return "utf-8";
}
private String getCharsetString(String s) {
try {
int index = s.indexOf(";");
if(index < 0)
return null;
ParameterList pl = new ParameterList(s.substring(index));
return pl.get("charset");
} catch(Exception e) {
return null;
}
}
public void saveChanges() throws SOAPException {
// suck in all the data from the attachments and have it
// ready for writing/sending etc.
String charset = initCharset();
/*if (countAttachments() == 0) {*/
int attachmentCount = (attachments == null) ? 0 : attachments.size();
if (attachmentCount == 0) {
if (!switchOffBM && !switchOffLazyAttachment &&
!attachmentsInitialized && (multiPart != null)) {
// so there might be attachments
attachmentCount = 1;
}
}
try {
if ((attachmentCount == 0) && !hasXOPContent()) {
InputStream in;
try{
/*
* Not sure why this is called getHeaderBytes(), but it actually
* returns the whole message as a byte stream. This stream could
* be either XML of Fast depending on the mode.
*/
in = getHeaderBytes();
// no attachments, hence this property can be false
this.optimizeAttachmentProcessing = false;
if (SOAPPartImpl.lazyContentLength) {
inputStreamAfterSaveChanges = in;
}
} catch (IOException ex) {
log.severe("SAAJ0539.soap.cannot.get.header.stream");
throw new SOAPExceptionImpl(
"Unable to get header stream in saveChanges: ",
ex);
}
if (in instanceof ByteInputStream) {
ByteInputStream bIn = (ByteInputStream)in;
messageBytes = bIn.getBytes();
messageByteCount = bIn.getCount();
}
setFinalContentType(charset);
/*
headers.setHeader(
"Content-Type",
getExpectedContentType() +
(isFastInfoset ? "" : "; charset=" + charset));*/
if (messageByteCount > 0) {
headers.setHeader(
"Content-Length",
Integer.toString(messageByteCount));
}
} else {
if(hasXOPContent())
mmp = getXOPMessage();
else
mmp = getMimeMessage();
}
} catch (Throwable ex) {
log.severe("SAAJ0540.soap.err.saving.multipart.msg");
throw new SOAPExceptionImpl(
"Error during saving a multipart message",
ex);
}
// FIX ME -- SOAP Action replaced by Content-Type optional parameter action
/*
if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
String[] soapAction = headers.getHeader("SOAPAction");
if (soapAction == null || soapAction.length == 0)
headers.setHeader("SOAPAction", "\"\"");
}
*/
saved = true;
}
private MimeMultipart getXOPMessage() throws SOAPException {
try {
MimeMultipart headerAndBody = new MimeMultipart();
SOAPPartImpl soapPart = (SOAPPartImpl)getSOAPPart();
MimeBodyPart mimeSoapPart = soapPart.getMimePart();
ContentType soapPartCtype =
new ContentType("application/xop+xml");
soapPartCtype.setParameter("type", getExpectedContentType());
String charset = initCharset();
soapPartCtype.setParameter("charset", charset);
mimeSoapPart.setHeader("Content-Type", soapPartCtype.toString());
headerAndBody.addBodyPart(mimeSoapPart);
for (Iterator eachAttachement = getAttachments();
eachAttachement.hasNext();
) {
headerAndBody.addBodyPart(
((AttachmentPartImpl) eachAttachement.next())
.getMimePart());
}
ContentType contentType = headerAndBody.getContentType();
ParameterList l = contentType.getParameterList();
//lets not write start-info for now till we get servlet fix done
l.set("start-info", getExpectedContentType());//+";charset="+initCharset());
// set content type depending on SOAP version
l.set("type", "application/xop+xml");
if (isCorrectSoapVersion(SOAP1_2_FLAG)) {
String action = getAction();
if(action != null)
l.set("action", action);
}
l.set("boundary", contentType.getParameter("boundary"));
ContentType nct = new ContentType("Multipart", "Related", l);
headers.setHeader(
"Content-Type",
convertToSingleLine(nct.toString()));
// TBD
// Set content length MIME header here.
return headerAndBody;
} catch (SOAPException ex) {
throw ex;
} catch (Throwable ex) {
log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
throw new SOAPExceptionImpl(
"Unable to convert SOAP message into "
+ "a MimeMultipart object",
ex);
}
}
private boolean hasXOPContent() throws ParseException {
String type = getContentType();
if(type == null)
return false;
ContentType ct = new ContentType(type);
//return isMimeMultipartXOPPackage(ct) || isSOAPBodyXOPPackage(ct);
return isMimeMultipartXOPSoap1_1Package(ct) ||
isMimeMultipartXOPSoap1_2Package(ct) || isSOAPBodyXOPPackage(ct);
}
public void writeTo(OutputStream out) throws SOAPException, IOException {
if (saveRequired()){
this.optimizeAttachmentProcessing = true;
saveChanges();
}
if(!optimizeAttachmentProcessing){
if (SOAPPartImpl.lazyContentLength && messageByteCount <= 0) {
byte[] buf = new byte[1024];
int length = 0;
while( (length = inputStreamAfterSaveChanges.read(buf)) != -1) {
out.write(buf,0, length);
messageByteCount += length;
}
if (messageByteCount > 0) {
headers.setHeader(
"Content-Length",
Integer.toString(messageByteCount));
}
} else {
out.write(messageBytes, 0, messageByteCount);
}
}
else{
try{
if(hasXOPContent()){
mmp.writeTo(out);
}else{
mmp.writeTo(out);
if (!switchOffBM && !switchOffLazyAttachment &&
(multiPart != null) && !attachmentsInitialized) {
((BMMimeMultipart)multiPart).setInputStream(
((BMMimeMultipart)mmp).getInputStream());
}
}
} catch(Exception ex){
log.severe("SAAJ0540.soap.err.saving.multipart.msg");
throw new SOAPExceptionImpl(
"Error during saving a multipart message",
ex);
}
}
if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
String[] soapAction = headers.getHeader("SOAPAction");
if (soapAction == null || soapAction.length == 0)
headers.setHeader("SOAPAction", "\"\"");
}
messageBytes = null;
needsSave();
}
public SOAPBody getSOAPBody() throws SOAPException {
SOAPBody body = getSOAPPart().getEnvelope().getBody();
/*if (body == null) {
throw new SOAPException("No SOAP Body was found in the SOAP Message");
}*/
return body;
}
public SOAPHeader getSOAPHeader() throws SOAPException {
SOAPHeader hdr = getSOAPPart().getEnvelope().getHeader();
/*if (hdr == null) {
throw new SOAPException("No SOAP Header was found in the SOAP Message");
}*/
return hdr;
}
private void initializeAllAttachments ()
throws MessagingException, SOAPException {
if (switchOffBM || switchOffLazyAttachment) {
return;
}
if (attachmentsInitialized || (multiPart == null)) {
return;
}
if (attachments == null)
attachments = new FinalArrayList();
int count = multiPart.getCount();
for (int i=0; i < count; i++ ) {
initializeAttachment(multiPart.getBodyPart(i));
}
attachmentsInitialized = true;
//multiPart = null;
needsSave();
}
private void initializeAttachment(MimeBodyPart mbp) throws SOAPException {
AttachmentPartImpl attachmentPart = new AttachmentPartImpl();
DataHandler attachmentHandler = mbp.getDataHandler();
attachmentPart.setDataHandler(attachmentHandler);
AttachmentPartImpl.copyMimeHeaders(mbp, attachmentPart);
attachments.add(attachmentPart);
}
private void initializeAttachment(MimeMultipart multiPart, int i)
throws Exception {
MimeBodyPart currentBodyPart = multiPart.getBodyPart(i);
AttachmentPartImpl attachmentPart = new AttachmentPartImpl();
DataHandler attachmentHandler = currentBodyPart.getDataHandler();
attachmentPart.setDataHandler(attachmentHandler);
AttachmentPartImpl.copyMimeHeaders(currentBodyPart, attachmentPart);
addAttachmentPart(attachmentPart);
}
private void setMimeHeaders(SOAPPart soapPart,
MimeBodyPart soapMessagePart) throws Exception {
// first remove the existing content-type
soapPart.removeAllMimeHeaders();
// add everything present in soapMessagePart
List headers = soapMessagePart.getAllHeaders();
int sz = headers.size();
for( int i=0; i