/*=============================================================================*
* Copyright 2006 The Apache Software Foundation
*
* Licensed 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 javax.xml.namespace.QName;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.muse.util.messages.Messages;
import org.apache.muse.util.messages.MessagesFactory;
import org.apache.muse.util.xml.XmlSerializable;
import org.apache.muse.util.xml.XmlUtils;
import org.apache.muse.ws.addressing.soap.SoapFault;
/**
*
* EndpointReference is a complete implementation of the EndpointReferenceType
* type defined in WS-Addressing 1.0.
* <br><br>
* This class is implemented as a wrapper for an XML representation of an EPR,
* providing methods that allow users to get and set values with regular Java
* types rather than DOM types. It also provides a number of copy and conversion
* constructors for creating EPRs from various sources.
*
* @author Dan Jemiolo (danj)
*
*/
public class EndpointReference implements XmlSerializable
{
//
// Used to lookup all exception messages
//
private static Messages _MESSAGES = MessagesFactory.get(EndpointReference.class);
private URI _address = null;
//
// pointer to the set of reference parameters - this node is added to
// the EPR's XML tree when it has one or more children
//
private Element _parameters = null;
//
// The QName of the root element in the XML representation. The default
// is wsa:EndpointReference, but sub-types will have a different QName.
//
private QName _rootName = null;
//
// The XML representation of the EPR
//
private Element _xml = null;
/**
*
* This is a convenience constructor that creates a new EPR from the
* given XML, making a deep copy of it in the process. It is equivalent
* to calling the EndpointReference(Element, boolean) constructor with
* the second parameter set to "true".
*
* @see #EndpointReference(Element, boolean)
*
*/
public EndpointReference(Element root)
throws SoapFault
{
this(root, true);
}
/**
*
* Creates a new EPR from the given XML definition.
*
* @param root
* The XML definition of the EPR.
*
* @param makeDeepCopyOfXML
* True if you want the class to clone the given XML fragment
* so that it has an independent copy of the data. If a copy of
* the given XML is made, it will retain the same root element
* QName.
*
* @throws SoapFault
* <ul>
* <li>If the XML fragment is not valid according to the WS-A EPR
* type definition.</li>
* </ul>
*
*/
public EndpointReference(Element root, boolean makeDeepCopyOfXML)
throws SoapFault
{
if (root == null)
throw new NullPointerException(_MESSAGES.get("NullEPRElement"));
//
// option #1 - make our own copy of the XML sub-tree
//
if (makeDeepCopyOfXML)
_xml = (Element)XmlUtils.EMPTY_DOC.importNode(root, true);
//
// option #2 - just use the existing fragment
//
else
_xml = root;
initializeFromXML();
}
/**
*
* This is a convenience constructor that is equivalent to calling the
* EndpointReference(EndpointReference, QName) constructor with the
* standard wsa:EndpointReference QName.
*
* @see #EndpointReference(EndpointReference, QName)
* @see WsaConstants#EPR_QNAME
*
*/
public EndpointReference(EndpointReference copy)
{
this(copy, WsaConstants.EPR_QNAME);
}
/**
*
* A copy constructor for EPRs - creates a deep copy of the given EPR.
* When serialized to XML, the given QName will be used as the name of
* the root element (regardless of the root QName of the copied EPR).
*
* @param copy
* The EPR to copy (deep copy).
*
* @param typeName
* The QName of the root element when this EPR is serialized to XML.
*
*/
public EndpointReference(EndpointReference copy, QName typeName)
{
if (copy == null)
throw new NullPointerException(_MESSAGES.get("NullCopyEPR"));
if (typeName == null)
throw new NullPointerException(_MESSAGES.get("NullEPRTypeQName"));
//
// create the root element - we can't just importNode because the
// QName might be different
//
Document doc = XmlUtils.EMPTY_DOC;
_xml = XmlUtils.createElement(doc, typeName);
//
// now copy all child elements...
//
NodeList children = copy.toXML().getChildNodes();
int length = children.getLength();
for (int n = 0; n < length; ++n)
{
Node next = doc.importNode(children.item(n), true);
_xml.appendChild(next);
}
try
{
initializeFromXML();
}
catch (SoapFault error)
{
throw new RuntimeException(_MESSAGES.get("InvalidEPRCreated"), error);
}
}
/**
*
* This is a convenience constructor that is the equivalent of calling
* EndpointReference(URI, QName) constructor with the standard
* wsa:EndpointReference QName.
*
* @see #EndpointReference(URI, QName)
* @see WsaConstants#EPR_QNAME
*
*/
public EndpointReference(URI address)
{
this(address, WsaConstants.EPR_QNAME);
}
/**
*
* Creates a new EPR with the given wsa:Address URI; when the EPR is
* serialized to XML, its root element will have the given QName. The
* new EPR will have no reference parameters or properties elements.
*
* @param address
* The wsa:Address of the EPR.
*
* @param typeName
* The QName of the root element when the EPR is serialized to XML.
*
*/
public EndpointReference(URI address, QName typeName)
{
if (address == null)
throw new NullPointerException(_MESSAGES.get("NullToAddress"));
if (typeName == null)
throw new NullPointerException(_MESSAGES.get("NullEPRTypeQName"));
_address = address;
Document doc = XmlUtils.EMPTY_DOC;
_xml = XmlUtils.createElement(doc, typeName);
//
// wsa:Address
//
Element addressNode =
XmlUtils.createElement(doc, WsaConstants.ADDRESS_QNAME, address);
_xml.appendChild(addressNode);
//
// create blank parameters
//
_parameters = XmlUtils.createElement(doc, WsaConstants.PARAMETERS_QNAME);
}
/**
*
* Adds the given Element to the collection of reference parameters. The
* object will make a deep copy of the Element.
*
* @param parameter
*
*/
public void addParameter(Element parameter)
{
if (parameter == null)
throw new NullPointerException(_MESSAGES.get("NullReferenceParameter"));
//
// if the ReferenceParameters was previously empty, we need to
// append it to the EPR XML tree
//
if (!_parameters.hasChildNodes())
_xml.appendChild(_parameters);
//
// copy the parameter element into the ReferenceParameters
//
Document doc = _xml.getOwnerDocument();
parameter = (Element)doc.importNode(parameter, true);
_parameters.appendChild(parameter);
}
/**
*
* This is a convenience method that calls addParameter(QName, Object)
* with a null parameter value.
*
* @see #addParameter(QName, Object)
*
*/
public void addParameter(QName qname)
{
addParameter(qname, null);
}
/**
*
* Creates a new reference parameter with the given name and value.
*
* @param qname
* The name of the reference parameter's XML element.
*
* @param value
* The parameter value (can be null).
*
*/
public void addParameter(QName qname, Object value)
{
if (qname == null)
throw new NullPointerException(_MESSAGES.get("NullParameterQName"));
//
// translate to XML, add it to the internal tree
//
Element parameter = XmlUtils.createElement(qname, value);
addParameter(parameter);
}
/**
*
* @return True, if:
* <ul>
* <li>The argument is not null.</li>
* <li>The wsa:Address values match.</li>
* <li>The wsa:PortType values match (if present).</li>
* <li>The wsa:ServiceName and PortName values match (if
* present).</li>
* </ul>
* The equality test does <b>not</b> compare the root element
* QNames used when serializing the EPRs to XML. It also does
* <b>not</b> compare any reference parameters or properties.
*
*/
public boolean equals(Object obj)
{
if (obj == null)
return false;
EndpointReference that = (EndpointReference)obj;
//
// compare wsa:Address - EXCLUDING the host. this allows us
// to compare EPRs that have equivalent IP addresses and
// machine names/localhost
//
String thisPath = getAddress().getPath();
String thatPath = that.getAddress().getPath();
if (!thisPath.equals(thatPath))
return false;
//
// test params that the user wants to include in equality check
//
Element[] values1 = getParameters();
Element[] values2 = that.getParameters();
if (values1.length != values2.length)
return false;
for (int n = 0; n < values1.length; ++n)
if (!XmlUtils.equals(values1[n], values2[n]))
return false;
//
// if we made it this far, all values were equal
//
return true;
}
/**
*
* @return The wsa:Address value.
*
*/
public URI getAddress()
{
return _address;
}
/**
*
* @return The current number of elements in the wsa:ReferenceParameters
* section.
*
*/
public int getNumberOfParameters()
{
return _parameters.getChildNodes().getLength();
}
/**
*
* @param qname
* The name of the reference parameter to look up.
*
* @return The first instance of the given parameter, or null if no
* instances exist.
*
*/
public Element getParameter(QName qname)
{
return getParameter(qname, 0);
}
/**
*
* @param qname
* The name of the reference parameter to look up.
*
* @return The n-th instance of the given parameter, or null if no
* instances exist.
*
*/
public Element getParameter(QName qname, int index)
{
return XmlUtils.getElement(_parameters, qname, index);
}
/**
*
* @return All reference parameter instances (there is no guarantee as
* to the order of the elements). If there are no reference
* parameters, the array is empty.
*
*/
public Element[] getParameters()
{
return XmlUtils.getAllElements(_parameters);
}
/**
*
* @return All reference parameter instances with the given name. If
* there are no instances with that name, the array is empty.
*
*/
public Element[] getParameters(QName qname)
{
return XmlUtils.getElements(_parameters, qname);
}
/**
*
* @param qname
* The name of the reference parameter to look up.
*
* @return The text value of the first instance of the parameter, or
* null if there was none.
*
*/
public String getParameterString(QName qname)
{
return getParameterString(qname, 0);
}
/**
*
* @param qname
* The name of the reference parameter to look up.
*
*
* @return The text value of the n-th instance of the parameter, or
* null if there was none.
*
*/
public String getParameterString(QName qname, int index)
{
Element xml = getParameter(qname, index);
if (xml == null)
return null;
return XmlUtils.extractText(xml);
}
/**
*
* @return The QName that is used for the root element when serializing
* this EPR to XML. The default value is wsa:EndpointReference,
* but this is not required.
*
*/
public QName getRootTypeName()
{
return _rootName;
}
/**
*
* This method has been properly overridden to account for the change
* to equals(Object). Overriding equals(Object) and not hashCode() can
* cause incorrect behavior when storing objects in associative data
* structures.
*
*/
public int hashCode()
{
String address = getAddress().getPath();
Element[] parameters = getParameters();
int paramHashCode = 0;
for (int n = 0; n < parameters.length; ++n)
paramHashCode += XmlUtils.getHashCode(parameters[n]);
return address.hashCode() + paramHashCode;
}
/**
*
* Parses the internal XML representation of the EPR to cache read-only
* values and set up references to other frequently-used data structures.
*
* @throws SoapFault
* <ul>
* <li>If the XML is not valid according to the WS-A EPR schema.</li>
* </ul>
*
*/
private void initializeFromXML()
throws SoapFault
{
//
// save the QName of the EPR type (could be a sub-type)
//
_rootName = XmlUtils.getElementQName(_xml);
String uri = XmlUtils.getElementText(_xml, WsaConstants.ADDRESS_QNAME);
try
{
_address = new URI(uri);
}
catch (Exception error)
{
Object[] filler = { uri };
String message = _MESSAGES.get("InvalidAddressURI", filler);
throw new SoapFault(message, error);
}
Document doc = _xml.getOwnerDocument();
//
// save references to the parameters sub-tree
//
_parameters = XmlUtils.getElement(_xml, WsaConstants.PARAMETERS_QNAME);
if (_parameters == null)
_parameters = XmlUtils.createElement(doc, WsaConstants.PARAMETERS_QNAME);
}
/**
*
* This is a convenience method that removes the first instance of a
* reference parameter with the given name. It is equivalent to calling
* removeParameter(QName, 0).
*
* @see #removeParameter(QName, int)
*
*/
public void removeParameter(QName qname)
{
removeParameter(qname, 0);
}
/**
*
* Removes the n-th instance of the parameter with the given name. If
* the parameter was the last one in the reference parameters collection,
* the wsa:ReferenceParameters section will no longer be serialized into
* the EPR's XML.
*
* @param qname
* The name of the parameter instance to delete.
*
* @param index
* The instance of the parameter to delete.
*
*/
public void removeParameter(QName qname, int index)
{
Node match = XmlUtils.getElement(_parameters, qname, index);
if (match == null)
{
Object[] filler = { qname };
String message = _MESSAGES.get("NoParameterFound", filler);
throw new IllegalArgumentException(message);
}
_parameters.removeChild(match);
//
// if the wsa:ReferenceParameters is empty, we don't want it
// in the EPR XML
//
if (!_parameters.hasChildNodes())
_xml.removeChild(_parameters);
}
/**
*
* Removes all instances of the parameter with the given name. If
* the parameters were the last in the reference parameters collection,
* the wsa:ReferenceParameters section will no longer be serialized into
* the EPR's XML.
*
* @param qname
* The name of the parameter(s) to delete.
*
*/
public void removeParameters(QName qname)
{
Element[] matches = XmlUtils.getElements(_parameters, qname);
if (matches.length == 0)
{
Object[] filler = { qname };
String message = _MESSAGES.get("NoParametersFound", filler);
throw new IllegalArgumentException(message);
}
for (int n = 0; n < matches.length; ++n)
_parameters.removeChild(matches[n]);
//
// if the wsa:ReferenceParameters is empty, we don't want it
// in the EPR XML
//
if (!_parameters.hasChildNodes())
_xml.removeChild(_parameters);
}
/**
*
* @param address
* The wsa:Address of the EPR (cannot be null).
*
*/
public void setAddress(URI address)
{
if (address == null)
throw new NullPointerException(_MESSAGES.get("NullToAddress"));
_address = address;
XmlUtils.setElement(_xml, WsaConstants.ADDRESS_QNAME, _address);
}
/**
*
* @return A string with the XML representation of the EPR.
*
*/
public String toString()
{
return XmlUtils.toString(toXML(), false);
}
/**
*
* @return The complete XML representation of the EPR. This is the actual
* underlying data structure of the EPR, so modifications should
* be made judiciously (if at all).
*
* @see #toXML(Document)
*
*/
public Element toXML()
{
return _xml;
}
/**
*
* @return A copy of the EPR'S XML representation, created using the
* given Document.
*
*/
public Element toXML(Document doc)
{
return (Element)doc.importNode(_xml, true);
}
}