You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-commits@axis.apache.org by ve...@apache.org on 2011/08/10 23:17:16 UTC

svn commit: r1156372 [2/2] - in /axis/axis2/java/core/branches/1_6: ./ modules/addressing/src/org/apache/axis2/handlers/addressing/ modules/jaxws-integration/test-resources/wsdl/ modules/jaxws-integration/test/org/apache/axis2/jaxws/sample/ modules/jax...

Modified: axis/axis2/java/core/branches/1_6/modules/jaxws/test/org/apache/axis2/jaxws/client/dispatch/DynamicPortCachingTests.java
URL: http://svn.apache.org/viewvc/axis/axis2/java/core/branches/1_6/modules/jaxws/test/org/apache/axis2/jaxws/client/dispatch/DynamicPortCachingTests.java?rev=1156372&r1=1156371&r2=1156372&view=diff
==============================================================================
--- axis/axis2/java/core/branches/1_6/modules/jaxws/test/org/apache/axis2/jaxws/client/dispatch/DynamicPortCachingTests.java (original)
+++ axis/axis2/java/core/branches/1_6/modules/jaxws/test/org/apache/axis2/jaxws/client/dispatch/DynamicPortCachingTests.java Wed Aug 10 21:17:15 2011
@@ -19,26 +19,41 @@
 package org.apache.axis2.jaxws.client.dispatch;
 
 import junit.framework.TestCase;
+
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.description.Parameter;
+import org.apache.axis2.engine.AxisConfiguration;
+import org.apache.axis2.jaxws.Constants;
 import org.apache.axis2.jaxws.description.DescriptionTestUtils2;
+import org.apache.axis2.jaxws.description.EndpointDescription;
 import org.apache.axis2.jaxws.description.ServiceDescription;
