You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by ds...@apache.org on 2014/02/10 11:08:35 UTC

svn commit: r1566555 [2/2] - in /cxf/trunk: core/src/main/java/org/apache/cxf/interceptor/ core/src/main/java/org/apache/cxf/io/ rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/ rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/feature/ rt/ws/rm/src/main/java/or...

Copied: cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInInterceptor.java (from r1566482, cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptor.java)
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInInterceptor.java?p2=cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInInterceptor.java&p1=cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptor.java&r1=1566482&r2=1566555&rev=1566555&view=diff
==============================================================================
--- cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInterceptor.java (original)
+++ cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapInInterceptor.java Mon Feb 10 10:08:34 2014
@@ -19,7 +19,6 @@
 
 package org.apache.cxf.ws.rm.soap;
 
-import java.net.HttpURLConnection;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
@@ -34,14 +33,11 @@ import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.namespace.QName;
 
-import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
 import org.apache.cxf.binding.Binding;
-import org.apache.cxf.binding.soap.Soap11;
 import org.apache.cxf.binding.soap.SoapFault;
-import org.apache.cxf.binding.soap.SoapHeader;
 import org.apache.cxf.binding.soap.SoapMessage;
 import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
 import org.apache.cxf.common.logging.LogUtils;
@@ -52,7 +48,6 @@ import org.apache.cxf.interceptor.Interc
 import org.apache.cxf.interceptor.InterceptorChain;
 import org.apache.cxf.message.Exchange;
 import org.apache.cxf.message.Message;
-import org.apache.cxf.message.MessageUtils;
 import org.apache.cxf.phase.Phase;
 import org.apache.cxf.phase.PhaseInterceptor;
 import org.apache.cxf.service.Service;
@@ -76,7 +71,6 @@ import org.apache.cxf.ws.rm.RMException;
 import org.apache.cxf.ws.rm.RMManager;
 import org.apache.cxf.ws.rm.RMMessageConstants;
 import org.apache.cxf.ws.rm.RMProperties;
-import org.apache.cxf.ws.rm.SequenceFault;
 import org.apache.cxf.ws.rm.v200702.AckRequestedType;
 import org.apache.cxf.ws.rm.v200702.SequenceAcknowledgement;
 import org.apache.cxf.wsdl.interceptors.BareInInterceptor;
@@ -85,7 +79,7 @@ import org.apache.cxf.wsdl.interceptors.
  * Protocol Handler responsible for {en|de}coding the RM 
  * Properties for {outgo|incom}ing messages.
  */
