Package org.apache.muse.ws.addressing

Source Code of org.apache.muse.ws.addressing.MessageHeaders

/*
* 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.muse.ws.addressing;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;

import javax.xml.namespace.QName;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import org.apache.muse.util.MultiMap;
import org.apache.muse.util.messages.Messages;
import org.apache.muse.util.messages.MessagesFactory;
import org.apache.muse.util.uuid.RandomUuidFactory;
import org.apache.muse.util.xml.XmlSerializable;
import org.apache.muse.util.xml.XmlUtils;
import org.apache.muse.ws.addressing.soap.SoapFault;
import org.apache.muse.ws.addressing.soap.SoapConstants;

/**
*
* MessageHeaders provides WS-Addressing processing for SOAP message headers.
* It can be used to parse SOAP headers according to WS-A spec and read the
* values <b>or</b> create a new set of headers that can be serialized to a
* set of valid SOAP headers.
* <br><br>
* This class references the W3C DOM API rather than the SAAJ API
* because SOAPElements can usually be converted into DOM Elements very
* easily, but having a DOM Element does not mean that the user code has
* access to (or wants access to) the SAAJ API and its SOAP factories.
*
* @author Dan Jemiolo (danj)
*
* @see EndpointReference
*
*/

public class MessageHeaders implements XmlSerializable
{
    //
    // Used to lookup all exception messages
    //
    private static Messages _MESSAGES = MessagesFactory.get(MessageHeaders.class);

    private static final String _REQUEST = "Request";

    private static final String _RESPONSE = "Response";
       
    //
    // below are the standard WS-A header values for SOAP messages
    //
   
    private String _action = null;
   
    //
    // all non-WS-A headers are made available separately
    //
    private Map _customHeadersByQName = new MultiMap();

    private EndpointReference _faultTo = null;
   
    private EndpointReference _from = null;
   
    private String _messageID = null;

    private String _relationship = null;
   
    private EndpointReference _replyTo = null;
   
    private EndpointReference _to = null;
   
    /**
     *
     * Creates a valid set of WS-A message headers from the given SOAP Header.
     * The wsa:To URI and reference parameter elements become an EPR; this
     * EPR can be used to determine which WS-resource is being targeted.
     * <br><br>
     * An appropriate set of reply headers can be generated with the
     * createReplyHeaders(URI) and createFaultHeaders() methods, assuming the
     * SOAP Header included the appropriate wsa:From, wsa:ReplyTo, or
     * wsa:FaultTo EPR.
     *
     * @param soapHeaders
     *        The Element representing a SOAP envelope's Header. The Header
     *        should contain all of the required WS-A elements.
     *
     * @throws SoapFault
     *         <ul>
     *         <li>
     *         If either the wsa:To, wsa:From, wsa:ReplyTo, or wsa:FaultTo
     *         XML is invalid according to the WS-A EPR definition.
     *         </li>
     *         <li>
     *         If either the wsa:To, wsa:MessageID, wsa:RelatesTo, or
     *         wsa:Action value is not a valid URI.
     *         </li>
     *         <li>
     *         If either the wsa:To, wsa:MessageID, or wsa:Action value is
     *         undefined.
     *         </li>
     *         </ul>
     *
     * @see #createFaultHeaders()
     * @see #createReplyHeaders()
     *
     */
    public MessageHeaders(Element soapHeaders)
        throws SoapFault
    {
        if (soapHeaders == null)
            throw new NullPointerException(_MESSAGES.get("NullSOAPHeader"));
       
        String toURI = XmlUtils.getElementText(soapHeaders, WsaConstants.TO_QNAME);
           
        _action = XmlUtils.getElementText(soapHeaders, WsaConstants.ACTION_QNAME);           
        _messageID = XmlUtils.getElementText(soapHeaders, WsaConstants.MESSAGE_ID_QNAME);
        _relationship = XmlUtils.getElementText(soapHeaders, WsaConstants.RELATES_TO_QNAME);
       
        //
        // all of the URIs are required values
        //
       
        if (toURI == null)
        {
            Object[] filler = { WsaConstants.TO_QNAME };
            throwInvalidAddressingHeaderFault(_MESSAGES.get("HeaderMissing", filler));
        }
       
        if (_action == null)
        {
            Object[] filler = { WsaConstants.ACTION_QNAME };
            throwInvalidAddressingHeaderFault(_MESSAGES.get("HeaderMissing", filler));
        }
       
        //
        // wsa:To is a URI, but we want to access it as an EPR
        //
        _to = new EndpointReference(URI.create(toURI));
       
        //
        // read in optional EPR values that tell us where to send replies
        //
        _faultTo = getEPR(soapHeaders, WsaConstants.FAULT_TO_QNAME);
        _from = getEPR(soapHeaders, WsaConstants.FROM_QNAME);
        _replyTo = getEPR(soapHeaders, WsaConstants.REPLY_TO_QNAME);
       
        if (_messageID == null && (_from != null || _replyTo != null))
            throwInvalidAddressingHeaderFault(_MESSAGES.get("MessageIDMissing"));
       
        Element[] children = XmlUtils.getAllElements(soapHeaders);
       
        //
        // look at all SOAP headers to determine if they're WS-A reference
        // parameters or not
        //
       
        for (int n = 0; n < children.length; ++n)
        {
            QName qname = XmlUtils.getElementQName(children[n]);
           
            //
            // check to see if its a WS-A reference parameter
            //
            String wsaAttr = children[n].getAttributeNS(WsaConstants.NAMESPACE_URI, WsaConstants.IS_REFERENCE_PARAMETER);
           
            if (Boolean.valueOf(wsaAttr) == Boolean.TRUE)
            {
                //
                // remove SOAP junk from parameter XML - this is especially
                // important when you start moving the EPR XML to other
                // messages/fragments that may not have the SOAP prefixes
                //
                children[n].removeAttributeNS(SoapConstants.NAMESPACE_URI, SoapConstants.ACTOR);
                children[n].removeAttributeNS(SoapConstants.NAMESPACE_URI, SoapConstants.MUST_UNDERSTAND);
               
                _to.addParameter(qname, children[n]);
            }
           
            //
            // if not, make sure it's not a WS-A element and add it to
            // the 'custom headers' collection
            //
            else if (!qname.getNamespaceURI().equals(WsaConstants.NAMESPACE_URI))
                _customHeadersByQName.put(qname, children[n]);
        }
    }
   