+import org.apache.axis2.jaxws.description.builder.MDQConstants;
+import org.apache.axis2.jaxws.description.impl.ServiceDescriptionImpl;
 import org.apache.axis2.jaxws.spi.ClientMetadataTest;
 import org.apache.axis2.jaxws.spi.ServiceDelegate;
 
 import javax.xml.namespace.QName;
 import javax.xml.ws.Service;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 
 /**
  * Tests the caching and isolation of dynamic ports,i.e. those created with
  * Service.addPort(...).  Dynamic ports should
- * 1) Only be visible to services on which an addPort was done
+ * 1) Only be visible to the service instances on which an addPort was done
  * 2) Share instances of the description objects (e.g. AxisService) for ports
- * added to different instances of the same service that use the same client
- * configuration
- * 3) Identical ports on services using different client configuration should
- * not be shared 
+ * added to the same instance of a service
+ * 3) Different service instances of the same-named service should not share the 
+ * the list of added dynamic ports.  Even if the same named port is added to each
+ * service, they should not share metadata objects (e.g. EndpointDescription, AxisService) 
+ * 
+ * Also validate the property that enables the previous behavior that allowed
+ * sharing of dyamic ports across services on the same AxisConfiguration.  With
+ * that property enabled, Dynamic ports should
+ * 1) Be shared across all services on the AxisConfiguration based on the key
+ * (PortQName, BindingId, EndpointAddress).
  */
 public class DynamicPortCachingTests extends TestCase {
     static final String namespaceURI = "http://dispatch.client.jaxws.axis2.apache.org";
@@ -49,11 +64,149 @@ public class DynamicPortCachingTests ext
     static final String epr1 = null;
 
     /**
-     * Two different instances of the same service should share the same
-     * description information (e.g. AxisService) if the same port is added
-     * to both 
+     * Validate setting the property enables the old behavior, which is that dynamic ports are 
+     * shared across all services on an AxisConfiguration based on the key
+     * (PortQName, BindingId, EndpointAddress).  This test validates that two ports that share the
+     * same Service QName will be share the same dynamic port objects.
+     * 
+     * NOTE!!! This test exists for validating backwards compatability.  This behavior was NOT
+     * intended in the runtime, but since it existed that way, customers could be depending on it.
+     */
+    public void testSamePortsSameServiceName_AxisConfig_PropertyTrue() {
+        try {
+            ClientMetadataTest.installCachingFactory();
+            QName svcQN = new QName(namespaceURI, svcLocalPart);
+            
+            Service svc1 = Service.create(svcQN);
+            assertNotNull(svc1);
+            ServiceDelegate svcDlg1 = DescriptionTestUtils2.getServiceDelegate(svc1);
+            assertNotNull(svcDlg1);
+            ServiceDescription svcDesc1 = svcDlg1.getServiceDescription();
+            assertNotNull(svcDesc1);
+
+            // Set the property to revert the behavior.  Note that although we are passing ni 
+            // a particular service, the property is set on the AxisConfig shared by all 
+            // services.
+            setAxisConfigParameter(svc1, MDQConstants.SHARE_DYNAMIC_PORTS_ACROSS_SERVICES, "true");
+
+            Service svc2 = Service.create(svcQN);
+            assertNotNull(svc2);
+            ServiceDelegate svcDlg2 = DescriptionTestUtils2.getServiceDelegate(svc2);
+            assertNotNull(svcDlg2);
+            ServiceDescription svcDesc2 = svcDlg2.getServiceDescription();
+            assertNotNull(svcDesc2);
+            
+            assertNotSame("Service instances should not be the same", svc1, svc2);
+            assertNotSame("Service delegates should not be the same", svcDlg1, svcDlg2);
+            assertSame("Instance of ServiceDescription should be the same", svcDesc1, svcDesc2);
+            
+            // Add a port to 1st service, should not be visible under the 2nd service
+            svc1.addPort(new QName(namespaceURI, dynamicPort1),
+                         bindingID1,
+                         epr1);
+            assertEquals(1, getList(svc1.getPorts()).size());
+            assertEquals(0, getList(svc2.getPorts()).size());
+            
+            // Add the same port to 2nd service, should now have same ports and description
+            // objects
+            svc2.addPort(new QName(namespaceURI, dynamicPort1),
+                         bindingID1,
+                         epr1);
+            assertEquals(1, getList(svc1.getPorts()).size());
+            assertEquals(1, getList(svc2.getPorts()).size());
+            
+            // Make sure the EndpointDescription objects are shared.
+            Collection<?> epDesc1Collection = 
+                ((ServiceDescriptionImpl) svcDesc1).getDynamicEndpointDescriptions_AsCollection(svcDlg1);
+            Collection<?> epDesc2Collection = 
+                ((ServiceDescriptionImpl) svcDesc2).getDynamicEndpointDescriptions_AsCollection(svcDlg2);
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc1Collection.size());
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc2Collection.size());
+           
+            EndpointDescription epDesc1 = (EndpointDescription) epDesc1Collection.toArray()[0];
+            EndpointDescription epDesc2 = (EndpointDescription) epDesc2Collection.toArray()[0];
+            assertSame("EndpointDescriptions not shared", epDesc1, epDesc2);
+            
+        } finally {
+            ClientMetadataTest.restoreOriginalFactory();
+        }
+        
+    }
+    /**
+     * Validate setting the property enables the old behavior, which is that dynamic ports are 
+     * shared across all services on an AxisConfiguration based on the key
+     * (PortQName, BindingId, EndpointAddress).  This test validates that two ports that have
+     * different Service QNames will still share the same dynamic port objects.  
+     * 
+     * NOTE!!! This test exists for validating backwards compatability.  This behavior was NOT
+     * intended in the runtime, but since it existed that way, customers could be depending on it.
+     */
+    public void testSamePortsDifferentServiceName_AxisConfig_PropertyTrue() {
+        try {
+            ClientMetadataTest.installCachingFactory();
+            QName svcQN = new QName(namespaceURI, svcLocalPart);
+            
+            Service svc1 = Service.create(svcQN);
+            assertNotNull(svc1);
+            ServiceDelegate svcDlg1 = DescriptionTestUtils2.getServiceDelegate(svc1);
+            assertNotNull(svcDlg1);
+            ServiceDescription svcDesc1 = svcDlg1.getServiceDescription();
+            assertNotNull(svcDesc1);
+
+            // Set the property to revert the behavior.  Note that although we are passing ni 
+            // a particular service, the property is set on the AxisConfig shared by all 
+            // services.
+            setAxisConfigParameter(svc1, MDQConstants.SHARE_DYNAMIC_PORTS_ACROSS_SERVICES, "true");
+
+            QName svcQN2 = new QName(namespaceURI, svcLocalPart + "2");
+            Service svc2 = Service.create(svcQN2);
+            assertNotNull(svc2);
+            ServiceDelegate svcDlg2 = DescriptionTestUtils2.getServiceDelegate(svc2);
+            assertNotNull(svcDlg2);
+            ServiceDescription svcDesc2 = svcDlg2.getServiceDescription();
+            assertNotNull(svcDesc2);
+            
+            assertNotSame("Service instances should not be the same", svc1, svc2);
+            assertNotSame("Service delegates should not be the same", svcDlg1, svcDlg2);
+            assertNotSame("Instance of ServiceDescription should be the same", svcDesc1, svcDesc2);
+            
+            // Add a port to 1st service, should not be visible under the 2nd service
+            svc1.addPort(new QName(namespaceURI, dynamicPort1),
+                         bindingID1,
+                         epr1);
+            assertEquals(1, getList(svc1.getPorts()).size());
+            assertEquals(0, getList(svc2.getPorts()).size());
+            
+            // Add the same port to 2nd service, should now have same ports and description
+            // objects
+            svc2.addPort(new QName(namespaceURI, dynamicPort1),
+                         bindingID1,
+                         epr1);
+            assertEquals(1, getList(svc1.getPorts()).size());
+            assertEquals(1, getList(svc2.getPorts()).size());
+            
+            // Make sure the EndpointDescription objects are shared.
+            Collection<?> epDesc1Collection = 
+                ((ServiceDescriptionImpl) svcDesc1).getDynamicEndpointDescriptions_AsCollection(svcDlg1);
+            Collection<?> epDesc2Collection = 
+                ((ServiceDescriptionImpl) svcDesc2).getDynamicEndpointDescriptions_AsCollection(svcDlg2);
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc1Collection.size());
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc2Collection.size());
+           
+            EndpointDescription epDesc1 = (EndpointDescription) epDesc1Collection.toArray()[0];
+            EndpointDescription epDesc2 = (EndpointDescription) epDesc2Collection.toArray()[0];
+            assertSame("EndpointDescriptions not shared", epDesc1, epDesc2);
+            
+        } finally {
+            ClientMetadataTest.restoreOriginalFactory();
+        }
+        
+    }
+    /**
+     * Validate that without the property set to revert the behavior, the default is that the ports are
+     * NOT shared across different instances of services with the same name 
      */
