| /* |
| * 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.axis2.addressing; |
| |
| import org.apache.axiom.om.OMAbstractFactory; |
| import org.apache.axiom.om.OMAttribute; |
| import org.apache.axiom.om.OMElement; |
| import org.apache.axiom.om.OMFactory; |
| import org.apache.axiom.om.OMNamespace; |
| import org.apache.axiom.om.OMNode; |
| import org.apache.axiom.om.OMXMLBuilderFactory; |
| import org.apache.axiom.util.UIDGenerator; |
| import org.apache.axis2.AxisFault; |
| import org.apache.axis2.context.externalize.ExternalizeConstants; |
| import org.apache.axis2.context.externalize.SafeObjectInputStream; |
| import org.apache.axis2.context.externalize.SafeObjectOutputStream; |
| import org.apache.axis2.context.externalize.SafeSerializable; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.stream.XMLStreamReader; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.Externalizable; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Class EndpointReference |
| * This class models the WS-A EndpointReferenceType. But this can be used without any WS-A handlers as well |
| * Since the models for this in Submission and Final versions are different, lets make this to comply with |
| * WS-A Final version. So any information found with WS-A submission will be "pumped" in to this model. |
| */ |
| public class EndpointReference implements Externalizable, SafeSerializable { |
| |
| private static final long serialVersionUID = 5278892171162372439L; |
| |
| private static final Log log = LogFactory.getLog(EndpointReference.class); |
| |
| // supported revision levels, add a new level to manage compatible changes |
| private static final int REVISION_2 = 2; |
| // current revision level of this object |
| private static final int revisionID = REVISION_2; |
| |
| private static final String myClassName = "EndpointReference"; |
| |
| /** |
| * An ID which can be used to correlate operations on an instance of |
| * this object in the log files |
| */ |
| private String logCorrelationIDString; |
| |
| /** |
| * The list of URIs that should be considered equivalent to |
| * the WS-Addressing anonymous URI |
| */ |
| private static volatile List<String> anonymousEquivalentURIs = new ArrayList<String>(); |
| |
| |
| /** |
| * <EndpointReference> |
| * <Address>xs:anyURI</Address> |
| * <ReferenceParameters>xs:any*</ReferenceParameters> |
| * <MetaData>xs:any*</MetaData> |
| * <!-- In addition to this, EPR can contain any number of OMElements --> |
| * </EndpointReference> |
| */ |
| |
| private String name; |
| private String address; |
| private ArrayList<OMAttribute> addressAttributes; |
| private ArrayList<OMNode> metaData; |
| private ArrayList<OMAttribute> metaDataAttributes; |
| private Map<QName, OMElement> referenceParameters; |
| private ArrayList<OMElement> extensibleElements; |
| private ArrayList<OMAttribute> attributes; |
| |
| /** |
| * No-Arg Constructor |
| * Required for Externalizable objects |
| */ |
| public EndpointReference() {} |
| |
| /** |
| * Adds a parameter to the list of anonymous equivalent URIs. |
| * @param anonymousEquivalentURI any URI that has an address that |
| * begins with this value will be considered to be anonymous |
| */ |
| public static void addAnonymousEquivalentURI(String anonymousEquivalentURI){ |
| if (log.isTraceEnabled()) |
| log.trace("addAnonymousEquivalentURI: " + anonymousEquivalentURI); |
| |
| // Avoid synchronization in hasAnonymousAddress by using the |
| // technique outlined at http://is.gd/gv3 |
| synchronized (anonymousEquivalentURIs) { |
| ArrayList<String> newList = new ArrayList<String>(anonymousEquivalentURIs); |
| newList.add(anonymousEquivalentURI); |
| anonymousEquivalentURIs = newList; |
| } |
| } |
| |
| |
| /** |
| * @param address |
| */ |
| public EndpointReference(String address) { |
| this.address = address; |
| } |
| |
| /** |
| * @param omElement |
| */ |
| public void addReferenceParameter(OMElement omElement) { |
| if (omElement == null) { |
| return; |
| } |
| if (referenceParameters == null) { |
| referenceParameters = new HashMap<QName, OMElement>(); |
| } |
| referenceParameters.put(omElement.getQName(), omElement); |
| } |
| |
| /** |
| * @param qname |
| * @param value - the text of the OMElement. Remember that this is a convenient method for the user, |
| * which has limited capability. If you want more power use @See EndpointReference#addReferenceParameter(OMElement) |
| */ |
| public void addReferenceParameter(QName qname, String value) { |
| if (qname == null) { |
| return; |
| } |
| OMElement omElement = OMAbstractFactory.getOMFactory().createOMElement(qname, null); |
| omElement.setText(value); |
| addReferenceParameter(omElement); |
| } |
| |
| /** |
| * This will return a Map of reference parameters with QName as the key and an OMElement |
| * as the value |
| * |
| * @return - map of the reference parameters, where the key is the QName of the reference parameter |
| * and the value is an OMElement |
| */ |
| public Map<QName, OMElement> getAllReferenceParameters() { |
| return referenceParameters; |
| } |
| |
| public String getAddress() { |
| return address; |
| } |
| |
| /** |
| * @param address - xs:anyURI |
| */ |
| public void setAddress(String address) { |
| this.address = address; |
| } |
| |
| public ArrayList<OMAttribute> getAddressAttributes() { |
| return addressAttributes; |
| } |
| |
| public void setAddressAttributes(ArrayList<OMAttribute> al) { |
| addressAttributes = al; |
| } |
| |
| public ArrayList<OMAttribute> getMetadataAttributes() { |
| return metaDataAttributes; |
| } |
| |
| public void setMetadataAttributes(ArrayList<OMAttribute> al) { |
| metaDataAttributes = al; |
| } |
| |
| /** |
| * This method identifies whether the address is a WS-Addressing spec defined |
| * anonymous URI. |
| * |
| * @return true if the address is a WS-Addressing spec defined anonymous URI. |
| * |
| * @see #hasAnonymousAddress() |
| */ |
| public boolean isWSAddressingAnonymous() { |
| return (AddressingConstants.Final.WSA_ANONYMOUS_URL.equals(address) || |
| AddressingConstants.Submission.WSA_ANONYMOUS_URL.equals(address)); |
| } |
| |
| /** |
| * This method is used to identify when response messages should be sent using |
| * the back channel of a two-way transport. |
| * |
| * @return true if the address is a WS-Addressing spec defined anonymous URI, |
| * or starts with a string that is specified to be equivalent to an anonymous |
| * URI. |
| * |
| * @see #addAnonymousEquivalentURI(String) |
| */ |
| public boolean hasAnonymousAddress() { |
| boolean result = isWSAddressingAnonymous(); |
| |
| if(!result && address != null) { |
| // If the address is not WS-A anonymous it might still be considered anonymous |
| // Avoid synchronization by using the |
| // technique outlined at http://is.gd/gv3 |
| List<String> localList = anonymousEquivalentURIs; |
| if(!localList.isEmpty()){ |
| Iterator<String> it = localList.iterator(); |
| while(it.hasNext()){ |
| result = address.startsWith((String)it.next()); |
| if(result){ |
| break; |
| } |
| } |
| } |
| } |
| |
| if (log.isTraceEnabled()) { |
| log.trace("hasAnonymousAddress: " + address + " is Anonymous: " + result); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * hasNoneAddress |
| * |
| * @return true if the address is the 'None URI' from the final addressing spec. |
| */ |
| public boolean hasNoneAddress() { |
| boolean result = AddressingConstants.Final.WSA_NONE_URI.equals(address); |
| if (log.isTraceEnabled()) { |
| log.trace("hasNoneAddress: " + address + " is None: " + result); |
| } |
| return result; |
| } |
| |
| /** |
| * @param localName |
| * @param ns |
| * @param value |
| */ |
| public void addAttribute(String localName, OMNamespace ns, String value) { |
| if (attributes == null) { |
| attributes = new ArrayList<OMAttribute>(); |
| } |
| attributes.add(OMAbstractFactory.getOMFactory().createOMAttribute(localName, ns, value)); |
| } |
| |
| public ArrayList<OMAttribute> getAttributes() { |
| return attributes; |
| } |
| |
| |
| /** |
| * @param omAttribute |
| */ |
| public void addAttribute(OMAttribute omAttribute) { |
| if (attributes == null) { |
| attributes = new ArrayList<OMAttribute>(); |
| } |
| attributes.add(omAttribute); |
| } |
| |
| public ArrayList<OMElement> getExtensibleElements() { |
| return extensibleElements; |
| } |
| |
| /** |
| * {any} |
| * |
| * @param extensibleElements |
| */ |
| public void setExtensibleElements(ArrayList<OMElement> extensibleElements) { |
| this.extensibleElements = extensibleElements; |
| } |
| |
| public void addExtensibleElement(OMElement extensibleElement) { |
| if (extensibleElement != null) { |
| if (this.extensibleElements == null) { |
| this.extensibleElements = new ArrayList<OMElement>(); |
| } |
| this.extensibleElements.add(extensibleElement); |
| } |
| } |
| |
| public ArrayList<OMNode> getMetaData() { |
| return metaData; |
| } |
| |
| public void addMetaData(OMNode metaData) { |
| if (metaData != null) { |
| if (this.metaData == null) { |
| this.metaData = new ArrayList<OMNode>(); |
| } |
| this.metaData.add(metaData); |
| } |
| |
| } |
| |
| /** |
| * @deprecated |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * @param name |
| * @deprecated |
| */ |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| /** |
| * Set a Map with QName as the key and an OMElement |
| * as the value |
| * |
| * @param referenceParameters |
| */ |
| public void setReferenceParameters(Map<QName, OMElement> referenceParameters) { |
| this.referenceParameters = referenceParameters; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see java.lang.Object#toString() |
| */ |
| public String toString() { |
| StringBuffer buffer = new StringBuffer("Address: " + address); |
| |
| if (addressAttributes != null) { |
| buffer.append(", Address Attributes: ").append(addressAttributes); |
| } |
| |
| if (metaData != null) { |
| buffer.append(", Metadata: ").append(metaData); |
| } |
| |
| if (metaDataAttributes != null) { |
| buffer.append(", Metadata Attributes: ").append(metaDataAttributes); |
| } |
| |
| if (referenceParameters != null) { |
| buffer.append(", Reference Parameters: ").append(referenceParameters); |
| } |
| |
| if (extensibleElements != null) { |
| buffer.append(", Extensibility elements: ").append(extensibleElements); |
| } |
| |
| if (attributes != null) { |
| buffer.append(", Attributes: ").append(attributes); |
| } |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Compares key parts of the state from the current instance of |
| * this class with the specified instance to see if they are |
| * equivalent. |
| * <p/> |
| * This differs from the java.lang.Object.equals() method in |
| * that the equals() method generally looks at both the |
| * object identity (location in memory) and the object state |
| * (data). |
| * <p/> |
| * |
| * @param epr The object to compare with |
| * @return TRUE if this object is equivalent with the specified object |
| * that is, key fields match |
| * FALSE, otherwise |
| */ |
| public boolean isEquivalent(EndpointReference epr) { |
| // NOTE: the input object is expected to exist (ie, be non-null) |
| |
| if ((this.name != null) && (epr.getName() != null)) { |
| if (!this.name.equals(epr.getName())) { |
| return false; |
| } |
| } else if ((this.name == null) && (epr.getName() == null)) { |
| // continue |
| } else { |
| // mismatch |
| return false; |
| } |
| |
| |
| if ((this.address != null) && (epr.getAddress() != null)) { |
| if (!this.address.equals(epr.getAddress())) { |
| return false; |
| } |
| } else if ((this.address == null) && (epr.getAddress() == null)) { |
| // continue |
| } else { |
| // mismatch |
| return false; |
| } |
| |
| // TODO: is a strict test ok to use? |
| |
| ArrayList<OMNode> eprMetaData = epr.getMetaData(); |
| |
| if ((this.metaData != null) && (eprMetaData != null)) { |
| if (!this.metaData.equals(eprMetaData)) { |
| // This is a strict test |
| // Returns true if and only if the specified object |
| // is also a list, both lists have the same size, and |
| // all corresponding pairs of elements in the two lists |
| // are equal, ie, two lists are defined to be equal if |
| // they contain the same elements in the same order. |
| |
| return false; |
| } |
| } else if ((this.metaData == null) && (eprMetaData == null)) { |
| // keep going |
| } else { |
| // one of the lists is null |
| return false; |
| } |
| |
| |
| ArrayList<OMElement> eprExtensibleElements = epr.getExtensibleElements(); |
| |
| if ((this.extensibleElements != null) && (eprExtensibleElements != null)) { |
| if (!this.extensibleElements.equals(eprExtensibleElements)) { |
| // This is a strict test |
| // Returns true if and only if the specified object |
| // is also a list, both lists have the same size, and |
| // all corresponding pairs of elements in the two lists |
| // are equal, ie, two lists are defined to be equal if |
| // they contain the same elements in the same order. |
| |
| return false; |
| } |
| } else if ((this.extensibleElements == null) && (eprExtensibleElements == null)) { |
| // keep going |
| } else { |
| // one of the lists is null |
| return false; |
| } |
| |
| |
| ArrayList<OMAttribute> eprAttributes = epr.getAttributes(); |
| |
| if ((this.attributes != null) && (eprAttributes != null)) { |
| if (!this.attributes.equals(eprAttributes)) { |
| // This is a strict test |
| // Returns true if and only if the specified object |
| // is also a list, both lists have the same size, and |
| // all corresponding pairs of elements in the two lists |
| // are equal, ie, two lists are defined to be equal if |
| // they contain the same elements in the same order. |
| |
| return false; |
| } |
| } else if ((this.attributes == null) && (eprAttributes == null)) { |
| // keep going |
| } else { |
| // one of the lists is null |
| return false; |
| } |
| |
| // TODO: check the Map referenceParameters for equivalency |
| |
| return true; |
| } |
| |
| //REVIEW: The following code is rather heavyweight, because we have to build |
| // the OM tree -- it would probably be better to have two serialization/deserialization |
| // paths and therefore, for trivial EPRs, store a smaller amount of info |
| |
| /** |
| * Write the EPR to the specified OutputStream. Because of potential |
| * OMElements/Attributes, we need to actually serialize the OM structures |
| * (at least in some cases.) |
| */ |
| public synchronized void writeExternal(java.io.ObjectOutput o) |
| throws IOException { |
| SafeObjectOutputStream out = SafeObjectOutputStream.install(o); |
| |
| // revision ID |
| out.writeInt(revisionID); |
| |
| // Correlation ID |
| String logCorrelationIDString = getLogCorrelationIDString(); |
| |
| // String object id |
| out.writeObject(logCorrelationIDString); |
| |
| // Write out the content as xml |
| out.writeUTF("start xml"); // write marker |
| OMElement om = |
| EndpointReferenceHelper.toOM(OMAbstractFactory.getOMFactory(), |
| this, |
| new QName("urn:axis2", "omepr", "ser"), |
| AddressingConstants.Final.WSA_NAMESPACE); |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| |
| try { |
| om.serialize(baos); |
| } catch (Exception e) { |
| IOException ioe = new IOException("Unable to serialize the EndpointReference with logCorrelationID [" |
| +logCorrelationIDString+"]"); |
| ioe.initCause(e); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("writeObject(): Unable to serialize the EPR with logCorrelationID [" |
| +logCorrelationIDString+"] original error ["+e.getClass().getName() |
| +"] message ["+e.getMessage()+"]",e); |
| } |
| |
| throw ioe; |
| } |
| |
| out.writeInt(baos.size()); |
| out.write(baos.toByteArray()); |
| out.writeUTF("end xml"); // write marker |
| |
| if (log.isDebugEnabled()) { |
| byte[] buffer = baos.toByteArray(); |
| String content = new String(buffer); |
| |
| log.debug("writeObject(): EPR logCorrelationID ["+logCorrelationIDString+"] " |
| +" EPR content size ["+baos.size()+"]" |
| +" EPR content ["+content+"]"); |
| } |
| |
| } |
| |
| /** |
| * Read the EPR to the specified InputStream. |
| */ |
| public void readExternal(java.io.ObjectInput inObject) |
| throws IOException, ClassNotFoundException { |
| SafeObjectInputStream in = SafeObjectInputStream.install(inObject); |
| |
| // revision ID |
| int revID = in.readInt(); |
| |
| // make sure the object data is in a revision level we can handle |
| if (revID != REVISION_2) { |
| throw new ClassNotFoundException(ExternalizeConstants.UNSUPPORTED_REVID); |
| } |
| |
| // String object id |
| logCorrelationIDString = (String) in.readObject(); |
| |
| // Read xml content |
| in.readUTF(); // read marker |
| int numBytes = in.readInt(); |
| |
| byte[] serBytes = new byte[numBytes]; |
| |
| // read the data from the input stream |
| |
| int bytesRead = 0; |
| int numberOfBytesLastRead; |
| |
| while (bytesRead < numBytes) { |
| numberOfBytesLastRead = in.read(serBytes, bytesRead, numBytes - bytesRead); |
| |
| if (numberOfBytesLastRead == -1) { |
| // TODO: What should we do if the reconstitution fails? |
| // For now, log the event and throw an exception |
| if (log.isDebugEnabled()) { |
| log.debug("readObject(): EPR logCorrelationID ["+logCorrelationIDString+"] " |
| + " ***WARNING*** unexpected end to data: data read from input stream [" |
| + bytesRead + "] expected data size [" + numBytes + "]"); |
| } |
| |
| IOException ioe = new IOException("Unable to deserialize the EndpointReference with logCorrelationID [" |
| +logCorrelationIDString+"]" |
| +" Cause: Unexpected end to data from input stream"); |
| |
| throw ioe; |
| } |
| |
| bytesRead += numberOfBytesLastRead; |
| } |
| |
| |
| if (bytesRead == 0) { |
| IOException ioe = new IOException("Unable to deserialize the EndpointReference with logCorrelationID [" |
| +logCorrelationIDString+"]" |
| +" Cause: No data from input stream"); |
| |
| throw ioe; |
| } |
| in.readUTF(); // read marker |
| |
| ByteArrayInputStream bais = new ByteArrayInputStream(serBytes); |
| |
| if (log.isDebugEnabled()) { |
| String content = new String(serBytes); |
| |
| log.debug("readObject(): EPR logCorrelationID ["+logCorrelationIDString+"] " |
| +" expected content size ["+numBytes+"]" |
| +" content size ["+content.length()+"]" |
| +" EPR buffered content ["+content+"]"); |
| } |
| |
| XMLStreamReader xmlReader = null; |
| |
| try { |
| OMElement om = OMXMLBuilderFactory.createOMBuilder(bais).getDocumentElement(); |
| |
| // expand the OM so we can close the stream reader |
| om.build(); |
| |
| // trace point |
| if (log.isDebugEnabled()) { |
| log.debug(myClassName + ":readObject(): " |
| + " EPR ["+logCorrelationIDString + "]" |
| + " EPR OM content ["+om.toString()+ "]"); |
| } |
| |
| EndpointReferenceHelper.fromOM(this, om, AddressingConstants.Final.WSA_NAMESPACE); |
| |
| |
| } catch (Exception e) { |
| IOException ioe = new IOException("Unable to deserialize the EndpointReference with logCorrelationID [" |
| +logCorrelationIDString+"]"); |
| ioe.initCause(e); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("readObject(): Unable to deserialize the EPR with logCorrelationID [" |
| +logCorrelationIDString+"] original error ["+e.getClass().getName() |
| +"] message ["+e.getMessage()+"]",e); |
| } |
| |
| throw ioe; |
| |
| } finally { |
| // Make sure that the reader is properly closed |
| // Note that closing a ByteArrayInputStream has no effect |
| |
| if (xmlReader != null) { |
| try { |
| xmlReader.close(); |
| } catch (Exception e2) { |
| IOException ioe2 = new IOException("Unable to close the XMLStreamReader for the EndpointReference with logCorrelationID [" |
| +logCorrelationIDString+"]"); |
| ioe2.initCause(e2); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("readObject(): Unable to close the XMLStreamReader for the EPR with logCorrelationID [" |
| +logCorrelationIDString+"] original error ["+e2.getClass().getName() |
| +"] message ["+e2.getMessage()+"]",e2); |
| } |
| |
| throw ioe2; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Get the ID associated with this object instance. |
| * |
| * @return A string that can be output to a log file as an identifier |
| * for this object instance. It is suitable for matching related log |
| * entries. |
| */ |
| public String getLogCorrelationIDString() { |
| if (logCorrelationIDString == null) { |
| logCorrelationIDString = myClassName + "@" + UIDGenerator.generateUID(); |
| } |
| return logCorrelationIDString; |
| } |
| |
| } |