    /**
     *
     * Creates a new set of WS-A message headers targeted to the given EPR
     * and Action. This constructor creates its own unique wsa:MessageID.
     *
     * @param to
     *        The EPR that will supply the URI for wsa:To.
     *
     * @param action
     *        The wsa:Action URI.
     *
     */
    public MessageHeaders(EndpointReference to, String action)
    {
        if (to == null)
            throw new NullPointerException(_MESSAGES.get("NullToEPR"));
       
        _to = to;
        _action = action;
       
        _messageID = createMessageID();
    }
   
    /**
     *
     * Returns a new set of WS-A message headers based on this object; the
     * headers returned can be used when replying to a message with a fault.
     * The headers will be targeted to the wsa:FaultTo EPR and the WS-A
     * fault action URI. If there is no wsa:FaultTo, wsa:From or the WS-A
     * anonymous EPR is used.
     * <br><br>
     * <b>Note:</b> This method should not be used to create headers for
     * "normal" responses - that is done with createReplyHeaders(URI).
     *
     * @return A new MessageHeaders where:
     *         <ul>
     *         <li>the wsa:To is the URI of this object's wsa:FaultTo,
     *         wsa:From, or the anonymous EPR</li>
     *         <li>the wsa:From is this object's wsa:To EPR</li>
     *         <li>the wsa:RelatesTo is the URI of this object's
     *         wsa:MessageID</li>
     *         <li>the wsa:Action is the standard WS-A fault URI.</li>
     *         </ul>
     *
     * @see #createReplyHeaders()
     * @see #getFaultToAddress()
     * @see #setFaultToAddress(EndpointReference)
     * @see WsaConstants#FAULT_URI
     *
     */
    public MessageHeaders createFaultHeaders()
    {
        EndpointReference faultTo = getFaultToAddress();
        String messageID = getMessageID();
       
        //
        // we can only send back fault headers if wsa:FaultTo or wsa:From exists
        //
        if (faultTo == null)
        {
            EndpointReference from = getFromAddress();
           
            if (from == null)
                from = WsaConstants.ANONYMOUS_EPR;
           
            faultTo = from;
        }
       
        //
        // 1. create headers bound for wsa:FaultTo
        // 2. associate headers with request MessageID
        // 3. set source that was in wsa:To
        //
        MessageHeaders faultHeaders =
            new MessageHeaders(faultTo, WsaConstants.FAULT_URI);
       
        if (messageID != null)
            faultHeaders.setRelationship(messageID);
       
        faultHeaders.setFromAddress(getToAddress());
       
        return faultHeaders;
    }