-    public void _testSamePortsSameService() {
+    public void testSamePortsSameServiceNameDifferentInstances() {
         try {
             ClientMetadataTest.installCachingFactory();
             QName svcQN = new QName(namespaceURI, svcLocalPart);
@@ -91,8 +244,122 @@ public class DynamicPortCachingTests ext
             assertEquals(1, getList(svc1.getPorts()).size());
             assertEquals(1, getList(svc2.getPorts()).size());
             
+            // Make sure the EndpointDescription objects are not shared.
+            Collection<?> epDesc1Collection = 
+                ((ServiceDescriptionImpl) svcDesc1).getDynamicEndpointDescriptions_AsCollection(svcDlg1);
+            Collection<?> epDesc2Collection = 
+                ((ServiceDescriptionImpl) svcDesc2).getDynamicEndpointDescriptions_AsCollection(svcDlg2);
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc1Collection.size());
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc2Collection.size());
+           
+            EndpointDescription epDesc1 = (EndpointDescription) epDesc1Collection.toArray()[0];
+            EndpointDescription epDesc2 = (EndpointDescription) epDesc2Collection.toArray()[0];
+            assertNotSame("EndpointDescriptions not shared", epDesc1, epDesc2);
             
+        } finally {
+            ClientMetadataTest.restoreOriginalFactory();
+        }
+        
+    }
+    
+    /**
+     * Validate that adding the same dynamic port to the same service instance re-uses the same
+     * description objects (e.g. EndpointDescription) 
+     */
+    public void testSamePortsSameServiceInstance() {
+        try {
+            ClientMetadataTest.installCachingFactory();
+            QName svcQN = new QName(namespaceURI, svcLocalPart);
+            
+            Service svc1 = Service.create(svcQN);
+            assertNotNull(svc1);
+            ServiceDelegate svcDlg1 = DescriptionTestUtils2.getServiceDelegate(svc1);
+            assertNotNull(svcDlg1);
+            ServiceDescription svcDesc1 = svcDlg1.getServiceDescription();
+            assertNotNull(svcDesc1);
+
+            // Add a port to service, save off the metadata to validate later
+            svc1.addPort(new QName(namespaceURI, dynamicPort1),
+                         bindingID1,
+                         epr1);
+            assertEquals(1, getList(svc1.getPorts()).size());
+            Collection<?> epDesc1Collection = 
+                ((ServiceDescriptionImpl) svcDesc1).getDynamicEndpointDescriptions_AsCollection(svcDlg1);
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc1Collection.size());
+            EndpointDescription epDescFirstAddPort = (EndpointDescription) epDesc1Collection.toArray()[0];
+
+            // Add the same port to the same service instance, should use the same description objects
+            svc1.addPort(new QName(namespaceURI, dynamicPort1),
+                         bindingID1,
+                         epr1);
+            assertEquals(1, getList(svc1.getPorts()).size());
             
+            // Make sure the EndpointDescription object is reused for second port.
+            Collection<?> epDesc2Collection = 
+                ((ServiceDescriptionImpl) svcDesc1).getDynamicEndpointDescriptions_AsCollection(svcDlg1);
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc2Collection.size());
+           
+            EndpointDescription epDescSecondAddPort = (EndpointDescription) epDesc1Collection.toArray()[0];
+            assertSame("EndpointDescriptions not reused", epDescFirstAddPort, epDescSecondAddPort);
+            
+        } finally {
+            ClientMetadataTest.restoreOriginalFactory();
+        }
+        
+    }
+    /**
+     * Validate that ports added to services with different service names (and thus different service instances)
+     * are not shared.
+     */
+    public void testSamePortsDifferentServiceNames() {
+        try {
+            ClientMetadataTest.installCachingFactory();
+            QName svcQN = new QName(namespaceURI, svcLocalPart);
+            Service svc1 = Service.create(svcQN);
+            assertNotNull(svc1);
+            ServiceDelegate svcDlg1 = DescriptionTestUtils2.getServiceDelegate(svc1);
+            assertNotNull(svcDlg1);
+            ServiceDescription svcDesc1 = svcDlg1.getServiceDescription();
+            assertNotNull(svcDesc1);
+
+            QName svcQN2 = new QName(namespaceURI, svcLocalPart + "2");
+            Service svc2 = Service.create(svcQN2);
+            assertNotNull(svc2);
+            ServiceDelegate svcDlg2 = DescriptionTestUtils2.getServiceDelegate(svc2);
+            assertNotNull(svcDlg2);
+            ServiceDescription svcDesc2 = svcDlg2.getServiceDescription();
+            assertNotNull(svcDesc2);
+            
+            assertNotSame("Service instances should not be the same", svc1, svc2);
+            assertNotSame("Service delegates should not be the same", svcDlg1, svcDlg2);
+            assertNotSame("Instance of ServiceDescription should not be the same", svcDesc1, svcDesc2);
+            
+            // Add a port to 1st service, should not be visible under the 2nd service
+            svc1.addPort(new QName(namespaceURI, dynamicPort1),
+                         bindingID1,
+                         epr1);
+            assertEquals(1, getList(svc1.getPorts()).size());
+            assertEquals(0, getList(svc2.getPorts()).size());
+            
+            // Add the same port to 2nd service, should now have same ports and description
+            // objects
+            svc2.addPort(new QName(namespaceURI, dynamicPort1),
+                         bindingID1,
+                         epr1);
+            assertEquals(1, getList(svc1.getPorts()).size());
+            assertEquals(1, getList(svc2.getPorts()).size());
+            
+            // Make sure the EndpointDescription objects are NOT shared.
+            Collection<?> epDesc1Collection = 
+                ((ServiceDescriptionImpl) svcDesc1).getDynamicEndpointDescriptions_AsCollection(svcDlg1);
+            Collection<?> epDesc2Collection = 
+                ((ServiceDescriptionImpl) svcDesc2).getDynamicEndpointDescriptions_AsCollection(svcDlg2);
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc1Collection.size());
+            assertEquals("Wrong number of dynamic endpoints", 1, epDesc2Collection.size());
+           
+            EndpointDescription epDesc1 = (EndpointDescription) epDesc1Collection.toArray().clone()[0];
+            EndpointDescription epDesc2 = (EndpointDescription) epDesc2Collection.toArray().clone()[0];
+            assertNotSame("EndpointDescriptions should not be shared across different services", epDesc1, epDesc2);
             
         } finally {
             ClientMetadataTest.restoreOriginalFactory();
@@ -124,4 +391,17 @@ public class DynamicPortCachingTests ext
         }
         return returnList;
     }
