alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Apache CXF example source code file (RMSoapInterceptor.java)

This example Apache CXF source code file (RMSoapInterceptor.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Apache CXF tags/keywords

collection, dom, element, element, header, jaxbexception, jaxbexception, list, log, logging, net, network, rmproperties, rmproperties, sequencefault, soap_header_encode_failure_msg, string, string, t, util

The Apache CXF RMSoapInterceptor.java source code

/**
 * 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.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
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.SoapMessage;
import org.apache.cxf.binding.soap.SoapVersion;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.PackageUtils;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.BareInInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.InterceptorChain;
import org.apache.cxf.interceptor.WrappedInInterceptor;
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;
import org.apache.cxf.service.model.BindingInfo;
import org.apache.cxf.service.model.BindingOperationInfo;
import org.apache.cxf.service.model.OperationInfo;
import org.apache.cxf.ws.addressing.AddressingProperties;
import org.apache.cxf.ws.addressing.AttributedURIType;
//import org.apache.cxf.ws.addressing.Names;
import org.apache.cxf.ws.addressing.soap.MAPCodec;
import org.apache.cxf.ws.rm.AbstractRMInterceptor;
import org.apache.cxf.ws.rm.AckRequestedType;
import org.apache.cxf.ws.rm.RMConstants;
import org.apache.cxf.ws.rm.RMContextUtils;
import org.apache.cxf.ws.rm.RMEndpoint;
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.SequenceAcknowledgement;
import org.apache.cxf.ws.rm.SequenceFault;
import org.apache.cxf.ws.rm.SequenceFaultType;
import org.apache.cxf.ws.rm.SequenceType;


/**
 * Protocol Handler responsible for {en|de}coding the RM 
 * Properties for {outgo|incom}ing messages.
 */
public class RMSoapInterceptor extends AbstractSoapInterceptor {

    protected static JAXBContext jaxbContext;

    private static final Logger LOG = LogUtils.getL7dLogger(RMSoapInterceptor.class);
    private static final String WS_RM_PACKAGE = 
        PackageUtils.getPackageName(SequenceType.class);
    
    /**
     * Constructor.
     */
    public RMSoapInterceptor() {
        super(Phase.PRE_PROTOCOL);
        
        addAfter(MAPCodec.class.getName());
    } 
    
    // AbstractSoapInterceptor interface 
    
    /**
     * @return the set of SOAP headers understood by this handler 
     */
    public Set<QName> getUnderstoodHeaders() {
        return RMConstants.getHeaders();
    }
    
    // Interceptor interface

    /* (non-Javadoc)
     * @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> header = message.getHeaders();
            discardRMHeaders(header);
            
            Document doc = DOMUtils.createDocument();
            SoapVersion version = Soap11.getInstance();
            Element hdr = doc.createElementNS(version.getHeader().getNamespaceURI(), 
                    version.getHeader().getLocalPart());
            // add WSRM namespace declaration to header, instead of
            // repeating in each individual child node
            
            Attr attr = doc.createAttributeNS("http://www.w3.org/2000/xmlns/", 
                                   "xmlns:" + RMConstants.getNamespacePrefix());
            attr.setValue(RMConstants.getNamespace());
            hdr.setAttributeNodeNS(attr);

            Marshaller marshaller = getJAXBContext().createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
           
            SequenceType seq = rmps.getSequence();
            if (null != seq) {
                encodeProperty(seq, 
                               RMConstants.getSequenceQName(), 
                               SequenceType.class, 
                               hdr,
                               marshaller);
            } 
            Collection<SequenceAcknowledgement> acks = rmps.getAcks();
            if (null != acks) {
                for (SequenceAcknowledgement ack : acks) {
                    encodeProperty(ack, 
                                   RMConstants.getSequenceAckQName(), 
                                   SequenceAcknowledgement.class, 
                                   hdr,
                                   marshaller);
                }
            }
            Collection<AckRequestedType> requested = rmps.getAcksRequested();
            if (null != requested) {
                for (AckRequestedType ar : requested) {
                    encodeProperty(ar, 
                                   RMConstants.getAckRequestedQName(), 
                                   AckRequestedType.class, 
                                   hdr,
                                   marshaller);
                }
            }
            Node node = hdr.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 = new Header(new QName(node.getNamespaceURI(), node.getLocalName()), node);
                header.add(holder);
                node = node.getNextSibling();
            }

        } catch (SOAPException se) {
            LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", se); 
        } catch (JAXBException je) {
            LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
        }        
    }
    
    /**
     * Encode the SeuqnceFault in protocol-specific header.
     *
     * @param message the SOAP message.
     * @param sf the SequenceFault.
     */
    public static void encodeFault(SoapMessage message, SequenceFault sf) {
        if (null == sf.getSequenceFault()) {
            return;
        }
        LOG.log(Level.FINE, "Encoding SequenceFault in SOAP header");
        try {
            List<Header> header = message.getHeaders();
            discardRMHeaders(header);
            
            Document doc = DOMUtils.createDocument();
            SoapVersion version = message.getVersion();
            Element hdr = doc.createElementNS(version.getHeader().getNamespaceURI(), 
                    version.getHeader().getLocalPart());
            // add WSRM namespace declaration to header, instead of
            // repeating in each individual child node
//            hdr.setAttributeNS("http://www.w3.org/2000/xmlns/",
//                                  "xmlns:" + RMConstants.getNamespacePrefix(),
//                                 RMConstants.getNamespace());
            Marshaller marshaller = getJAXBContext().createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

            encodeProperty(sf.getSequenceFault(), 
                           RMConstants.getSequenceFaultQName(), 
                           SequenceFaultType.class, 
                           hdr, 
                           marshaller);
            Node node = hdr.getFirstChild();
            if (node instanceof Element) {
                
                Attr attr = doc.createAttributeNS("http://www.w3.org/2000/xmlns/", 
                                                  "xmlns:" + RMConstants.getNamespacePrefix());
                attr.setValue(RMConstants.getNamespace());
                ((Element)node).setAttributeNodeNS(attr);
            }
            
            header.add(new Header(new QName(node.getNamespaceURI(), node.getLocalName()), node));
        } catch (SOAPException se) {
            LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", se); 
        } catch (JAXBException je) {
            LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
        }        
    }
    
    /**
     * Decode the RM properties from protocol-specific headers
     * and store them in the message.
     *  
     * @param message the SOAP mesage
     */
    void decode(SoapMessage message) {
        RMProperties rmps = unmarshalRMProperties(message);
        RMContextUtils.storeRMProperties(message, rmps, false);
        // TODO: decode SequenceFault ?
    }
    
    /**
     * Decode the RM properties from protocol-specific headers.
     * 
     * @param message the SOAP message
     * @return the RM properties
     */
    public RMProperties unmarshalRMProperties(SoapMessage message) { 
        RMProperties rmps = new RMProperties();
        
        try {
            Collection<SequenceAcknowledgement> acks = new ArrayList();
            Collection<AckRequestedType> requested = new ArrayList();           
            
            List<Header> header = message.getHeaders();
 
            if (header != null) {
                Unmarshaller unmarshaller = 
                    getJAXBContext().createUnmarshaller();
                Iterator<Header> iter = header.iterator();
                while (iter.hasNext()) {
                    Object node = iter.next().getObject();
                    if (node instanceof Element) {
                        Element elem = (Element) node;
                        if (Node.ELEMENT_NODE != elem.getNodeType()) {
                            continue;
                        }
                        String headerURI = elem.getNamespaceURI();
                        String localName = elem.getLocalName();
                        if (RMConstants.getNamespace().equals(headerURI)) {
                            LOG.log(Level.FINE, "decoding RM header {0}", localName);
                            if (RMConstants.getSequenceName().equals(localName)) {
                                SequenceType s = decodeProperty(SequenceType.class,
                                        elem,
                                                                unmarshaller);
                                
                                rmps.setSequence(s);
                            } else if (RMConstants.getSequenceAckName().equals(localName)) {
                                SequenceAcknowledgement ack = decodeProperty(SequenceAcknowledgement.class,
                                        elem,
                                                                unmarshaller);
                                acks.add(ack);                            
                            } else if (RMConstants.getAckRequestedName().equals(localName)) {
                                AckRequestedType ar = decodeProperty(AckRequestedType.class,
                                        elem,
                                                                unmarshaller);
                                requested.add(ar);
                            }
                        }
                    }
                }
                if (acks.size() > 0) {
                    rmps.setAcks(acks);
                }
                if (requested.size() > 0) {
                    rmps.setAcksRequested(requested);
                }
            } 
        } catch (JAXBException ex) {
            LOG.log(Level.WARNING, "SOAP_HEADER_DECODE_FAILURE_MSG", ex); 
        }
        return rmps;
    }


    /**
     * @return a JAXBContext
     */
    private static synchronized JAXBContext getJAXBContext() throws JAXBException {
        if (jaxbContext == null) {
            jaxbContext =
                JAXBContext.newInstance(
                    WS_RM_PACKAGE,
                    SequenceAcknowledgement.class.getClassLoader());
        }
        return jaxbContext;
    }
    
    /**
     * Encodes an RM property as a SOAP header.
     *
     * @param value the value to encode
     * @param qname the QName for the header 
     * @param clz the class
     * @param header the SOAP header element
     * @param marshaller the JAXB marshaller to use
     */
    private static <T> void encodeProperty(T value, 
                                           QName qname, 
                                           Class<T> clz, 
                                           Element header,
                                           Marshaller marshaller)
        throws JAXBException {
        if (value != null) {
            LOG.log(Level.FINE, "encoding " + value + " into RM header {0}", qname);
            marshaller.marshal(new JAXBElement<T>(qname, clz, value), header);
        }
    }
    
    /**
     * Decodes an RM property from a SOAP header.
     * 
     * @param clz the class
     * @param headerElement the SOAP header element
     * @param marshaller the JAXB marshaller to use
     * @return the decoded EndpointReference
     */
    public static <T> T decodeProperty(Class clz,
                                       Element headerElement,
                                       Unmarshaller unmarshaller)
        throws JAXBException {
        if (null == unmarshaller) {
            unmarshaller = getJAXBContext().createUnmarshaller();
        }
        JAXBElement<T> element =
            unmarshaller.unmarshal(headerElement, clz);
        return element.getValue();
    }


    /**
     * 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) throws SOAPException {
        
        Iterator<Header> iter = header.iterator();
        while (iter.hasNext()) {
            Header hdr = iter.next();
            if (RMConstants.getNamespace().equals(hdr.getName().getNamespaceURI())) {
                iter.remove();
            }
        }
    }
    
    
    /**
     * When invoked inbound, check if the action indicates that this is one of the 
     * RM protocol messages (CreateSequence, CreateSequenceResponse, TerminateSequence)
     * and if so, replace references to the application service model with references to
     * the RM service model.
     * The addressing protocol handler must have extracted the action beforehand. 
     * @see org.apache.cxf.transport.ChainInitiationObserver
     * 
     * @param message the message
     */
    private void updateServiceModelInfo(SoapMessage message) {

        AddressingProperties maps = RMContextUtils.retrieveMAPs(message, false, false);
        AttributedURIType actionURI = null == maps ? null : maps.getAction();
        String action = null == actionURI ? null : actionURI.getValue().trim();
        
        LOG.fine("action: " + action);
   
        if (!(RMConstants.getCreateSequenceAction().equals(action)
            || RMConstants.getCreateSequenceResponseAction().equals(action)
            || RMConstants.getTerminateSequenceAction().equals(action)
            || RMConstants.getSequenceAckAction().equals(action)
            || RMConstants.getLastMessageAction().equals(action))) {
            return;
        }
        
        LOG.info("Updating service model info in exchange");
        
        RMManager manager = getManager(message);
        assert manager != null;
        
        RMEndpoint rme = manager.getReliableEndpoint(message);
  
        Exchange exchange = message.getExchange();
        
        exchange.put(Endpoint.class, rme.getEndpoint());
        exchange.put(Service.class, rme.getService());
        exchange.put(Binding.class, rme.getEndpoint().getBinding());
        
        // Also set BindingOperationInfo as some operations (SequenceAcknowledgment) have
        // neither in nor out messages, and thus the WrappedInInterceptor cannot
        // determine the operation name.
        
        BindingInfo bi = rme.getEndpoint().getEndpointInfo().getBinding();
        BindingOperationInfo boi = null;
        boolean isOneway = true;
        if (RMConstants.getCreateSequenceAction().equals(action)) {
            if (RMContextUtils.isServerSide(message)) {
                boi = bi.getOperation(RMConstants.getCreateSequenceOperationName());
                isOneway = false;
            } else {
                boi = bi.getOperation(RMConstants.getCreateSequenceOnewayOperationName());
            }
        } else if (RMConstants.getCreateSequenceResponseAction().equals(action)) {
            if (RMContextUtils.isServerSide(message)) {
                boi = bi.getOperation(RMConstants.getCreateSequenceResponseOnewayOperationName());
            } else {
                boi = bi.getOperation(RMConstants.getCreateSequenceOperationName());
                isOneway = false;
            }
        } else if (RMConstants.getSequenceAckAction().equals(action)) {
            boi = bi.getOperation(RMConstants.getSequenceAckOperationName()); 
        } else if (RMConstants.getTerminateSequenceAction().equals(action)) {
            boi = bi.getOperation(RMConstants.getTerminateSequenceOperationName()); 
        } else if (RMConstants.getLastMessageAction().equals(action)) {
            boi = bi.getOperation(RMConstants.getLastMessageOperationName()); 
        }
        assert boi != null;
        exchange.put(BindingOperationInfo.class, boi);
        exchange.put(OperationInfo.class, boi.getOperationInfo());
        exchange.setOneWay(isOneway); 
        
        // Fix requestor role (as the client side message observer always sets it to TRUE) 
        // to allow unmarshalling the body of a server originated TerminateSequence request.
        // 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.        
        // 
        
        if (!RMConstants.getCreateSequenceResponseAction().equals(action)) {
            LOG.fine("Changing requestor role from " + message.get(Message.REQUESTOR_ROLE)
                     + " to false");
            Object originalRequestorRole = message.get(Message.REQUESTOR_ROLE);
            if (null != originalRequestorRole) {
                message.put(RMMessageConstants.ORIGINAL_REQUESTOR_ROLE, originalRequestorRole);
            }
            message.put(Message.REQUESTOR_ROLE, Boolean.FALSE);
        }       
        
        // replace WrappedInInterceptor with BareInInterceptor if necessary
        // as RM protocol messages use paremeter style BARE

        InterceptorChain chain = message.getInterceptorChain();
        ListIterator it = chain.getIterator();            
        boolean bareIn = false;
        boolean wrappedIn = false;
        while (it.hasNext() && !wrappedIn && !bareIn) {
            PhaseInterceptor pi = (PhaseInterceptor)it.next();
            if (WrappedInInterceptor.class.getName().equals(pi.getId())) {
                wrappedIn = true;
                it.remove();
                LOG.fine("Removed WrappedInInterceptor from chain.");
            } else if (BareInInterceptor.class.getName().equals(pi.getId())) {
                bareIn = true;
            }
      
        }
        if (!bareIn) {
            chain.add(new BareInInterceptor());
            LOG.fine("Added BareInInterceptor to chain.");
        }
    }

    private RMManager getManager(SoapMessage message) {
        InterceptorChain chain = message.getInterceptorChain();
        ListIterator it = chain.getIterator();
        while (it.hasNext()) {
            Interceptor i = (Interceptor)it.next();
            if (i instanceof AbstractRMInterceptor) {
                return ((AbstractRMInterceptor)i).getManager();
            }
        }
        return null;
    }
}







Other Apache CXF examples (source code examples)

Here is a short list of links related to this Apache CXF RMSoapInterceptor.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.