    /**
     *
     * @return A unique UUID for use as a wsa:MessageID.
     *
     */
    private String createMessageID()
    {
        return RandomUuidFactory.getInstance().createUUID();
    }
   
    /**
     *
     * Returns a new set of WS-A message headers based on this object; the
     * headers returned can be used when replying to a message (no fault).
     * The headers will be targeted to the wsa:ReplyTo EPR. If there is no
     * wsa:ReplyTo, wsa:From or the WS-A anonymous EPR is used.
     * <br><br>
     * <b>Note:</b> This method should not be used to create headers for
     * responses where a fault has occurred - that should be done with
     * createFaultHeaders().
     *
     * @return A new MessageHeaders where:
     *         <ul>
     *         <li>the wsa:To is the URI of this object's wsa:ReplyTo,
     *         wsa:From, or the anonymous EPR</li>
     *         <li>the wsa:From is this object's wsa:To EPR</li>
     *         <li>the wsa:RelatesTo is the URI of this object's
     *         wsa:MessageID</li>
     *         <li>the wsa:Action is the current URI with a "Response" suffix.</li>
     *         </ul>
     *
     * @see #getReplyToAddress()
     * @see #setReplyToAddress(EndpointReference)
     *
     */
    public MessageHeaders createReplyHeaders()
    {
        EndpointReference replyTo = getReplyToAddress();
        String messageID = getMessageID();

        //
        // we can only send back headers if wsa:ReplyTo or wsa:From exists
        //
        if (replyTo == null)
        {
            EndpointReference from = getFromAddress();
           
            if (from == null)
                from = WsaConstants.ANONYMOUS_EPR;
           
            replyTo = from;
        }
       
        String requestAction = getAction().toString();
        String replyAction = null;
       
        int requestSuffix = requestAction.lastIndexOf(_REQUEST);
       
        if (requestSuffix >= 0)
            replyAction = requestAction.substring(0, requestSuffix) + _RESPONSE;
       
        else
            replyAction = requestAction + _RESPONSE;
       
        //
        // 1. create headers bound for wsa:ReplyTo
        // 2. associate headers with request MessageID
        // 3. set source that was in wsa:To
        //
        MessageHeaders replyHeaders = new MessageHeaders(replyTo, replyAction);
       
       
        if (messageID != null)
            replyHeaders.setRelationship(messageID);
       
        replyHeaders.setFromAddress(getToAddress());
       
        return replyHeaders;
    }
   
    /**
     *
     * @return The wsa:Action URI. This is required value.
     *
     */
    public String getAction()
    {
        return _action;
    }
   
    /**
     *
     * This method is useful if you are only expecting one instance of a given
     * SOAP header element and do not want to sort through a Collection or
     * Iterator just to get one item.
     *
     * @param elementName
     *        The name of the non-WS-A SOAP header to return.
     *       
     * @return The <b>first</b> DOM Element found with the given name, or
     *         null if no such element was found.
     *
     */
    public Element getCustomHeader(QName elementName)
    {
        Collection headers = getCustomHeaders(elementName);
        return headers.isEmpty() ? null : (Element)headers.iterator().next();
    }
   
    /**
     *
     * @param elementName
     *        The name of the non-WS-A SOAP header(s) to return.
     *       
     * @return A collection of DOM Elements with the given name. The collection
     *         may be empty.
     *
     */
    public Collection getCustomHeaders(QName elementName)
    {
        Collection headers = (Collection)_customHeadersByQName.get(elementName);
        return headers != null ? headers : Collections.EMPTY_LIST;
    }
   
    /**
     *
     * @return A collection of QNames, one for each SOAP header that a) does
     *         not have the WS-A namespace, and b) is not a WS-A reference
     *         parameter. The collection may be empty.
     *
     */
    public Collection getCustomHeaderNames()
    {
        return Collections.unmodifiableSet(_customHeadersByQName.keySet());
    }
   
    /**
     *
     * Parses the given XML to create a valid EPR. The EPR XML root element
     * must have the given QName.
     *
     * @param root
     *        The Element containing the EPR Element as one of its children.
     *
     * @param qname
     *        The QName of the child element that defines an EPR.
     *
     * @return A valid EPR based on the given XML.
     *
     */
    private EndpointReference getEPR(Element root, QName qname)
        throws SoapFault
    {
        Element epr = XmlUtils.getElement(root, qname);
       
        if (epr == null)
            return null;
       
        return new EndpointReference(epr);
    }