+    
+    private void setAxisConfigParameter(Service service, String key, String value) {
+        ServiceDelegate delegate = DescriptionTestUtils2.getServiceDelegate(service);
+        ServiceDescription svcDesc = delegate.getServiceDescription();
+        AxisConfiguration axisConfig = svcDesc.getAxisConfigContext().getAxisConfiguration();
+        Parameter parameter = new Parameter(key, value);
+        try {
+            axisConfig.addParameter(parameter);
+        } catch (AxisFault e) {
+            fail("Unable to set Parameter on AxisConfig due to exception " + e);
+        }
+    }
+
 }
\ No newline at end of file

Modified: axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/ExceptionFactory.java
URL: http://svn.apache.org/viewvc/axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/ExceptionFactory.java?rev=1156372&r1=1156371&r2=1156372&view=diff
==============================================================================
--- axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/ExceptionFactory.java (original)
+++ axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/ExceptionFactory.java Wed Aug 10 21:17:15 2011
@@ -323,6 +323,27 @@ public class ExceptionFactory {
         pw.close();
         return sw.getBuffer().toString();
     }
-
+    
+    /**
+     * Give a target Throwable, set the initialCause Throwable as the initial cause on the target. 
+     * @param target The throwable on which to set the initial cause
+     * @param initialCause The initial cause to set on the target Throwable.
+     */
+    public static void setInitialCause(Throwable target, Throwable initialCause) {
+        if (target != null && initialCause != null) {
+            // Create a WebServiceException from the initialCause throwable.  This skips over things like
+            // AxisFault as a cause.  Set the result on the target throwable.
+            try {
+                WebServiceException localException = ExceptionFactory.makeWebServiceException(initialCause);
+                target.initCause(localException.getCause());
+            } catch (Throwable t) {
+                // If the cause had already been set, then it can't be set again; it throws an exception.
+                if (log.isDebugEnabled()) {
+                    log.debug("Caught exception trying to set initial cause on: " + target +".  Initial cause: " +
+                            initialCause + ".  Caught: " + t);
+                }
+            }
+        }
+    }
 }
 

