Author: veithen Date: Wed Jun 15 20:25:14 2011 New Revision: 1136177 URL: http://svn.apache.org/viewvc?rev=1136177&view=rev Log: AXIS2-5067: Committed patch provided by Ivan (xuhaihong), with minor changes. This implements the saveChanges method and calculates the correct content type for a SOAPMessage.
Modified: axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPMessageImpl.java axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPPartImpl.java axis/axis2/java/core/trunk/modules/saaj/test/org/apache/axis2/saaj/SOAPMessageTest.java Modified: axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPMessageImpl.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPMessageImpl.java?rev=1136177&r1=1136176&r2=1136177&view=diff ============================================================================== --- axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPMessageImpl.java (original) +++ axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPMessageImpl.java Wed Jun 15 20:25:14 2011 @@ -26,9 +26,12 @@ import org.apache.axiom.om.impl.OMMultip import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axiom.soap.impl.dom.soap11.SOAP11Factory; import org.apache.axiom.soap.impl.dom.soap12.SOAP12Factory; +import org.apache.axiom.util.UIDGenerator; import org.apache.axis2.saaj.util.SAAJUtil; import org.apache.axis2.transport.http.HTTPConstants; +import javax.mail.internet.ContentType; +import javax.mail.internet.ParseException; import javax.xml.soap.AttachmentPart; import javax.xml.soap.MimeHeader; import javax.xml.soap.MimeHeaders; @@ -73,13 +76,13 @@ public class SOAPMessageImpl extends SOA String contentType = null; String tmpContentType = ""; if (mimeHeaders != null) { - String contentTypes[] = mimeHeaders.getHeader(HTTPConstants.CONTENT_TYPE); + String contentTypes[] = mimeHeaders.getHeader(HTTPConstants.HEADER_CONTENT_TYPE); if (contentTypes != null && contentTypes.length > 0) { tmpContentType = contentTypes[0]; contentType = SAAJUtil.normalizeContentType(tmpContentType); } } - if ("multipart/related".equals(contentType)) { + if (HTTPConstants.MEDIA_TYPE_MULTIPART_RELATED.equals(contentType)) { try { Attachments attachments = new Attachments(inputstream, tmpContentType, false, "", ""); @@ -88,7 +91,7 @@ public class SOAPMessageImpl extends SOA // parts of the SOAP message package. We need to reconstruct them from // the available information. MimeHeaders soapPartHeaders = new MimeHeaders(); - soapPartHeaders.addHeader(HTTPConstants.CONTENT_TYPE, + soapPartHeaders.addHeader(HTTPConstants.HEADER_CONTENT_TYPE, attachments.getSOAPPartContentType()); String soapPartContentId = attachments.getSOAPPartContentID(); soapPartHeaders.addHeader("Content-ID", "<" + soapPartContentId + ">"); @@ -164,6 +167,7 @@ public class SOAPMessageImpl extends SOA */ public void removeAllAttachments() { attachmentParts.clear(); + saveRequired = true; } /** @@ -223,7 +227,8 @@ public class SOAPMessageImpl extends SOA public void addAttachmentPart(AttachmentPart attachmentPart) { if (attachmentPart != null) { attachmentParts.add(attachmentPart); - mimeHeaders.setHeader(HTTPConstants.CONTENT_TYPE, "multipart/related"); + mimeHeaders.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, "multipart/related"); + saveRequired = true; } } @@ -267,8 +272,70 @@ public class SOAPMessageImpl extends SOA * @throws SOAPException if there was a problem saving changes to this message. */ public void saveChanges() throws SOAPException { + try { + String contentTypeValue = getSingleHeaderValue(HTTPConstants.HEADER_CONTENT_TYPE); + ContentType contentType = null; + if (isEmptyString(contentTypeValue)) { + if (attachmentParts.size() > 0) { + contentTypeValue = HTTPConstants.MEDIA_TYPE_MULTIPART_RELATED; + } else { + contentTypeValue = getBaseType(); + } + } + contentType = new ContentType(contentTypeValue); + + //Use configures the baseType with multipart/related while no attachment exists or all the attachments are removed + if(contentType.getBaseType().equals(HTTPConstants.MEDIA_TYPE_MULTIPART_RELATED) && attachmentParts.size() == 0) { + contentType = new ContentType(getBaseType()); + } + + //If it is of multipart/related, initialize those required values in the content-type, including boundary etc. + if (contentType.getBaseType().equals(HTTPConstants.MEDIA_TYPE_MULTIPART_RELATED)) { + + //Configure boundary + String boundaryParam = contentType.getParameter("boundary"); + if (isEmptyString(boundaryParam)) { + contentType.setParameter("boundary", UIDGenerator.generateMimeBoundary()); + } + + //Configure start content id, always get it from soapPart in case it is changed + String soapPartContentId = soapPart.getContentId(); + if (isEmptyString(soapPartContentId)) { + soapPartContentId = "<" + UIDGenerator.generateContentId() + ">"; + soapPart.setContentId(soapPartContentId); + } + contentType.setParameter("start", soapPartContentId); + + //Configure contentId for each attachments + for(AttachmentPart attachmentPart : attachmentParts) { + if(isEmptyString(attachmentPart.getContentId())) { + attachmentPart.setContentId("<" + UIDGenerator.generateContentId() + ">"); + } + } + + //Configure type + contentType.setParameter("type", getBaseType()); + + //Configure charset + String soapPartContentTypeValue = getSingleHeaderValue(soapPart.getMimeHeader(HTTPConstants.HEADER_CONTENT_TYPE)); + ContentType soapPartContentType = null; + if (isEmptyString(soapPartContentTypeValue)) { + soapPartContentType = new ContentType(soapPartContentTypeValue); + } else { + soapPartContentType = new ContentType(getBaseType()); + } + setCharsetParameter(soapPartContentType); + } else { + //Configure charset + setCharsetParameter(contentType); + } + + mimeHeaders.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, contentType.toString()); + } catch (ParseException e) { + throw new SOAPException("Invalid Content Type Field in the Mime Message", e); + } + saveRequired = false; - // TODO not sure of the implementation } public void setSaveRequired() { @@ -299,23 +366,45 @@ public class SOAPMessageImpl extends SOA * @throws IOException if an I/O error occurs */ public void writeTo(OutputStream out) throws SOAPException, IOException { - try { + try { + saveChanges(); OMOutputFormat format = new OMOutputFormat(); String enc = (String)getProperty(CHARACTER_SET_ENCODING); format.setCharSetEncoding(enc != null ? enc : OMOutputFormat.DEFAULT_CHAR_SET_ENCODING); String writeXmlDecl = (String)getProperty(WRITE_XML_DECLARATION); if (writeXmlDecl == null || writeXmlDecl.equals("false")) { - //SAAJ default case doesn't send XML decl format.setIgnoreXMLDeclaration(true); } - - SOAPEnvelope envelope = ((SOAPEnvelopeImpl)soapPart.getEnvelope()).getOMEnvelope(); + + SOAPEnvelope envelope = ((SOAPEnvelopeImpl) soapPart.getEnvelope()).getOMEnvelope(); if (attachmentParts.isEmpty()) { envelope.serialize(out, format); } else { - format.setSOAP11(((SOAPEnvelopeImpl)soapPart.getEnvelope()).getOMFactory() - instanceof SOAP11Factory); + ContentType contentType = new ContentType(getSingleHeaderValue(HTTPConstants.HEADER_CONTENT_TYPE)); + String boundary = contentType.getParameter("boundary"); + if(isEmptyString(boundary)) { + boundary = UIDGenerator.generateMimeBoundary(); + contentType.setParameter("boundary", boundary); + } + format.setMimeBoundary(boundary); + + String rootContentId = soapPart.getContentId(); + if(isEmptyString(rootContentId)) { + rootContentId = "<" + UIDGenerator.generateContentId() + ">"; + soapPart.setContentId(rootContentId); + } + contentType.setParameter("start", rootContentId); + if ((rootContentId.indexOf("<") > -1) & (rootContentId.indexOf(">") > -1)) { + rootContentId = rootContentId.substring(1, (rootContentId.length() - 1)); + } + format.setRootContentId(rootContentId); + + format.setSOAP11(((SOAPEnvelopeImpl) soapPart.getEnvelope()).getOMFactory() instanceof SOAP11Factory); + + //Double save the content-type in case anything is updated + mimeHeaders.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, contentType.toString()); + OMMultipartWriter mpw = new OMMultipartWriter(out, format); OutputStream rootPartOutputStream = mpw.writeRootPart(); envelope.serialize(rootPartOutputStream); @@ -325,7 +414,8 @@ public class SOAPMessageImpl extends SOA } mpw.complete(); } - saveChanges(); + + saveRequired = true; } catch (Exception e) { throw new SOAPException(e); } @@ -453,6 +543,7 @@ public class SOAPMessageImpl extends SOA } attachmentParts.clear(); this.attachmentParts = newAttachmentParts; + saveRequired = true; } /** @@ -502,4 +593,45 @@ public class SOAPMessageImpl extends SOA } } } + + private boolean isEmptyString(String value) { + return value == null || value.length() == 0; + } + + private String getSingleHeaderValue(String[] values) { + return values != null && values.length > 0 ? values[0] : null; + } + + private String getSingleHeaderValue(String name) { + String[] values = mimeHeaders.getHeader(name); + if (values == null || values.length == 0) { + return null; + } else { + return values[0]; + } + } + + private String getBaseType() throws SOAPException { + boolean isSOAP12 = ((SOAPEnvelopeImpl) soapPart.getEnvelope()).getOMFactory() instanceof SOAP12Factory; + return isSOAP12 ? HTTPConstants.MEDIA_TYPE_APPLICATION_SOAP_XML : HTTPConstants.MEDIA_TYPE_TEXT_XML; + } + + /** + * If the charset is configured by CHARACTER_SET_ENCODING, set it in the contentPart always. + * If it has already been configured in the contentType, leave it there. + * UTF-8 is used as the default value. + * @param contentType + * @throws SOAPException + */ + private void setCharsetParameter(ContentType contentType) throws SOAPException{ + String charset = (String)getProperty(CHARACTER_SET_ENCODING); + if (!isEmptyString(charset)) { + contentType.setParameter("charset", charset); + } else { + charset = contentType.getParameter("charset"); + if(isEmptyString(charset)) { + contentType.setParameter("charset", "UTF-8"); + } + } + } } Modified: axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPPartImpl.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPPartImpl.java?rev=1136177&r1=1136176&r2=1136177&view=diff ============================================================================== --- axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPPartImpl.java (original) +++ axis/axis2/java/core/trunk/modules/saaj/src/org/apache/axis2/saaj/SOAPPartImpl.java Wed Jun 15 20:25:14 2011 @@ -89,7 +89,7 @@ public class SOAPPartImpl extends SOAPPa SOAPEnvelopeImpl soapEnvelope) { //setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, IDGenerator.generateID()); //setMimeHeader(HTTPConstants.HEADER_CONTENT_TYPE, "text/xml"); - this.mimeHeaders = parentSoapMsg.getMimeHeaders(); + this.mimeHeaders = SAAJUtil.copyMimeHeaders(parentSoapMsg.getMimeHeaders()); soapMessage = parentSoapMsg; envelope = soapEnvelope; document = soapEnvelope.getOwnerDocument(); @@ -122,7 +122,7 @@ public class SOAPPartImpl extends SOAPPa this.mimeHeaders.addHeader("Content-ID", IDGenerator.generateID()); this.mimeHeaders.addHeader("content-type", HTTPConstants.MEDIA_TYPE_APPLICATION_SOAP_XML); } else { - String contentTypes[] = mimeHeaders.getHeader(HTTPConstants.CONTENT_TYPE); + String contentTypes[] = mimeHeaders.getHeader(HTTPConstants.HEADER_CONTENT_TYPE); if (contentTypes != null && contentTypes.length > 0) { try { contentType = new ContentType(contentTypes[0]); Modified: axis/axis2/java/core/trunk/modules/saaj/test/org/apache/axis2/saaj/SOAPMessageTest.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/saaj/test/org/apache/axis2/saaj/SOAPMessageTest.java?rev=1136177&r1=1136176&r2=1136177&view=diff ============================================================================== --- axis/axis2/java/core/trunk/modules/saaj/test/org/apache/axis2/saaj/SOAPMessageTest.java (original) +++ axis/axis2/java/core/trunk/modules/saaj/test/org/apache/axis2/saaj/SOAPMessageTest.java Wed Jun 15 20:25:14 2011 @@ -21,11 +21,13 @@ package org.apache.axis2.saaj; import junit.framework.Assert; import org.apache.axis2.saaj.util.SAAJDataSource; +import org.apache.axis2.transport.http.HTTPConstants; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import javax.activation.DataHandler; +import javax.mail.internet.ContentType; import javax.xml.namespace.QName; import javax.xml.soap.AttachmentPart; import javax.xml.soap.MessageFactory; @@ -45,6 +47,8 @@ import javax.xml.soap.SOAPMessage; import javax.xml.soap.SOAPPart; import javax.xml.transform.stream.StreamSource; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; @@ -187,8 +191,7 @@ public class SOAPMessageTest extends Ass } } - // TODO: check why this fails with Sun's SAAJ implementation - @Test + @Validated @Test public void testGetContent() { try { MessageFactory fac = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL); @@ -201,7 +204,7 @@ public class SOAPMessageTest extends Ass AttachmentPart ap; InputStream inputStream = TestUtils.getTestFile("attach.xml"); - ap = msg.createAttachmentPart(inputStream, "text/xml"); + ap = msg.createAttachmentPart(new StreamSource(inputStream), "text/xml"); DataHandler dh = new DataHandler(new SAAJDataSource(inputStream, 1000, "text/xml", true)); @@ -229,6 +232,67 @@ public class SOAPMessageTest extends Ass } } + @Validated @Test + public void testContentTypeGeneration() throws Exception{ + MessageFactory fac = MessageFactory.newInstance(); + SOAPMessage msg = fac.createMessage(); + InputStream inputStream = TestUtils.getTestFile("attach.xml"); + AttachmentPart ap = msg.createAttachmentPart(new StreamSource(inputStream), "text/xml"); + msg.addAttachmentPart(ap); + msg.saveChanges(); + assertNotNull(msg.getMimeHeaders().getHeader(HTTPConstants.HEADER_CONTENT_TYPE)); + String contentTypeValue = msg.getMimeHeaders().getHeader(HTTPConstants.HEADER_CONTENT_TYPE)[0]; + ContentType contentType = new ContentType(contentTypeValue); + assertNotNull("boundary parameter should exist in the content-type header", contentType.getParameter("boundary")); + //start parameter is not checked, due to it is optional parameter, and seems RI will not add this value + //assertNotNull("start parameter should exist in the content-type header", contentType.getParameter("start")); + assertNotNull("type parameter should exist in the content-type header", contentType.getParameter("type")); + assertEquals(HTTPConstants.MEDIA_TYPE_MULTIPART_RELATED, contentType.getBaseType()); + } + + @Validated @Test + public void testCreateMessageWithMimeHeaders() throws Exception{ + MessageFactory fac = MessageFactory.newInstance(); + SOAPMessage msg = fac.createMessage(); + InputStream inputStream = TestUtils.getTestFile("attach.xml"); + AttachmentPart ap = msg.createAttachmentPart(new StreamSource(inputStream), "text/xml"); + msg.addAttachmentPart(ap); + msg.saveChanges(); + ContentType contentType = new ContentType(msg.getMimeHeaders().getHeader(HTTPConstants.HEADER_CONTENT_TYPE)[0]); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + msg.writeTo(out); + SOAPMessage msg2 = fac.createMessage(msg.getMimeHeaders(), new ByteArrayInputStream(out.toByteArray())); + msg2.saveChanges(); + ContentType contentType2 = new ContentType(msg2.getMimeHeaders().getHeader(HTTPConstants.HEADER_CONTENT_TYPE)[0]); + + assertEquals(contentType.getBaseType(), contentType2.getBaseType()); + assertEquals(contentType.getParameter("boundary"), contentType2.getParameter("boundary")); + assertEquals(contentType.getParameter("type"), contentType2.getParameter("type")); + //start parameter is not checked, due to it is an optional parameter, and seems RI will not add this value + //assertEquals(contentType.getParameter("start"), contentType2.getParameter("start")); + } + + @Validated @Test + public void testContentTypeUpdateWithAttachmentChanges() throws Exception{ + MessageFactory fac = MessageFactory.newInstance(); + SOAPMessage msg = fac.createMessage(); + InputStream inputStream = TestUtils.getTestFile("attach.xml"); + AttachmentPart ap = msg.createAttachmentPart(new StreamSource(inputStream), "text/xml"); + msg.addAttachmentPart(ap); + msg.saveChanges(); + + assertNotNull(msg.getMimeHeaders().getHeader(HTTPConstants.HEADER_CONTENT_TYPE)); + ContentType contentType = new ContentType(msg.getMimeHeaders().getHeader(HTTPConstants.HEADER_CONTENT_TYPE)[0]); + assertEquals(HTTPConstants.MEDIA_TYPE_MULTIPART_RELATED, contentType.getBaseType()); + + msg.removeAllAttachments(); + msg.saveChanges(); + + assertNotNull(msg.getMimeHeaders().getHeader(HTTPConstants.HEADER_CONTENT_TYPE)); + contentType = new ContentType(msg.getMimeHeaders().getHeader(HTTPConstants.HEADER_CONTENT_TYPE)[0]); + assertEquals("text/xml", contentType.getBaseType()); + } private StringBuffer copyToBuffer(InputStream inputStream) { if (inputStream == null) {