    /**
     *
     * @return The wsa:FaultTo EPR. The method returns null if the value is
     *         not set.
     *
     */
    public EndpointReference getFaultToAddress()
    {
        return _faultTo;
    }
   
    /**
     *
     * @return The wsa:From EPR. The method returns null if the value is
     *         not set.
     *
     */
    public EndpointReference getFromAddress()
    {
        return _from;
    }
   
    /**
     *
     * @return The unique wsa:MessageID UUID. This is a required value.
     *
     */
    public String getMessageID()
    {
        return _messageID;
    }
   
    /**
     *
     * @return The identifier found after the last slash in the wsa:Action
     *         URI. If the URI is 'http://example.com/spec/DoStuff', the value
     *         that is returned is 'DoStuff'.
     *
     */
    public String getMethodName()
    {
        String actionURI = getAction().toString();
        int lastSlash = actionURI.lastIndexOf('/');
        String name = actionURI.substring(lastSlash + 1);
       
        int request = name.indexOf("Request");
       
        if (request >= 0)
            name = name.substring(0, request);
       
        return Character.toLowerCase(name.charAt(0)) + name.substring(1);
    }
   
    /**
     *
     * @return The wsa:RelatesTo value. The method returns null if the value
     *         is not set. wsa:RelatesTo is a UUID that maps to another
     *         MessageHeader's wsa:MessageID.
     *
     */
    public String getRelationship()
    {
        return _relationship;
    }
   
    /**
     *
     * @return This method always returns WsaUtils.REPLY_RELATIONSHIP_QNAME.
     *
     * @see WsaConstants#REPLY_RELATIONSHIP_QNAME
     *
     */
    public QName getRelationshipType()
    {
        return WsaConstants.REPLY_RELATIONSHIP_QNAME;
    }
   
    /**
     *
     * @return The wsa:ReplyTo EPR. The method returns null if the value is
     *         not set.
     *
     */
    public EndpointReference getReplyToAddress()
    {
        return _replyTo;
    }
   
    /**
     *
     * @return An EPR representing the wsa:To URI and the reference properties
     *         defined in the SOAP Header (if any). This is a required value.
     *
     */
    public EndpointReference getToAddress()
    {
        return _to;
    }
   
    /**
     *
     * @param action
     *        The wsa:Action URI. This cannot be null.
     *
     */
    protected void setAction(String action)
    {
        if (action == null)
            throw new NullPointerException(_MESSAGES.get("NullActionURI"));
       
        _action = action;
    }
   
    /**
     *
     * @param faultTo
     *        The wsa:FaultTo EPR - if non-null, a copy of EPR will be made,
     *        so modifications to the original will have no effect on this
     *        set of headers.
     *
     */
    public void setFaultToAddress(EndpointReference faultTo)
    {
        if (faultTo == null)
            _faultTo = null;
       
        //
        // need to make our own copy whose root element is wsa:FaultTo
        //
        else
            _faultTo = new EndpointReference(faultTo, WsaConstants.FAULT_TO_QNAME);
    }
   
    /**
     *
     * @param from
     *        The wsa:From EPR - if non-null, a copy of EPR will be made, so
     *        modifications to the original will have no effect on this set
     *        of headers.
     *
     */
    public void setFromAddress(EndpointReference from)
    {
        if (from == null)
            _from = null;

        //
        // need to make our own copy whose root element is wsa:From
        //
        else
            _from = new EndpointReference(from, WsaConstants.FROM_QNAME);
    }
   
    /**
     *
     * @param messageID
     *        The wsa:MessageID. This cannot be null.
     *
     */
    protected void setMessageID(String messageID)
    {
        if (messageID == null)
            throw new NullPointerException(_MESSAGES.get("NullMessageID"));
       
        _messageID = messageID;
    }
   
    /**
     *
     * @param relationship
     *        The wsa:RelatesTo ID. This cannot be null;
     *
     */
    public void setRelationship(String relationship)
    {
        if (relationship == null)
            throw new NullPointerException(_MESSAGES.get("NullRelatesTo"));
       
        _relationship = relationship;
    }
   
    /**
     *
     * @param replyTo
     *        The wsa:ReplyTo EPR - if non-null, a copy of EPR will be made,
     *        so modifications to the original will have no effect on this
     *        set of headers.
     *
     */
    public void setReplyToAddress(EndpointReference replyTo)
    {
        if (replyTo == null)
            _replyTo = null;

        //
        // need to make our own copy whose root element is wsa:ReplyTo
        //
        else
            _replyTo = new EndpointReference(replyTo, WsaConstants.REPLY_TO_QNAME);
    }
   