Modified: axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/description/builder/MDQConstants.java
URL: http://svn.apache.org/viewvc/axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/description/builder/MDQConstants.java?rev=1156372&r1=1156371&r2=1156372&view=diff
==============================================================================
--- axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/description/builder/MDQConstants.java (original)
+++ axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/description/builder/MDQConstants.java Wed Aug 10 21:17:15 2011
@@ -98,4 +98,24 @@ public class MDQConstants {
     public static final String SUN_WEB_METHOD_BEHAVIOR_CHANGE_VERSION = "2.1.6";
     public static final String USE_POST_WEB_METHOD_RULES = "jaxws.runtime.usePostWebMethodRules";
 
+    /** 
+     * Context Property:
+     * Name: jaxws.share.dynamic.ports.enable
+     * Value: String "false" or "true"
+     * Default: null, which is interpreted as "false"
+     * Can be set on:
+     * - Axis Configuration, which indicates that dynamic ports should be shared across services based on 
+     * a key of (PortQName, BindingId, EndpointAddress)
+     *   
+     * Dynamic ports, which are those created by Service.addPort(...), should only be visible to the instance
+     * of the service that did the addPort.  However, for backwards compatibility, this flag can be used
+     * to enable the sharing of dynamic ports across all services on an AxisConfiguration based on the key
+     * (PortQName, BindingId, EndpointAddress).
+     * 
+     * The default setting of this property is null, which is interpreted as "false", which will scope the
+     * visibility of dynamic ports to the instance of the service that did the addPort().
+     */
+    public static final String  SHARE_DYNAMIC_PORTS_ACROSS_SERVICES = "jaxws.share.dynamic.ports.enable"; 
+
+
 }

Modified: axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/description/impl/ServiceDescriptionImpl.java
URL: http://svn.apache.org/viewvc/axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/description/impl/ServiceDescriptionImpl.java?rev=1156372&r1=1156371&r2=1156372&view=diff
==============================================================================
--- axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/description/impl/ServiceDescriptionImpl.java (original)
+++ axis/axis2/java/core/branches/1_6/modules/metadata/src/org/apache/axis2/jaxws/description/impl/ServiceDescriptionImpl.java Wed Aug 10 21:17:15 2011
@@ -146,6 +146,14 @@ public class ServiceDescriptionImpl
     // RUNTIME INFORMATION
     Map<String, ServiceRuntimeDescription> runtimeDescMap =
             new ConcurrentHashMap<String, ServiceRuntimeDescription>();