-public class RMSoapInterceptor extends AbstractSoapInterceptor {
+public class RMSoapInInterceptor extends AbstractSoapInterceptor {
 
     protected static JAXBContext jaxbContext;
     
@@ -97,12 +91,12 @@ public class RMSoapInterceptor extends A
         HEADERS = set;
     }
 
-    private static final Logger LOG = LogUtils.getL7dLogger(RMSoapInterceptor.class);
+    private static final Logger LOG = LogUtils.getL7dLogger(RMSoapInInterceptor.class);
     
     /**
      * Constructor.
      */
-    public RMSoapInterceptor() {
+    public RMSoapInInterceptor() {
         super(Phase.PRE_PROTOCOL);
         
         addAfter(MAPCodec.class.getName());
@@ -123,113 +117,8 @@ public class RMSoapInterceptor extends A
      * @see org.apache.cxf.interceptor.Interceptor#handleMessage(org.apache.cxf.message.Message)
      */
     public void handleMessage(SoapMessage message) throws Fault {
-        mediate(message);
-    }
-
-    /**
-     * Mediate message flow, peforming RMProperties {en|de}coding.
-     * 
-     * @param message the messsage
-     */ 
-    void mediate(SoapMessage message) {
-        if (MessageUtils.isOutbound(message)) {
-            encode(message);
-        } else {
-            decode(message);
-            updateServiceModelInfo(message);
-        }
-    }
-    
-    /**
-     * Encode the current RM properties in protocol-specific headers.
-     *
-     * @param message the SOAP message
-     */
-    void encode(SoapMessage message) {
-        RMProperties rmps = RMContextUtils.retrieveRMProperties(message, true);
-        if (null != rmps) {
-            encode(message, rmps);
-        } else if (MessageUtils.isFault(message)) {
-            Exception ex = message.getContent(Exception.class);
-            if (ex instanceof SoapFault && ex.getCause() instanceof SequenceFault) {
-                encodeFault(message, (SequenceFault)ex.getCause());
-            }
-        }
-        
-    }
-
-    /**
-     * Encode the current RM properties in protocol-specific headers.
-     *
-     * @param message the SOAP message.
-     * @param rmps the current RM properties.
-     */
-    public static void encode(SoapMessage message, RMProperties rmps) {
-        if (null == rmps) {
-            return;
-        }
-        LOG.log(Level.FINE, "encoding RMPs in SOAP headers");
-        
-        try {
-            
-            List<Header> headers = message.getHeaders();
-            discardRMHeaders(headers);
-            
-            AddressingProperties maps = RMContextUtils.retrieveMAPs(message, false, true);
-            ProtocolVariation protocol = ProtocolVariation.findVariant(rmps.getNamespaceURI(),
-                maps.getNamespaceURI());
-            Element header = protocol.getCodec().buildHeaders(rmps, Soap11.getInstance().getHeader());
-            Node node = header.getFirstChild();
-            if (node != null && MessageUtils.isPartialResponse(message)) {
-                // make sure the response is returned as HTTP 200 and not 202
-                message.put(Message.RESPONSE_CODE, HttpURLConnection.HTTP_OK);
-            }
-            while (node != null) {
-                Header holder = null;
-                if (node.getLocalName().equals("Sequence")) {
-                    holder = new SoapHeader(new QName(node.getNamespaceURI(), node.getLocalName()), node);
-                    ((SoapHeader)holder).setMustUnderstand(true);
-                } else {
-                    holder = new Header(new QName(node.getNamespaceURI(), node.getLocalName()), node);
-                }
-                headers.add(holder);
-                node = node.getNextSibling();
-            }
-
-        } catch (JAXBException je) {
-            LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
-        }        
-    }
-    
-    /**
-     * Encode the SequenceFault in protocol-specific header.
-     *
-     * @param message the SOAP message.
-     * @param sf the SequenceFault.
-     */
-    public static void encodeFault(SoapMessage message, SequenceFault sf) {
-        LOG.log(Level.FINE, "Encoding SequenceFault in SOAP header");
-        try {
-            List<Header> headers = message.getHeaders();
-            discardRMHeaders(headers);
-            Message inmsg = message.getExchange().getInMessage();
-            RMProperties rmps = RMContextUtils.retrieveRMProperties(inmsg, false);
-            AddressingProperties maps = RMContextUtils.retrieveMAPs(inmsg, false, false);
-            ProtocolVariation protocol = ProtocolVariation.findVariant(rmps.getNamespaceURI(),
-                maps.getNamespaceURI());
-            Element header = protocol.getCodec().buildHeaderFault(sf, Soap11.getInstance().getHeader());
-            Node node = header.getFirstChild();
-            if (node instanceof Element) {
-                Attr attr = header.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/",
-                    "xmlns:" + RMConstants.NAMESPACE_PREFIX);
-                attr.setValue(rmps.getNamespaceURI());
-                ((Element)node).setAttributeNodeNS(attr);
-            }
-            
-            headers.add(new Header(new QName(node.getNamespaceURI(), node.getLocalName()), node));
-        } catch (JAXBException je) {
-            LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
-        }        
+        decode(message);
+        updateServiceModelInfo(message);
     }
     
     /**
@@ -321,24 +210,6 @@ public class RMSoapInterceptor extends A
             LOG.log(Level.WARNING, "SOAP_HEADER_DECODE_FAILURE_MSG", ex); 
         }
     }
-
-    /**
-     * Discard any pre-existing RM headers - this may occur if the runtime
-     * re-uses a SOAP message.
-     *
-     * @param header the SOAP header element
-     */
-    private static void discardRMHeaders(List<Header> header) {
-        Iterator<Header> iter = header.iterator();
-        while (iter.hasNext()) {
-            Header hdr = iter.next();
-            String uri = hdr.getName().getNamespaceURI();
-            if (RM10Constants.NAMESPACE_URI.equals(uri)
-                || RM11Constants.NAMESPACE_URI.equals(uri)) {
-                iter.remove();
-            }
-        }
-    }
     
     /**
      * When invoked inbound, check if the action indicates that this is one of the 
@@ -434,7 +305,7 @@ public class RMSoapInterceptor extends A
         // In the logical RM interceptor set it back to what it was so that the logical
         // addressing interceptor does not try to send a partial response to 
         // server originated oneway RM protocol messages.        
-        // The actions that can appear in the response to the requestor should be excluded.
+        // 
         
         if (!consts.getCreateSequenceResponseAction().equals(action)
             && !consts.getSequenceAckAction().equals(action)

Added: cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapOutInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapOutInterceptor.java?rev=1566555&view=auto
==============================================================================
--- cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapOutInterceptor.java (added)
+++ cxf/trunk/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RMSoapOutInterceptor.java Mon Feb 10 10:08:34 2014
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.ws.rm.soap;
+
+import java.net.HttpURLConnection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.namespace.QName;
+import javax.xml.soap.SOAPException;
+import javax.xml.soap.SOAPMessage;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.apache.cxf.binding.soap.Soap11;
+import org.apache.cxf.binding.soap.SoapFault;
+import org.apache.cxf.binding.soap.SoapMessage;
+import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.headers.Header;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.ws.addressing.AddressingProperties;
+import org.apache.cxf.ws.rm.ProtocolVariation;
+import org.apache.cxf.ws.rm.RM10Constants;
+import org.apache.cxf.ws.rm.RM11Constants;
+import org.apache.cxf.ws.rm.RMConstants;
+import org.apache.cxf.ws.rm.RMContextUtils;
+import org.apache.cxf.ws.rm.RMOutInterceptor;
+import org.apache.cxf.ws.rm.RMProperties;
+import org.apache.cxf.ws.rm.SequenceFault;
+
+/**
+ * Protocol Handler responsible for {en|de}coding the RM 
+ * Properties for {outgo|incom}ing messages.
+ */
+public class RMSoapOutInterceptor extends AbstractSoapInterceptor {
+
+    protected static JAXBContext jaxbContext;
+    
+    private static final Set<QName> HEADERS;
+    static {
+        Set<QName> set = new HashSet<QName>();
+        set.addAll(RM10Constants.HEADERS);
+        set.addAll(RM11Constants.HEADERS);
+        HEADERS = set;
+    }
+
+    private static final Logger LOG = LogUtils.getL7dLogger(RMSoapOutInterceptor.class);
+    
+    /**
+     * Constructor.
+     */
+    public RMSoapOutInterceptor() {
+        super(Phase.POST_PROTOCOL);
+        
+        addAfter(RMOutInterceptor.class.getName());
+    } 
+    
+    // AbstractSoapInterceptor interface 
+    
+    /**
+     * @return the set of SOAP headers understood by this handler 
+     */
+    public Set<QName> getUnderstoodHeaders() {
+        return HEADERS;
+    }
+    
+    // Interceptor interface
+
+    /* (non-Javadoc)
+     * @see org.apache.cxf.interceptor.Interceptor#handleMessage(org.apache.cxf.message.Message)
+     */
+    public void handleMessage(SoapMessage message) throws Fault {
+        encode(message);
+    }
+    
+    /**
+     * Encode the current RM properties in protocol-specific headers.
+     *
+     * @param message the SOAP message
+     */
+    void encode(SoapMessage message) {
+        RMProperties rmps = RMContextUtils.retrieveRMProperties(message, true);
+        if (null != rmps) {
+            encode(message, rmps);
+        } else if (MessageUtils.isFault(message)) {
+            Exception ex = message.getContent(Exception.class);
+            if (ex instanceof SoapFault && ex.getCause() instanceof SequenceFault) {
+                encodeFault(message, (SequenceFault)ex.getCause());
+            }
+        }
+        
+    }
+
+    /**
+     * Encode the current RM properties in protocol-specific headers.
+     *
+     * @param message the SOAP message.
+     * @param rmps the current RM properties.
+     */
+    public static void encode(SoapMessage message, RMProperties rmps) {
+        if (null == rmps) {
+            return;
+        }
+        LOG.log(Level.FINE, "encoding RMPs in SOAP headers");
+        try {
+            
+            AddressingProperties maps = RMContextUtils.retrieveMAPs(message, false, true);
+            ProtocolVariation protocol = ProtocolVariation.findVariant(rmps.getNamespaceURI(),
+                maps.getNamespaceURI());
+            SOAPMessage content = message.getContent(SOAPMessage.class);
+            boolean added = protocol.getCodec().insertHeaders(rmps, content.getSOAPPart());
+            if (added && MessageUtils.isPartialResponse(message)) {
+                // make sure the response is returned as HTTP 200 and not 202
+                message.put(Message.RESPONSE_CODE, HttpURLConnection.HTTP_OK);
+            }
+            if (added) {
+                try {
+                    content.saveChanges();
+//                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+//                    content.writeTo(bos);
+//                    bos.close();
+//                    LOG.info("Message after headers added: " + bos.toString("UTF-8"));
+                } catch (SOAPException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+//                } catch (IOException e) {
+//                    // TODO Auto-generated catch block
+//                    e.printStackTrace();
+                }
+                
+            }
+
+        } catch (JAXBException je) {
+            LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
+        }        
+    }
+    
+    /**
+     * Encode the SequenceFault in protocol-specific header.
+     *
+     * @param message the SOAP message.
+     * @param sf the SequenceFault.
+     */
+    public static void encodeFault(SoapMessage message, SequenceFault sf) {
+        LOG.log(Level.FINE, "Encoding SequenceFault in SOAP header");
+        try {
+            List<Header> headers = message.getHeaders();
+            Message inmsg = message.getExchange().getInMessage();
+            RMProperties rmps = RMContextUtils.retrieveRMProperties(inmsg, false);
+            AddressingProperties maps = RMContextUtils.retrieveMAPs(inmsg, false, false);
+            ProtocolVariation protocol = ProtocolVariation.findVariant(rmps.getNamespaceURI(),
+                maps.getNamespaceURI());
+            Element header = protocol.getCodec().buildHeaderFault(sf, Soap11.getInstance().getHeader());
+            Node node = header.getFirstChild();
+            if (node instanceof Element) {
+                Attr attr = header.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/",
+                    "xmlns:" + RMConstants.NAMESPACE_PREFIX);
+                attr.setValue(rmps.getNamespaceURI());
+                ((Element)node).setAttributeNodeNS(attr);
+            }
+            
+            headers.add(new Header(new QName(node.getNamespaceURI(), node.getLocalName()), node));
+        } catch (JAXBException je) {
+            LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
+        }        
+    }
+}
\ No newline at end of file

Added: cxf/trunk/rt/ws/rm/src/test/java/org/apache/cxf/ws/rm/soap/RMSoapOutInterceptorTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/ws/rm/src/test/java/org/apache/cxf/ws/rm/soap/RMSoapOutInterceptorTest.java?rev=1566555&view=auto
==============================================================================
--- cxf/trunk/rt/ws/rm/src/test/java/org/apache/cxf/ws/rm/soap/RMSoapOutInterceptorTest.java (added)
+++ cxf/trunk/rt/ws/rm/src/test/java/org/apache/cxf/ws/rm/soap/RMSoapOutInterceptorTest.java Mon Feb 10 10:08:34 2014
@@ -0,0 +1,475 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.ws.rm.soap;
+
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.w3c.dom.Element;
+
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.binding.soap.SoapFault;
+import org.apache.cxf.binding.soap.SoapMessage;
+import org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor;
+import org.apache.cxf.binding.soap.interceptor.StartBodyInterceptor;
+import org.apache.cxf.headers.Header;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.ExchangeImpl;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.ws.addressing.AddressingProperties;
+import org.apache.cxf.ws.addressing.Names;
+import org.apache.cxf.ws.rm.RM10Constants;
+import org.apache.cxf.ws.rm.RMConstants;
+import org.apache.cxf.ws.rm.RMContextUtils;
+import org.apache.cxf.ws.rm.RMProperties;
+import org.apache.cxf.ws.rm.SequenceFault;
+import org.apache.cxf.ws.rm.v200702.AckRequestedType;
+import org.apache.cxf.ws.rm.v200702.Identifier;
+import org.apache.cxf.ws.rm.v200702.ObjectFactory;
+import org.apache.cxf.ws.rm.v200702.SequenceAcknowledgement;
+import org.apache.cxf.ws.rm.v200702.SequenceAcknowledgement.AcknowledgementRange;
+import org.apache.cxf.ws.rm.v200702.SequenceType;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RMSoapOutInterceptorTest extends Assert {
+
+    private static final String SEQ_IDENTIFIER = "http://Business456.com/RM/ABC";
+    private static final Long ONE = new Long(1);
+    private static final Long TEN = new Long(10);
+    private static final Long MSG1_MESSAGE_NUMBER = ONE;
+    private static final Long MSG2_MESSAGE_NUMBER = new Long(2);
+
+    private IMocksControl control;
+    
+    private SequenceType s1;
+    private SequenceType s2;
+    private SequenceAcknowledgement ack1;
+    private SequenceAcknowledgement ack2;
+    private AckRequestedType ar1;
+    private AckRequestedType ar2;
+    
+    @Before
+    public void setUp() {
+        control = EasyMock.createNiceControl(); 
+    }
+
+    @Test
+    public void testGetUnderstoodHeaders() throws Exception {
+        RMSoapOutInterceptor codec = new RMSoapOutInterceptor();
+        Set<QName> headers = codec.getUnderstoodHeaders();
+        assertTrue("expected Sequence header", headers.contains(RM10Constants.SEQUENCE_QNAME));
+        assertTrue("expected SequenceAcknowledgment header", 
+                   headers.contains(RM10Constants.SEQUENCE_ACK_QNAME));
+        assertTrue("expected AckRequested header", 
+                   headers.contains(RM10Constants.ACK_REQUESTED_QNAME));
+    }
+    
+    @Test
+    public void testHandleMessage() throws NoSuchMethodException {
+        Method m = RMSoapOutInterceptor.class.getDeclaredMethod("mediate", 
+            new Class[] {SoapMessage.class});
+        RMSoapOutInterceptor codec = 
+            EasyMock.createMockBuilder(RMSoapOutInterceptor.class)
+                .addMockedMethod(m).createMock(control);
+        SoapMessage msg = control.createMock(SoapMessage.class);
+        codec.mediate(msg);
+        EasyMock.expectLastCall();
+        
+        control.replay();
+        codec.handleMessage(msg);
+        control.verify();
+    }
+    
+    @Test
+    public void testMediate() throws NoSuchMethodException, XMLStreamException {
+        Method m1 = RMSoapOutInterceptor.class.getDeclaredMethod("encode", 
+                                                             new Class[] {SoapMessage.class});
+        Method m2 = RMSoapOutInterceptor.class.getDeclaredMethod("decode", 
+                                                              new Class[] {SoapMessage.class});
+        RMSoapOutInterceptor codec =
+            EasyMock.createMockBuilder(RMSoapOutInterceptor.class)
+                .addMockedMethods(m1, m2).createMock(control);
+        
+        SoapMessage msg = control.createMock(SoapMessage.class);
+        Exchange exchange = control.createMock(Exchange.class);
+        EasyMock.expect(msg.getExchange()).andReturn(exchange);
+        EasyMock.expect(exchange.getOutMessage()).andReturn(msg);
+        codec.encode(msg);
+        EasyMock.expectLastCall();
+        
+        control.replay();
+        codec.mediate(msg);
+        control.verify();
+                
+        control.reset();
+        EasyMock.expect(msg.getExchange()).andReturn(null);
+        codec.decode(msg);
+        EasyMock.expectLastCall();
+        
+        control.replay();
+        codec.mediate(msg);
+        control.verify();
+        
+    }
+
+    @Test
+    public void testEncode() throws Exception {
+        RMSoapOutInterceptor codec = new RMSoapOutInterceptor();
+        setUpOutbound();
+        SoapMessage message = setupOutboundMessage();
+
+        // no RM headers
+   
+        codec.handleMessage(message);
+        verifyHeaders(message, new String[] {});
+
+        // one sequence header
+
+        message = setupOutboundMessage();        
+        RMProperties rmps = RMContextUtils.retrieveRMProperties(message, true);     
+        rmps.setSequence(s1);
+        codec.encode(message);
+        verifyHeaders(message, new String[] {RMConstants.SEQUENCE_NAME});
+
+        // one acknowledgment header
+
+        message = setupOutboundMessage(); 
+        rmps = RMContextUtils.retrieveRMProperties(message, true);          
+        Collection<SequenceAcknowledgement> acks = new ArrayList<SequenceAcknowledgement>();
+        acks.add(ack1);
+        rmps.setAcks(acks);        
+        codec.encode(message);
+        verifyHeaders(message, new String[] {RMConstants.SEQUENCE_ACK_NAME});
+
+        // two acknowledgment headers
+
+        message = setupOutboundMessage();
+        rmps = RMContextUtils.retrieveRMProperties(message, true);        
+        acks.add(ack2);
+        rmps.setAcks(acks);
+        codec.encode(message);
+        verifyHeaders(message, new String[] {RMConstants.SEQUENCE_ACK_NAME, 
+                                             RMConstants.SEQUENCE_ACK_NAME});
+
+        // one ack requested header
+
+        message = setupOutboundMessage();
+        rmps = RMContextUtils.retrieveRMProperties(message, true);        
+        Collection<AckRequestedType> requested = new ArrayList<AckRequestedType>();
+        requested.add(ar1);
+        rmps.setAcksRequested(requested);
+        codec.encode(message);
+        verifyHeaders(message, new String[] {RMConstants.ACK_REQUESTED_NAME});
+
+        // two ack requested headers
+
+        message = setupOutboundMessage();
+        rmps = RMContextUtils.retrieveRMProperties(message, true);         
+        requested.add(ar2);
+        rmps.setAcksRequested(requested);
+        codec.encode(message);
+        verifyHeaders(message, new String[] {RMConstants.ACK_REQUESTED_NAME, 
+                                             RMConstants.ACK_REQUESTED_NAME});
+    }
+    
+    @Test
+    public void testEncodeFault() throws Exception {
+        RMSoapOutInterceptor codec = new RMSoapOutInterceptor();
+        setUpOutbound();
+        SoapMessage message = setupOutboundFaultMessage();
+
+        // no RM headers and no fault
+   
+        codec.encode(message);
+        verifyHeaders(message, new String[] {});
+
+        // fault is not a SoapFault
+
+        message = setupOutboundFaultMessage();
+        assertTrue(MessageUtils.isFault(message));
+        Exception ex = new RuntimeException("");
+        message.setContent(Exception.class, ex);      
+        codec.encode(message);
+        verifyHeaders(message, new String[] {});
+        
+        // fault is a SoapFault but does not have a SequenceFault cause
+
+        message = setupOutboundFaultMessage();
+        SoapFault f = new SoapFault("REASON", RM10Constants.UNKNOWN_SEQUENCE_FAULT_QNAME);
+        message.setContent(Exception.class, f);      
+        codec.encode(message);
+        verifyHeaders(message, new String[] {});
+
+        // fault is a SoapFault and has a SequenceFault cause
+        
+        message = setupOutboundFaultMessage();
+        SequenceFault sf = new SequenceFault("REASON");
+        sf.setFaultCode(RM10Constants.UNKNOWN_SEQUENCE_FAULT_QNAME);
+        Identifier sid = new Identifier();
+        sid.setValue("SID");
+        sf.setSender(true);
+        f.initCause(sf);
+        message.setContent(Exception.class, f);      
+        codec.encode(message);
+        verifyHeaders(message, new String[] {RMConstants.SEQUENCE_FAULT_NAME});
+
+    }
+
+    @Test
+    public void testDecodeSequence() throws XMLStreamException {
+        SoapMessage message = setUpInboundMessage("resources/Message1.xml");
+        RMSoapOutInterceptor codec = new RMSoapOutInterceptor();
+        codec.handleMessage(message);
+        RMProperties rmps = RMContextUtils.retrieveRMProperties(message, false);
+        SequenceType st = rmps.getSequence();
+        assertNotNull(st);
+        assertEquals(st.getIdentifier().getValue(), SEQ_IDENTIFIER);
+        assertEquals(st.getMessageNumber(), MSG1_MESSAGE_NUMBER);
+        
+        assertNull(rmps.getAcks());
+        assertNull(rmps.getAcksRequested());
+
+    }
+
+    @Test
+    public void testDecodeAcknowledgements() throws XMLStreamException {
+        SoapMessage message = setUpInboundMessage("resources/Acknowledgment.xml");
+        RMSoapOutInterceptor codec = new RMSoapOutInterceptor();
+        codec.handleMessage(message);
+        RMProperties rmps = RMContextUtils.retrieveRMProperties(message, false);
+        Collection<SequenceAcknowledgement> acks = rmps.getAcks();
+        assertNotNull(acks);
+        assertEquals(1, acks.size());
+        SequenceAcknowledgement ack = acks.iterator().next();
+        assertNotNull(ack);
+        assertEquals(ack.getIdentifier().getValue(), SEQ_IDENTIFIER);
+        assertEquals(2, ack.getAcknowledgementRange().size());
+        AcknowledgementRange r1 = ack.getAcknowledgementRange().get(0);
+        AcknowledgementRange r2 = ack.getAcknowledgementRange().get(1);
+        verifyRange(r1, 1, 1);
+        verifyRange(r2, 3, 3);
+        assertNull(rmps.getSequence());
+        assertNull(rmps.getAcksRequested());
+    }
+
+    @Test
+    public void testDecodeAcknowledgements2() throws XMLStreamException {
+        SoapMessage message = setUpInboundMessage("resources/Acknowledgment2.xml");
+        RMSoapOutInterceptor codec = new RMSoapOutInterceptor();
+        codec.handleMessage(message);
+        RMProperties rmps = RMContextUtils.retrieveRMProperties(message, false);
+        Collection<SequenceAcknowledgement> acks = rmps.getAcks();
+        assertNotNull(acks);
+        assertEquals(1, acks.size());
+        SequenceAcknowledgement ack = acks.iterator().next();
+        assertNotNull(ack);
+        assertEquals(1, ack.getAcknowledgementRange().size());
+        AcknowledgementRange r1 = ack.getAcknowledgementRange().get(0);
+        verifyRange(r1, 1, 3);
+        assertNull(rmps.getSequence());
+        assertNull(rmps.getAcksRequested());
+    }
+
+    private void verifyRange(AcknowledgementRange r, int i, int j) {
+        assertNotNull(r);
+        if (i > 0) {
+            assertNotNull(r.getLower());
+            assertEquals(i, r.getLower().longValue());
+        }
+        if (j > 0) {
+            assertNotNull(r.getUpper());
+            assertEquals(j, r.getUpper().longValue());
+        }
+    }
+
+    @Test
+    public void testDecodeAcksRequested() throws XMLStreamException {
+        SoapMessage message = setUpInboundMessage("resources/Retransmission.xml");
+        RMSoapOutInterceptor codec = new RMSoapOutInterceptor();
+        codec.handleMessage(message);
+        RMProperties rmps = RMContextUtils.retrieveRMProperties(message, false);
+        Collection<AckRequestedType> requested = rmps.getAcksRequested();
+        assertNotNull(requested);
+        assertEquals(1, requested.size());
+        AckRequestedType ar = requested.iterator().next();
+        assertNotNull(ar);
+        assertEquals(ar.getIdentifier().getValue(), SEQ_IDENTIFIER);
+
+        SequenceType s = rmps.getSequence();
+        assertNotNull(s);
+        assertEquals(s.getIdentifier().getValue(), SEQ_IDENTIFIER);
+        assertEquals(s.getMessageNumber(), MSG2_MESSAGE_NUMBER);
+
+        assertNull(rmps.getAcks());
+    }
+
+    private void setUpOutbound() {
+        ObjectFactory factory = new ObjectFactory();
+        s1 = factory.createSequenceType();
+        Identifier sid = factory.createIdentifier();
+        sid.setValue("sequence1");
+        s1.setIdentifier(sid);
+        s1.setMessageNumber(ONE);
+        s2 = factory.createSequenceType();
+        sid = factory.createIdentifier();
+        sid.setValue("sequence2");
+        s2.setIdentifier(sid);
+        s2.setMessageNumber(TEN);
+
+        ack1 = factory.createSequenceAcknowledgement();
+        SequenceAcknowledgement.AcknowledgementRange r = 
+            factory.createSequenceAcknowledgementAcknowledgementRange();
+        r.setLower(ONE);
+        r.setUpper(ONE);
+        ack1.getAcknowledgementRange().add(r);
+        ack1.setIdentifier(s1.getIdentifier());
+
+        ack2 = factory.createSequenceAcknowledgement();
+        r = factory.createSequenceAcknowledgementAcknowledgementRange();
+        r.setLower(ONE);
+        r.setUpper(TEN);
+        ack2.getAcknowledgementRange().add(r);
+        ack2.setIdentifier(s2.getIdentifier());
+
+        ar1 = factory.createAckRequestedType();
+        ar1.setIdentifier(s1.getIdentifier());
+
+        ar2 = factory.createAckRequestedType();
+        ar2.setIdentifier(s2.getIdentifier());
+    }
+
+    private SoapMessage setupOutboundMessage() throws Exception {
+        Exchange ex = new ExchangeImpl();        
+        Message message = new MessageImpl();
+        SoapMessage soapMessage = new SoapMessage(message);         
+        RMProperties rmps = new RMProperties();
+        rmps.exposeAs(RM10Constants.NAMESPACE_URI);
+        RMContextUtils.storeRMProperties(soapMessage, rmps, true);
+        AddressingProperties maps = new AddressingProperties();
+        RMContextUtils.storeMAPs(maps, soapMessage, true, false);
+        ex.setOutMessage(soapMessage);
+        soapMessage.setExchange(ex);        
+        return soapMessage;
+    }
+    
+    private SoapMessage setupOutboundFaultMessage() throws Exception {
+        Exchange ex = new ExchangeImpl();
+        Message message = new MessageImpl();
+        RMProperties rmps = new RMProperties();
+        rmps.exposeAs(RM10Constants.NAMESPACE_URI);
+        RMContextUtils.storeRMProperties(message, rmps, false);
+        AddressingProperties maps = new AddressingProperties();
+        RMContextUtils.storeMAPs(maps, message, false, false);
+        ex.setInMessage(message);
+        message = new MessageImpl();
+        SoapMessage soapMessage = new SoapMessage(message);         
+        ex.setOutFaultMessage(soapMessage);
+        soapMessage.setExchange(ex);        
+        return soapMessage;
+    }
+
+    private void verifyHeaders(SoapMessage message, String... names) {
+        List<Header> header = message.getHeaders();
+
+        // check all expected headers are present
+
+        for (String name : names) {
+            boolean found = false;
+            Iterator<Header> iter = header.iterator();
+            while (iter.hasNext()) {
+                Object obj = iter.next().getObject();
+                if (obj instanceof Element) {
+                    Element elem = (Element) obj;
+                    String namespace = elem.getNamespaceURI();
+                    String localName = elem.getLocalName();
+                    if (RM10Constants.NAMESPACE_URI.equals(namespace)
+                        && localName.equals(name)) {
+                        found = true;
+                        break;
+                    } else if (Names.WSA_NAMESPACE_NAME.equals(namespace)
+                        && localName.equals(name)) {
+                        found = true;
+                        break;
+                    }
+                }
+            }
+            assertTrue("Could not find header element " + name, found);
+        }
+
+        // no other headers should be present
+
+        Iterator<Header> iter1 = header.iterator();
+        while (iter1.hasNext()) {
+            Object obj = iter1.next().getObject();
+            if (obj instanceof Element) {
+                Element elem = (Element) obj;
+                String namespace = elem.getNamespaceURI();
+                String localName = elem.getLocalName();
+                assertTrue(RM10Constants.NAMESPACE_URI.equals(namespace) 
+                    || Names.WSA_NAMESPACE_NAME.equals(namespace));
+                boolean found = false;
+                for (String name : names) {
+                    if (localName.equals(name)) {
+                        found = true;
+                        break;
+                    }
+                }
+                assertTrue("Unexpected header element " + localName, found);
+            }
+        }
+    }
+    
+    private SoapMessage setUpInboundMessage(String resource) throws XMLStreamException {
+        Message message = new MessageImpl();
+        SoapMessage soapMessage = new SoapMessage(message);
+        RMProperties rmps = new RMProperties();
+        rmps.exposeAs(RM10Constants.NAMESPACE_URI);
+        RMContextUtils.storeRMProperties(soapMessage, rmps, false);
+        AddressingProperties maps = new AddressingProperties();
+        RMContextUtils.storeMAPs(maps, soapMessage, false, false);
+        message.put(MessaRMSoapOutInterceptorTestENABLED, false);
+        InputStream is = RMSoapInterceptorTest.class.getResourceAsStream(resource);
+        assertNotNull(is);
+        XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(is);
+        soapMessage.setContent(XMLStreamReader.class, reader);
+        ReadHeadersInterceptor rji = new ReadHeadersInterceptor(BusFactory.getDefaultBus());
+        rji.handleMessage(soapMessage); 
+        StartBodyInterceptor sbi = new StartBodyInterceptor();
+        sbi.handleMessage(soapMessage);
+        return soapMessage;
+    }
+}