    /**
     *
     * @param to
     *        An EPR with the address value for the wsa:To URI. This cannot
     *        be null. No copy of the given EPR is made, so subsequent
     *        modifications to it will affect this object.
     *
     */
    protected void setToAddress(EndpointReference to)
    {
        if (to == null)
            throw new NullPointerException(_MESSAGES.get("NullToEPR"));
       
        _to = to;
    }
   
    protected void throwInvalidAddressingHeaderFault(String message)
        throws SoapFault
    {
        SoapFault fault = new SoapFault(message);
        fault.setCode(SoapConstants.SENDER_QNAME);
        fault.setSubCode(WsaConstants.INVALID_HEADER_FAULT_QNAME);
        throw fault;
    }
   
    /**
     *
     * @return An XML string representing the collection of WS-A headers.
     *         The XML would be a valid SOAP Header element.
     *
     */
    public String toString()
    {
        return XmlUtils.toString(toXML(), false);
    }
   
    /**
     *
     * @see #toXML(Document)
     *
     */
    public Element toXML()
    {
        return toXML(XmlUtils.EMPTY_DOC);
    }
   
    /**
     *
     *
     *
     * @param doc
     *        The XML Document to use when creating new elements for the
     *        SOAP Header XML. The elements that are created will <b>not</b>
     *        be appended to this Document.
     *
     * @return A SOAP Header element with all of the valid WS-A headers as
     *         child elements. There is no guarantee on the order of the WS-A
     *         headers. All reference parameters and properties in the wsa:To
     *         EPR will be appended as children of the root SOAP Header element.
     *
     * @see SoapConstants#HEADER_QNAME
     *
     */
    public Element toXML(Document doc)
    {
        if (doc == null)
            throw new NullPointerException(_MESSAGES.get("NullXMLDocument"));
       
        Element soapHeaders =
            XmlUtils.createElement(doc, SoapConstants.HEADER_QNAME);
       
        //
        // wsa:To
        //
        URI toURI = getToAddress().getAddress();
        Element to = XmlUtils.createElement(doc, WsaConstants.TO_QNAME, toURI);
        soapHeaders.appendChild(to);
       
        //
        // wsa:Action
        //
        XmlUtils.setElement(soapHeaders, WsaConstants.ACTION_QNAME, getAction());
       
        //
        // wsa:MessageID
        //
        String messageID = getMessageID();
       
        if (messageID != null)
            XmlUtils.setElement(soapHeaders, WsaConstants.MESSAGE_ID_QNAME, messageID);
       
        //
        // wsa:RelatesTo (optional)
        //
        String relatesTo = getRelationship();
       
        if (relatesTo != null)
        {
            Element relationship =
                XmlUtils.createElement(doc, WsaConstants.RELATES_TO_QNAME, relatesTo);
           
            String type = XmlUtils.toString(getRelationshipType());
            relationship.setAttribute(WsaConstants.RELATIONSHIP_TYPE, type);
           
            soapHeaders.appendChild(relationship);
        }
       
        //
        // wsa:From, wsa:ReplyTo, and wsa:FaultTo (optional)
        //
       
        EndpointReference[] eprs = new EndpointReference[] {
            getFromAddress(), getReplyToAddress(), getFaultToAddress()
        };
       
        for (int n = 0; n < eprs.length; ++n)
        {
            if (eprs[n] != null)
            {
                Element xml = eprs[n].toXML();
                xml = (Element)doc.importNode(xml, true);
                soapHeaders.appendChild(xml);
            }
        }
       
        //
        // wsa:ReferenceParameters (optional)
        //
       
        Element[] parameters = getToAddress().getParameters();
       
        for (int n = 0; n < parameters.length; ++n)
        {
            Element copy = (Element)doc.importNode(parameters[n], true);
            copy.setAttributeNS(WsaConstants.NAMESPACE_URI, WsaConstants.IS_REFERENCE_PARAMETER_QNAME, "true");
            soapHeaders.appendChild(copy);
        }
       
        //
        // non-WS-A headers
        //
        Iterator i = getCustomHeaderNames().iterator();
       
        while (i.hasNext())
        {
            QName qname = (QName)i.next();
            Iterator j = getCustomHeaders(qname).iterator();
           
            while (j.hasNext())
            {
                Element next = (Element)j.next();
                next = (Element)doc.importNode(next, true);
                soapHeaders.appendChild(next);
            }
        }
       
        return soapHeaders;
    }
}
TOP

Related Classes of org.apache.muse.ws.addressing.MessageHeaders

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.