+    
+    /**
+     * Property representing a collection of endpointDescription instances for dynamic ports 
+     * which are shared across all services on an AxisConfig.  Note that this behavior is incorrect; dynamic 
+     * ports should only be visible to the instance of the service that created them.  However, the sharing
+     * across services is maintained for backwards compatibility.
+     * @deprecated 
+     */
     private static final String JAXWS_DYNAMIC_ENDPOINTS = "jaxws.dynamic.endpoints";
 
     /**
@@ -491,7 +499,7 @@ public class ServiceDescriptionImpl
                                  Messages.getMessage("serviceDescriptionImplAddPortErr"));
                     }
 
-                    endpointDescription = createEndpointDescriptionImpl(sei, portQName, bindingId, endpointAddress);    
+                    endpointDescription = getDynamicEndpointDescriptionImpl(sei, portQName, bindingId, endpointAddress);    
                     addDynamicEndpointDescriptionImpl(endpointDescription, serviceDelegateKey);
 
                 } else {
@@ -596,19 +604,104 @@ public class ServiceDescriptionImpl
         return endpointDescription;
     }
 
-    private EndpointDescriptionImpl createEndpointDescriptionImpl(Class sei, QName portQName, String bindingId, String endpointAddress) {
+    /**
+     * Find or create an EndpointDescription instance for a dynamic port.  Dynamic ports should be scoped to
+     * the instance of the service that created them, so the method getDynamicEndpointDescriptionImpl should
+     * have find one if it already exists.  However, logic was introduced (in Apache Axis2 revision 664411) that
+     * also scoped dynamic ports to a configuration context based on port name, binding ID, and endpointAddress.
+     * Although that logic is incorrect, it is maintained for backwards compatability based on the setting of
+     * a property.
+     * 
+     * @param sei
+     * @param portQName
+     * @param bindingId
+     * @param endpointAddress
+     * @return
+     */
+    private EndpointDescriptionImpl getDynamicEndpointDescriptionImpl(Class sei, QName portQName, String bindingId, String endpointAddress) {
         if (log.isDebugEnabled()) {
             log.debug("Calling createEndpointDescriptionImpl : ("
                       + portQName + "," + bindingId + "," + endpointAddress + ")");
         }
         EndpointDescriptionImpl endpointDescription = null;
+        SharedDynamicEndpointEntry sharedDynamicEndpointEntry = null;
+        boolean areDynamicPortsShared = isShareDynamicPortsAcrossServicesEnabled();
+        if (areDynamicPortsShared) {
+            // If ports are being shared, see if there's already one in the cache.  Note that this will
+            // always return an Entry value, but the endpoint in it may be null if one wasn't found.
+            sharedDynamicEndpointEntry = findSharedDynamicEndpoint(portQName, bindingId, endpointAddress);
+            endpointDescription = sharedDynamicEndpointEntry.endpointDescription;
+        }
+        
+        boolean endpointCreated = false;
+        if(endpointDescription == null) {
+            endpointDescription = new EndpointDescriptionImpl(sei, portQName, true, this);
+            endpointCreated = true;
+        }
+        if (areDynamicPortsShared && endpointCreated) {
+            // If ports are being shared and a new endpoint was created, then it needs to be 
+            // added to the cache.  Note that the other values in the entry (the cache hashmap and
+            // the key) were set when we looked for the entry above.
+            sharedDynamicEndpointEntry.endpointDescription = endpointDescription;
+            cacheSharedDynamicPointEndpoint(sharedDynamicEndpointEntry);
+        }
+        return endpointDescription;
+    }
+    
+    /**
+     * Add the EndpointDescriptionImpl representing the dynamic port to the cache of ports shared across 
+     * services.   
+     * @param sharedDynamicEndpointEntry Contains the EndpointDescriptionImpl instance and the key 
+     *  associated with to be added to the cache, which is also contained in the entry.
+     */
+    private void cacheSharedDynamicPointEndpoint(SharedDynamicEndpointEntry sharedDynamicEndpointEntry) {
+        
+        HashMap cachedDescriptions = sharedDynamicEndpointEntry.cachedDescriptions;
+        String key = sharedDynamicEndpointEntry.key;
+        EndpointDescriptionImpl endpointDescription = sharedDynamicEndpointEntry.endpointDescription;
+        
+        synchronized(cachedDescriptions) {
+            if (log.isDebugEnabled()) {
+                log.debug("Calling cachedDescriptions.put : ("
+                        + key.toString() + ") : size - " + cachedDescriptions.size());
+            }
+            cachedDescriptions.put(key.toString(), new WeakReference(endpointDescription));
+        }
+    }
+    
+    /**
+     * Look for an existing shared endpointDescriptionImpl instance corresponding to a dynamic port based on
+     * a key of(portQname, bindingId, endpointAddress).  Note that sharing dynamic ports across services is
+     * disable by default, and must be enabled via a property.  Dynamic ports, by default, should only be 
+     * visible to the instance of the service that added them.
+     * 
+     * Note that a SharedDynamicEntry will be returned whether or not an existing EndpointDescriptionImpl
+     * is found.  If one is not found, then one needs to be created and then added to the cache.  The information
+     * in the returned SharedDynamicEntry can be used to add it.
+     * 
+     * @see #isShareDynamicPortsAcrossServicesEnabled()
+     * @see #cacheSharedDynamicPointEndpoint(SharedDynamicEndpointEntry)
+     * @see SharedDynamicEndpointEntry
+     * @see #getDynamicEndpointDescriptionImpl(QName, Object)
+     * 
+     * @param portQName
+     * @param bindingId
+     * @param endpointAddress
+     * @return A non-null SharedDynamicEndpointEntry is always returned.  The key and cachedDescriptions
+     * variables will always be set.  If an existing endpoint is found, then endpointDescription will be non-null.
+     */
+    private SharedDynamicEndpointEntry findSharedDynamicEndpoint(QName portQName,
+            String bindingId, String endpointAddress) {
+        
+        SharedDynamicEndpointEntry returnDynamicEntry = new SharedDynamicEndpointEntry();
+        EndpointDescriptionImpl sharedDynamicEndpoint = null;
+        
         AxisConfiguration configuration = configContext.getAxisConfiguration();
         if (log.isDebugEnabled()) {
             log.debug("looking for " + JAXWS_DYNAMIC_ENDPOINTS + " in AxisConfiguration : " + configuration);
         }
         Parameter parameter = configuration.getParameter(JAXWS_DYNAMIC_ENDPOINTS);
-        HashMap cachedDescriptions = (HashMap)
-                ((parameter == null) ? null : parameter.getValue());
+        HashMap cachedDescriptions = (HashMap)((parameter == null) ? null : parameter.getValue());
         if(cachedDescriptions == null) {
             cachedDescriptions = new HashMap();
             try {
@@ -622,10 +715,10 @@ public class ServiceDescriptionImpl
         } else {
             if (log.isDebugEnabled()) {
                 log.debug("found old jaxws.dynamic.endpoints cache in AxisConfiguration ("  +  cachedDescriptions + ") with size : ("
-                          + cachedDescriptions.size() + ")");
+                        + cachedDescriptions.size() + ")");
             }
         }
-
+        
         StringBuffer key = new StringBuffer();
         key.append(portQName == null ? "NULL" : portQName.toString());
         key.append(':');
@@ -635,27 +728,50 @@ public class ServiceDescriptionImpl
         synchronized(cachedDescriptions) {
             WeakReference ref = (WeakReference) cachedDescriptions.get(key.toString());
             if (ref != null) {
-                endpointDescription = (EndpointDescriptionImpl) ref.get();
-            }
-        }
-        if(endpointDescription == null) {
-            endpointDescription = new EndpointDescriptionImpl(sei, portQName, true, this);
-            synchronized(cachedDescriptions) {
+                sharedDynamicEndpoint = (EndpointDescriptionImpl) ref.get();
                 if (log.isDebugEnabled()) {
-                    log.debug("Calling cachedDescriptions.put : ("
-                              + key.toString() + ") : size - " + cachedDescriptions.size());
+                    log.debug("found old entry for endpointDescription in jaxws.dynamic.endpoints cache : ("
+                            + cachedDescriptions.size() + ")");
                 }
-                cachedDescriptions.put(key.toString(), new WeakReference(endpointDescription));
             }
-        } else {
-            if (log.isDebugEnabled()) {
-                log.debug("found old entry for endpointDescription in jaxws.dynamic.endpoints cache : ("
-                          + cachedDescriptions.size() + ")");
+        }
+        
+        returnDynamicEntry.cachedDescriptions = cachedDescriptions;
+        returnDynamicEntry.key = key.toString();
+        returnDynamicEntry.endpointDescription = sharedDynamicEndpoint;
+        
+        return returnDynamicEntry;
+    }
+
+    /**
+     * Answer if dynamic ports are to be shared across services.  The default value is FALSE, but it
+     * can be configured by an AxisConfig property.  Note that this can only be set via a config property 
+     * and not via a request context property since the setting affects multiple services.
+     * 
+     * @return true if dynamic ports are to be shared across services, false otherwise.
+     */
+    private boolean isShareDynamicPortsAcrossServicesEnabled() {
+        boolean resolutionEnabled = false;
+
+        // See if an  AxisConfig property enables the sharing of dynamic ports 
+        String flagValue = null;
+        AxisConfiguration axisConfig = getAxisConfigContext().getAxisConfiguration();
+        Parameter parameter = axisConfig.getParameter(MDQConstants.SHARE_DYNAMIC_PORTS_ACROSS_SERVICES);
+        if (parameter != null) {
+            flagValue = (String) parameter.getValue();
+        }
+
+        // If the property was set, check the value.
+        if (flagValue != null) {
+            if ("false".equalsIgnoreCase(flagValue)) {
+                resolutionEnabled = false;
+            } else if ("true".equalsIgnoreCase(flagValue)) {
+                resolutionEnabled = true;
             }
         }
-        return endpointDescription;
+        return resolutionEnabled;
     }
-    
+
     /**
      * This method will get all properties that have been set on the DescriptionBuilderComposite
      * instance. If the DBC represents an implementation class that references an SEI, the
@@ -3039,4 +3155,13 @@ public class ServiceDescriptionImpl
         return responses;
     }
 
+    /**
+     * Entry returned from looking for an endpointDescriptionImpl for a shared dynamic port, or to be used
+     * to add one that was created to the cache.
+     */
+    class SharedDynamicEndpointEntry {
+        String key;
+        EndpointDescriptionImpl endpointDescription;
+        HashMap cachedDescriptions;
+    }
 }

