/** * 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.addressing.soap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; 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.Unmarshaller; import javax.xml.namespace.QName; 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.soap.SoapFault; import org.apache.cxf.binding.soap.SoapHeader; 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.binding.soap.interceptor.SoapActionInInterceptor; import org.apache.cxf.common.jaxb.JAXBUtils; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.headers.Header; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.interceptor.Interceptor; import org.apache.cxf.jaxb.JAXBDataBinding; 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.ws.addressing.AddressingProperties; import org.apache.cxf.ws.addressing.AttributedURIType; import org.apache.cxf.ws.addressing.ContextUtils; import org.apache.cxf.ws.addressing.EndpointReferenceType; import org.apache.cxf.ws.addressing.EndpointReferenceUtils; import org.apache.cxf.ws.addressing.JAXWSAConstants; import org.apache.cxf.ws.addressing.MAPAggregator; import org.apache.cxf.ws.addressing.Names; import org.apache.cxf.ws.addressing.ReferenceParametersType; import org.apache.cxf.ws.addressing.RelatesToType; import org.apache.cxf.ws.addressing.VersionTransformer.Names200408; /** * SOAP interceptor responsible for {en|de}coding the Message Addressing * Properties for {outgo|incom}ing messages. */ public class MAPCodec extends AbstractSoapInterceptor { private static final Logger LOG = LogUtils.getL7dLogger(MAPCodec.class); private static final String IS_REFERENCE_PARAM_ATTR_NAME = "IsReferenceParameter"; private static final ResourceBundle BUNDLE = LOG.getResourceBundle(); private static final String DECOUPLED_FAULT_SUPPORT = "org.apache.cxf.ws.addressing.decoupled_fault_support"; /** * REVISIT: map usage that the *same* interceptor instance * is used in all chains. */ protected final Map<String, Exchange> uncorrelatedExchanges = new ConcurrentHashMap<String, Exchange>(); private VersionTransformer transformer; private HeaderFactory headerFactory; /** * Constructor. */ public MAPCodec() { super(Phase.PRE_PROTOCOL); transformer = new VersionTransformer(this); } public Map<String, Exchange> getUncorrelatedExchanges() { return uncorrelatedExchanges; } /** * @return the set of SOAP headers understood by this handler */ public Set<QName> getUnderstoodHeaders() { return VersionTransformer.HEADERS; } /** * Invoked for normal processing of inbound and outbound messages. * * @param message the messsage */ public void handleMessage(SoapMessage message) { mediate(message); } /** * Invoked when unwinding normal interceptor chain when a fault occurred. * * @param message the messsage message */ public void handleFault(SoapMessage message) { if (!message.getExchange().isOneWay()) { AddressingProperties maps = ContextUtils.retrieveMAPs(message, false, true, false); if (ContextUtils.isRequestor(message) && maps != null) { //fault occurred trying to send the message, remove it uncorrelatedExchanges.remove(maps.getMessageID().getValue()); } else if (!ContextUtils.isRequestor(message) && maps == null && !message.containsKey(MAPAggregator.class.getName())) { //fault occurred while processing the incoming message, but possibly //before the MAPAggregator was called. We need to see if we can //try and map this if at all possible so a FaultTo/ReplyTo can //be properly determined to get the fault back to the rightful //place. for (Interceptor<? extends Message> i : message.getInterceptorChain()) { if (i instanceof MAPAggregator) { try { MAPAggregator agg = (MAPAggregator)i; agg.handleMessage(message); } catch (Throwable t) { //ignore } return; } } } } if (MessageUtils.getContextualBoolean(message, DECOUPLED_FAULT_SUPPORT, false)) { new DecoupledFaultHandler().handleFault(message); } } /** * Mediate message flow, performing MAP {en|de}coding. * * @param message the message message */ private void mediate(SoapMessage message) { if (!MessageUtils.getContextualBoolean(message, MAPAggregator.ADDRESSING_DISABLED, false)) { if (ContextUtils.isOutbound(message)) { encode(message, ContextUtils.retrieveMAPs(message, false, true)); } else if (null == ContextUtils.retrieveMAPs(message, false, false, false)) { AddressingProperties maps = decode(message); ContextUtils.storeMAPs(maps, message, false); markPartialResponse(message, maps); restoreExchange(message, maps); if (maps != null && !MessageUtils.isRequestor(message) && message.getExchange().getBindingOperationInfo() == null && !MessageUtils.isOutbound(message) && maps.getAction() != null) { //try and use the Action from the maps to find the operation String action = maps.getAction().getValue(); if (action != null) { boolean strict = MessageUtils.getContextualBoolean(message, "ws-addressing.strict.action.checking", false); SoapActionInInterceptor.getAndSetOperation(message, action, strict); } } } } } /** * Encode the current MAPs in protocol-specific headers. * * @param message the messsage message * @param maps the MAPs to encode */ private void encode(SoapMessage message, AddressingProperties maps) { if (maps != null) { cacheExchange(message, maps); LOG.log(Level.FINE, "Outbound WS-Addressing headers"); try { List<Header> header = message.getHeaders(); discardMAPs(header, maps); JAXBContext jaxbContext = VersionTransformer.getExposedJAXBContext( maps.getNamespaceURI()); QName duplicate = maps.getDuplicate(); encodeAsExposed(maps, message, maps.getAction(), Names.WSA_ACTION_QNAME, AttributedURIType.class, jaxbContext); if (Names.WSA_ACTION_QNAME.equals(duplicate)) { encodeAsExposed(maps, message, maps.getAction(), Names.WSA_ACTION_QNAME, AttributedURIType.class, jaxbContext); } encodeAsExposed(maps, message, maps.getMessageID(), Names.WSA_MESSAGEID_QNAME, AttributedURIType.class, jaxbContext); if (Names.WSA_MESSAGEID_QNAME.equals(duplicate)) { encodeAsExposed(maps, message, maps.getMessageID(), Names.WSA_MESSAGEID_QNAME, AttributedURIType.class, jaxbContext); } encodeAsExposed(maps, message, maps.getTo(), Names.WSA_TO_QNAME, AttributedURIType.class, jaxbContext); if (Names.WSA_TO_QNAME.equals(duplicate)) { encodeAsExposed(maps, message, maps.getTo(), Names.WSA_TO_QNAME, AttributedURIType.class, jaxbContext); } if (needsReplyTo(maps, message)) { encodeAsExposed(maps, message, maps.getReplyTo(), Names.WSA_REPLYTO_QNAME, EndpointReferenceType.class, jaxbContext); if (Names.WSA_REPLYTO_QNAME.equals(duplicate)) { encodeAsExposed(maps, message, maps.getReplyTo(), Names.WSA_REPLYTO_QNAME, EndpointReferenceType.class, jaxbContext); } } encodeAsExposed(maps, message, maps.getRelatesTo(), Names.WSA_RELATESTO_QNAME, RelatesToType.class, jaxbContext); if (Names.WSA_RELATESTO_QNAME.equals(duplicate)) { encodeAsExposed(maps, message, maps.getRelatesTo(), Names.WSA_RELATESTO_QNAME, RelatesToType.class, jaxbContext); } encodeAsExposed(maps, message, maps.getFrom(), Names.WSA_FROM_QNAME, EndpointReferenceType.class, jaxbContext); if (Names.WSA_FROM_QNAME.equals(duplicate)) { encodeAsExposed(maps, message, maps.getFrom(), Names.WSA_FROM_QNAME, EndpointReferenceType.class, jaxbContext); } if (needsFaultTo(maps)) { encodeAsExposed(maps, message, maps.getFaultTo(), Names.WSA_FAULTTO_QNAME, EndpointReferenceType.class, jaxbContext); if (Names.WSA_FAULTTO_QNAME.equals(duplicate)) { encodeAsExposed(maps, message, maps.getFaultTo(), Names.WSA_FAULTTO_QNAME, EndpointReferenceType.class, jaxbContext); } } encodeReferenceParameters(maps, message, jaxbContext); maps.setDuplicate(null); propogateAction(maps.getAction(), message); applyMAPValidation(message); } catch (JAXBException je) { LOG.log(Level.WARNING, "SOAP_HEADER_ENCODE_FAILURE_MSG", je); } } } private boolean needsReplyTo(AddressingProperties maps, SoapMessage m) { if (!MessageUtils.getContextualBoolean(m, "ws-addressing.write.optional.replyto", true)) { if (ContextUtils.isNoneAddress(maps.getReplyTo()) && m.getExchange().isOneWay()) { //one-way + none, not needed return false; } if (ContextUtils.isAnonymousAddress(maps.getReplyTo())) { //anonymous is the default if not specified, not needed return false; } } return maps.getReplyTo() != null && maps.getReplyTo().getAddress() != null && maps.getReplyTo().getAddress().getValue() != null && !(VersionTransformer.Names200408.WSA_NAMESPACE_NAME.equals(maps.getNamespaceURI()) && maps.getReplyTo().getAddress().getValue() .equals(ContextUtils.getNoneEndpointReference().getAddress().getValue())); } private boolean needsFaultTo(AddressingProperties maps) { return maps.getFaultTo() != null && maps.getFaultTo().getAddress() != null && maps.getFaultTo().getAddress().getValue() != null && !maps.getFaultTo().getAddress().getValue() .equals(maps.getReplyTo().getAddress().getValue()); } private void encodeReferenceParameters(AddressingProperties maps, SoapMessage msg, JAXBContext ctx) throws JAXBException { Element header = null; EndpointReferenceType toEpr = maps.getToEndpointReference(); if (null != toEpr) { ReferenceParametersType params = toEpr.getReferenceParameters(); if (null != params) { for (Object o : params.getAny()) { if (o instanceof Element || o instanceof JAXBElement) { if (header == null) { header = getHeaderFactory().getHeader(msg.getVersion()); } JAXBElement<?> jaxbEl = null; if (o instanceof Element) { Element e = (Element)o; Node importedNode = header.getOwnerDocument().importNode(e, true); header.appendChild(importedNode); } else { jaxbEl = (JAXBElement<?>) o; ctx.createMarshaller().marshal(jaxbEl, header); } Element lastAdded = (Element)header.getLastChild(); header.removeChild(lastAdded); addIsReferenceParameterMarkerAttribute(lastAdded, maps.getNamespaceURI()); Header holder = new Header(new QName(lastAdded.getNamespaceURI(), lastAdded.getLocalName()), lastAdded); msg.getHeaders().add(holder); } else { LOG.log(Level.WARNING, "IGNORE_NON_ELEMENT_REF_PARAM_MSG", o); } } } } } private void addIsReferenceParameterMarkerAttribute(Element lastAdded, String namespaceURI) { String pfx = lastAdded.lookupPrefix(namespaceURI); if (StringUtils.isEmpty(pfx)) { //attributes cannot be in empty namespace... if (lastAdded.lookupNamespaceURI(JAXWSAConstants.WSA_PREFIX) == null) { pfx = JAXWSAConstants.WSA_PREFIX; Attr attr = lastAdded.getOwnerDocument() .createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:wsa"); attr.setValue(namespaceURI); lastAdded.setAttributeNodeNS(attr); } else if (lastAdded.lookupNamespaceURI(JAXWSAConstants.WSA_PREFIX).equals(namespaceURI)) { pfx = JAXWSAConstants.WSA_PREFIX; } else { int cnt = 1; while (lastAdded.lookupNamespaceURI(JAXWSAConstants.WSA_PREFIX + cnt) != null) { cnt++; } pfx = JAXWSAConstants.WSA_PREFIX + cnt; Attr attr = lastAdded.getOwnerDocument() .createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:wsa" + cnt); attr.setValue(namespaceURI); lastAdded.setAttributeNodeNS(attr); } } Attr isRefParamAttr = lastAdded.getOwnerDocument().createAttributeNS(namespaceURI, pfx + ":" + IS_REFERENCE_PARAM_ATTR_NAME); isRefParamAttr.setTextContent("1"); lastAdded.setAttributeNodeNS(isRefParamAttr); } /** * Encode message in exposed version. * * @param maps the MAPs, where getNamespceURI() specifies the WS-Addressing * version to expose * @param value the value to encode * @param name the QName for the header * @param clz the class * @param header the SOAP header element * @param JAXBContext the JAXB context to use */ private <T> void encodeAsExposed(AddressingProperties maps, SoapMessage message, T value, QName name, Class<T> clz, JAXBContext context) throws JAXBException { if (value != null) { LOG.log(Level.FINE, "{0} : {1}", new Object[] {name.getLocalPart(), getLogText(value)}); boolean mu = maps.getMustUnderstand().contains(name); transformer.encodeAsExposed(message, maps.getNamespaceURI(), value, name.getLocalPart(), clz, context, mu); } } /** * Decode the MAPs from protocol-specific headers. * * @param message the SOAP message * @param the decoded MAPs * @exception SOAPFaultException if decoded MAPs are invalid */ public AddressingProperties unmarshalMAPs(SoapMessage message) { // REVISIT generate MessageAddressingHeaderRequired fault if an // expected header is missing AddressingProperties maps = null; Unmarshaller unmarshaller = null; try { List<Header> header = message.getHeaders(); if (header != null) { LOG.log(Level.FINE, "Inbound WS-Addressing headers"); Set<Element> referenceParameterHeaders = null; QName invalidCardinalityQName = null; Iterator<Header> iter = header.iterator(); while (iter.hasNext()) { Header hdr = iter.next(); if (hdr.getObject() instanceof Element) { Element headerElement = (Element)hdr.getObject(); String headerURI = headerElement.getNamespaceURI(); // Need to check the uri before getting unmarshaller else // would get wrong unmarshaller and fail to process required // headers. if (VersionTransformer.isSupported(headerURI)) { if (unmarshaller == null) { JAXBContext jaxbContext = VersionTransformer.getExposedJAXBContext(headerURI); unmarshaller = jaxbContext.createUnmarshaller(); unmarshaller.setEventHandler(null); } if (maps == null) { maps = new AddressingProperties(); maps.exposeAs(headerURI); } String localName = headerElement.getLocalName(); if (Names.WSA_MESSAGEID_NAME.equals(localName)) { invalidCardinalityQName = maps.getMessageID() != null ? Names.WSA_MESSAGEID_QNAME : null; maps.setMessageID(decodeAsNative( headerURI, AttributedURIType.class, headerElement, unmarshaller)); } else if (Names.WSA_TO_NAME.equals(localName)) { invalidCardinalityQName = maps.getTo() != null ? Names.WSA_TO_QNAME : null; AttributedURIType addr = decodeAsNative( headerURI, AttributedURIType.class, headerElement, unmarshaller); maps.setTo(EndpointReferenceUtils.getEndpointReference(addr)); } else if (Names.WSA_FROM_NAME.equals(localName)) { invalidCardinalityQName = maps.getFrom() != null ? Names.WSA_FROM_QNAME : null; maps.setFrom(decodeAsNative( headerURI, EndpointReferenceType.class, headerElement, unmarshaller)); } else if (Names.WSA_REPLYTO_NAME.equals(localName)) { invalidCardinalityQName = maps.getReplyTo() != null ? Names.WSA_REPLYTO_QNAME : null; maps.setReplyTo(decodeAsNative( headerURI, EndpointReferenceType.class, headerElement, unmarshaller)); } else if (Names.WSA_FAULTTO_NAME.equals(localName)) { invalidCardinalityQName = maps.getFaultTo() != null ? Names.WSA_FAULTTO_QNAME : null; maps.setFaultTo(decodeAsNative( headerURI, EndpointReferenceType.class, headerElement, unmarshaller)); } else if (Names.WSA_RELATESTO_NAME.equals(localName)) { maps.setRelatesTo(decodeAsNative( headerURI, RelatesToType.class, headerElement, unmarshaller)); } else if (Names.WSA_ACTION_NAME.equals(localName)) { invalidCardinalityQName = maps.getAction() != null ? Names.WSA_ACTION_QNAME : null; maps.setAction(decodeAsNative( headerURI, AttributedURIType.class, headerElement, unmarshaller)); } } else if (null != headerElement.getAttribute(IS_REFERENCE_PARAM_ATTR_NAME)) { if (null == referenceParameterHeaders) { referenceParameterHeaders = new HashSet<>(); } referenceParameterHeaders.add(headerElement); } else if (headerURI.contains(Names.WSA_NAMESPACE_PATTERN)) { LOG.log(Level.WARNING, "UNSUPPORTED_VERSION_MSG", headerURI); } } } if (maps != null && !MessageUtils.isRequestor(message) && maps.getReplyTo() == null) { //per spec, if unspecified, replyTo is anon AttributedURIType address = ContextUtils.getAttributedURI(Names.WSA_ANONYMOUS_ADDRESS); EndpointReferenceType replyTo = ContextUtils.WSA_OBJECT_FACTORY.createEndpointReferenceType(); replyTo.setAddress(address); maps.setReplyTo(replyTo); } if (null != referenceParameterHeaders && null != maps) { decodeReferenceParameters(referenceParameterHeaders, maps, unmarshaller); } if (invalidCardinalityQName != null) { storeInvalidCardinalityFault(message, invalidCardinalityQName); } } } catch (JAXBException je) { LOG.log(Level.WARNING, "SOAP_HEADER_DECODE_FAILURE_MSG", je); } finally { JAXBUtils.closeUnmarshaller(unmarshaller); } return maps; } private void storeInvalidCardinalityFault(SoapMessage message, QName wsaHeaderName) { LOG.log(Level.WARNING, "INVALID_CARDINALITY_MESSAGE", wsaHeaderName); String reason = BUNDLE.getString("INVALID_ADDRESSING_PROPERTY_MESSAGE"); ContextUtils.storeMAPFaultName(Names.INVALID_CARDINALITY_NAME, message); ContextUtils.storeMAPFaultReason(reason, message); } private void decodeReferenceParameters(Set<Element> referenceParameterHeaders, AddressingProperties maps, Unmarshaller unmarshaller) throws JAXBException { EndpointReferenceType toEpr = maps.getToEndpointReference(); if (null != toEpr) { for (Element e : referenceParameterHeaders) { if (DOMUtils.getChild(e, Node.ELEMENT_NODE) == null) { JAXBElement<String> el = unmarshaller.unmarshal(e, String.class); ContextUtils.applyReferenceParam(toEpr, el); } else { ContextUtils.applyReferenceParam(toEpr, e); } } } } /** * Decodes a MAP from a exposed version. * * @param encodedAs specifies the encoded version * @param clz the class * @param headerElement the SOAP header element * @param marshaller the JAXB marshaller to use * @return the decoded value */ public <T> T decodeAsNative(String encodedAs, Class<T> clz, Element headerElement, Unmarshaller unmarshaller) throws JAXBException { T value = clz.cast(transformer.decodeAsNative(encodedAs, clz, headerElement, unmarshaller)); LOG.log(Level.FINE, "{0} : {1}", new Object[] {headerElement.getLocalName(), getLogText(value)}); return value; } /** * Return a text representation of a header value for logging. * * @param <T> header type * @param value header value * @return */ private <T> String getLogText(T value) { String text = "unknown"; if (value == null) { text = "null"; } else if (value instanceof AttributedURIType) { text = ((AttributedURIType)value).getValue(); } else if (value instanceof EndpointReferenceType) { text = ((EndpointReferenceType)value).getAddress() != null ? ((EndpointReferenceType)value).getAddress().getValue() : "null"; } else if (value instanceof RelatesToType) { text = ((RelatesToType)value).getValue(); } return text; } /** * Decode the MAPs from protocol-specific headers. * * @param message the messsage * @param the decoded MAPs * @exception SOAPFaultException if decoded MAPs are invalid */ private AddressingProperties decode(SoapMessage message) { // REVISIT generate MessageAddressingHeaderRequired fault if an // expected header is missing return unmarshalMAPs(message); } /** * Encodes an MAP as a SOAP header. * * @param message the message to store the headers on * @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 context to use */ protected <T> void encodeMAP(SoapMessage message, T value, QName qname, Class<T> clz, JAXBContext ctx, boolean mustUnderstand) throws JAXBException { SoapHeader h = new SoapHeader(qname, new JAXBElement<T>(qname, clz, value), new JAXBDataBinding(ctx)); h.setMustUnderstand(mustUnderstand); message.getHeaders().add(h); } /** * Decodes a MAP from a SOAP header. * * @param clz the class * @param headerElement the SOAP header element * @param marshaller the JAXB marshaller to use * @return the decoded value */ protected <T> T decodeMAP(Class<T> clz, Element headerElement, Unmarshaller unmarshaller) throws JAXBException { JAXBElement<T> element = unmarshaller.unmarshal(headerElement, clz); return element.getValue(); } /** * Discard any pre-existing MAP headers - this may occur if the runtime * re-uses a SOAP message. * * @param header the SOAP header * @param maps the current MAPs */ private void discardMAPs(List<Header> header, AddressingProperties maps) { Iterator<Header> iter = header.iterator(); while (iter.hasNext()) { Header hdr = iter.next(); if (Names.WSA_NAMESPACE_NAME.equals(hdr.getName().getNamespaceURI()) || Names200408.WSA_NAMESPACE_NAME.equals(hdr.getName().getNamespaceURI())) { iter.remove(); } } } /** * Propogate action to SOAPAction header * * @param action the Action property * @param message the SOAP message */ private void propogateAction(AttributedURIType action, SoapMessage message) { if (!(action == null || "".equals(action.getValue()))) { Map<String, List<String>> mimeHeaders = CastUtils.cast((Map<?, ?>) message.get(Message.MIME_HEADERS)); if (mimeHeaders != null) { List<String> soapActionHeaders = mimeHeaders.get("SOAPAction"); // only propogate to SOAPAction header if currently non-empty if (!(soapActionHeaders == null || soapActionHeaders.size() == 0 || "".equals(soapActionHeaders.get(0)))) { LOG.log(Level.FINE, "encoding wsa:Action in SOAPAction header {0}", action.getValue()); soapActionHeaders.clear(); soapActionHeaders.add("\"" + action.getValue() + "\""); } } } } /** * Apply results of validation of incoming MAPs. * * @param message the message * @exception SOAPFaultException if the MAPs are invalid * @exception SOAPException if SOAPFault cannot be constructed */ private void applyMAPValidation(SoapMessage message) { String faultName = ContextUtils.retrieveMAPFaultName(message); if (faultName != null) { String reason = ContextUtils.retrieveMAPFaultReason(message); throw createSOAPFaut(faultName, Names.WSA_NAMESPACE_NAME, reason); } } /** * Create a SoapFault. * * @param localName the fault local name * @param prefix the fault prefix * @param namespace the fault namespace * @param reason the fault reason * @return a new SoapFault */ private SoapFault createSOAPFaut(String localName, String namespace, String reason) { return new SoapFault(reason, new QName(namespace, localName)); } /** * Cache exchange for correlated response * * @param message the current message * @param maps the addressing properties */ private void cacheExchange(SoapMessage message, AddressingProperties maps) { if (ContextUtils.isRequestor(message) && !message.getExchange().isOneWay()) { uncorrelatedExchanges.put(maps.getMessageID().getValue(), message.getExchange()); } } /** * Restore exchange for correlated response * * @param message the current message * @param maps the addressing properties */ private void restoreExchange(SoapMessage message, AddressingProperties maps) { if (maps != null && maps.getRelatesTo() != null && !Names.WSA_UNSPECIFIED_RELATIONSHIP.equals(maps.getRelatesTo().getValue()) && isRelationshipReply(maps.getRelatesTo())) { Exchange correlatedExchange = uncorrelatedExchanges.remove(maps.getRelatesTo().getValue()); if (correlatedExchange != null) { synchronized (correlatedExchange) { message.setExchange(correlatedExchange); } } else if (ContextUtils.isRequestor(message) && !message.getExchange().isOneWay()) { if (ContextUtils.retrieveDeferUncorrelatedMessageAbort(message)) { LOG.fine("deferring uncorrelated message abort"); ContextUtils.storeDeferredUncorrelatedMessageAbort(message); } else if (!MessageUtils.getContextualBoolean(message, "org.apache.cxf.ws.addressing.MAPAggregator.addressingDisabled", false)) { //see if it can directly be correlated with the out message: Message outmsg = message.getExchange().getOutMessage(); AddressingProperties outp = outmsg != null ? ContextUtils.retrieveMAPs(outmsg, false, true, false) : null; if (outp == null || !outp.getMessageID().getValue().equals(maps.getRelatesTo().getValue())) { LOG.log(Level.WARNING, "CORRELATION_FAILURE_MSG"); message.getInterceptorChain().abort(); } } } } else if (isRequestor(message)) { if (maps == null) { Message m = message.getExchange().getOutMessage(); maps = ContextUtils.retrieveMAPs(m, false, true, false); if (maps != null) { Exchange ex = uncorrelatedExchanges.get(maps.getMessageID().getValue()); if (ex == message.getExchange()) { uncorrelatedExchanges.remove(maps.getMessageID().getValue()); LOG.log(Level.WARNING, "RESPONSE_NOT_USING_WSADDRESSING"); } } } else if (maps.getRelatesTo() == null && maps.getAction() != null && (Names.WSA_DEFAULT_FAULT_ACTION.equals(maps.getAction().getValue()) || Names.WSA_DEFAULT_SOAP_FAULT_ACTION.equals(maps.getAction().getValue()))) { //there is an Action header that points to a fault and no relatesTo. Use the out map for the ID Message m = message.getExchange().getOutMessage(); maps = ContextUtils.retrieveMAPs(m, false, true, false); if (maps != null) { uncorrelatedExchanges.remove(maps.getMessageID().getValue()); } } } } /** * @param relatesTo the current RelatesTo * @return true iff the relationship type is reply */ private boolean isRelationshipReply(RelatesToType relatesTo) { return Names.WSA_RELATIONSHIP_REPLY.equals(relatesTo.getRelationshipType()); } /** * Marks a message as partial response * * @param message the current message */ private void markPartialResponse(SoapMessage message, AddressingProperties maps) { if (ContextUtils.isRequestor(message) && null != maps && (null == maps.getRelatesTo() || (null != maps.getRelatesTo() && Names.WSA_UNSPECIFIED_RELATIONSHIP.equals(maps.getRelatesTo().getValue())))) { message.put(Message.PARTIAL_RESPONSE_MESSAGE, Boolean.TRUE); } } protected HeaderFactory getHeaderFactory() { if (headerFactory == null) { headerFactory = new HeaderFactory() { public Element getHeader(SoapVersion soapversion) { Document doc = DOMUtils.createDocument(); return doc.createElementNS(soapversion.getHeader().getNamespaceURI(), soapversion.getHeader().getLocalPart()); } }; } return headerFactory; } protected void setHeaderFactory(HeaderFactory factory) { headerFactory = factory; } public interface HeaderFactory { Element getHeader(SoapVersion soapversion); } }