Modified: axis/axis2/java/core/branches/1_6/modules/saaj/src/org/apache/axis2/saaj/SOAPMessageImpl.java
URL: http://svn.apache.org/viewvc/axis/axis2/java/core/branches/1_6/modules/saaj/src/org/apache/axis2/saaj/SOAPMessageImpl.java?rev=1156372&r1=1156371&r2=1156372&view=diff
==============================================================================
--- axis/axis2/java/core/branches/1_6/modules/saaj/src/org/apache/axis2/saaj/SOAPMessageImpl.java (original)
+++ axis/axis2/java/core/branches/1_6/modules/saaj/src/org/apache/axis2/saaj/SOAPMessageImpl.java Wed Aug 10 21:17:15 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/branches/1_6/modules/saaj/src/org/apache/axis2/saaj/SOAPPartImpl.java
URL: http://svn.apache.org/viewvc/axis/axis2/java/core/branches/1_6/modules/saaj/src/org/apache/axis2/saaj/SOAPPartImpl.java?rev=1156372&r1=1156371&r2=1156372&view=diff
==============================================================================
--- axis/axis2/java/core/branches/1_6/modules/saaj/src/org/apache/axis2/saaj/SOAPPartImpl.java (original)
+++ axis/axis2/java/core/branches/1_6/modules/saaj/src/org/apache/axis2/saaj/SOAPPartImpl.java Wed Aug 10 21:17:15 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/branches/1_6/modules/saaj/test/org/apache/axis2/saaj/SOAPMessageTest.java
URL: http://svn.apache.org/viewvc/axis/axis2/java/core/branches/1_6/modules/saaj/test/org/apache/axis2/saaj/SOAPMessageTest.java?rev=1156372&r1=1156371&r2=1156372&view=diff
==============================================================================
--- axis/axis2/java/core/branches/1_6/modules/saaj/test/org/apache/axis2/saaj/SOAPMessageTest.java (original)
+++ axis/axis2/java/core/branches/1_6/modules/saaj/test/org/apache/axis2/saaj/SOAPMessageTest.java Wed Aug 10 21:17:15 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) {