diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapElement.java b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapElement.java index bb23168f..4bb9e7c1 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapElement.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapElement.java @@ -36,6 +36,8 @@ class SaajSoapElement implements SoapElement { private final SOAPElement element; + private SaajImplementation implementation; + SaajSoapElement(SOAPElement element) { Assert.notNull(element, "element must not be null"); this.element = element; @@ -94,17 +96,20 @@ class SaajSoapElement implements SoapElement { } protected final SaajImplementation getImplementation() { - if (SaajUtils.getSaajVersion() == SaajUtils.SAAJ_13) { - return Saaj13Implementation.getInstance(); - } - else if (SaajUtils.getSaajVersion() == SaajUtils.SAAJ_12) { - return Saaj12Implementation.getInstance(); - } - else if (SaajUtils.getSaajVersion() == SaajUtils.SAAJ_11) { - return Saaj11Implementation.getInstance(); - } - else { - throw new IllegalStateException("Could not find SAAJ on the classpath"); + if (implementation == null) { + if (SaajUtils.getSaajVersion(element) == SaajUtils.SAAJ_13) { + implementation = Saaj13Implementation.getInstance(); + } + else if (SaajUtils.getSaajVersion(element) == SaajUtils.SAAJ_12) { + implementation = Saaj12Implementation.getInstance(); + } + else if (SaajUtils.getSaajVersion(element) == SaajUtils.SAAJ_11) { + implementation = Saaj11Implementation.getInstance(); + } + else { + throw new IllegalStateException("Could not find SAAJ on the classpath"); + } } + return implementation; } } diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessage.java b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessage.java index 8ec092f7..a25266aa 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessage.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessage.java @@ -59,6 +59,8 @@ public class SaajSoapMessage extends AbstractSoapMessage { private final boolean langAttributeOnSoap11FaulString; + private SaajImplementation implementation; + /** * Create a new SaajSoapMessage based on the given SAAJ SOAPMessage. * @@ -77,12 +79,12 @@ public class SaajSoapMessage extends AbstractSoapMessage { */ public SaajSoapMessage(SOAPMessage soapMessage, boolean langAttributeOnSoap11FaulString) { Assert.notNull(soapMessage, "soapMessage must not be null"); + saajMessage = soapMessage; + this.langAttributeOnSoap11FaulString = langAttributeOnSoap11FaulString; MimeHeaders headers = getImplementation().getMimeHeaders(soapMessage); if (ObjectUtils.isEmpty(headers.getHeader(TransportConstants.HEADER_SOAP_ACTION))) { headers.addHeader(TransportConstants.HEADER_SOAP_ACTION, "\"\""); } - saajMessage = soapMessage; - this.langAttributeOnSoap11FaulString = langAttributeOnSoap11FaulString; } /** Return the SAAJ SOAPMessage that this SaajSoapMessage is based on. */ @@ -169,7 +171,7 @@ public class SaajSoapMessage extends AbstractSoapMessage { } public boolean isXopPackage() { - if (SaajUtils.getSaajVersion() >= SaajUtils.SAAJ_13) { + if (SaajUtils.getSaajVersion(saajMessage) >= SaajUtils.SAAJ_13) { SOAPPart saajPart = saajMessage.getSOAPPart(); String[] contentTypes = saajPart.getMimeHeader(TransportConstants.HEADER_CONTENT_TYPE); for (int i = 0; i < contentTypes.length; i++) { @@ -182,7 +184,7 @@ public class SaajSoapMessage extends AbstractSoapMessage { } public boolean convertToXopPackage() { - if (SaajUtils.getSaajVersion() >= SaajUtils.SAAJ_13) { + if (SaajUtils.getSaajVersion(saajMessage) >= SaajUtils.SAAJ_13) { convertMessageToXop(); convertPartToXop(); return true; @@ -244,19 +246,22 @@ public class SaajSoapMessage extends AbstractSoapMessage { return new SaajAttachment(saajAttachment); } - protected SaajImplementation getImplementation() { - if (SaajUtils.getSaajVersion() == SaajUtils.SAAJ_13) { - return Saaj13Implementation.getInstance(); - } - else if (SaajUtils.getSaajVersion() == SaajUtils.SAAJ_12) { - return Saaj12Implementation.getInstance(); - } - else if (SaajUtils.getSaajVersion() == SaajUtils.SAAJ_11) { - return Saaj11Implementation.getInstance(); - } - else { - throw new IllegalStateException("Could not find SAAJ on the classpath"); + protected final SaajImplementation getImplementation() { + if (implementation == null) { + if (SaajUtils.getSaajVersion(saajMessage) == SaajUtils.SAAJ_13) { + implementation = Saaj13Implementation.getInstance(); + } + else if (SaajUtils.getSaajVersion(saajMessage) == SaajUtils.SAAJ_12) { + implementation = Saaj12Implementation.getInstance(); + } + else if (SaajUtils.getSaajVersion(saajMessage) == SaajUtils.SAAJ_11) { + implementation = Saaj11Implementation.getInstance(); + } + else { + throw new IllegalStateException("Could not find SAAJ on the classpath"); + } } + return implementation; } public String toString() { diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/support/SaajUtils.java b/core/src/main/java/org/springframework/ws/soap/saaj/support/SaajUtils.java index 1527650e..921becda 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/support/SaajUtils.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/support/SaajUtils.java @@ -18,7 +18,12 @@ package org.springframework.ws.soap.saaj.support; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map; import javax.xml.namespace.QName; import javax.xml.soap.MessageFactory; import javax.xml.soap.MimeHeaders; @@ -29,13 +34,16 @@ import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPMessage; +import org.w3c.dom.Element; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.core.io.Resource; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.ws.transport.TransportConstants; import org.springframework.xml.namespace.QNameUtils; -import org.w3c.dom.Element; /** * Collection of generic utility methods to work with SAAJ. Includes conversion from SAAJ {@link Name} objects to {@link @@ -48,6 +56,11 @@ import org.w3c.dom.Element; */ public abstract class SaajUtils { + // The exception message thrown by WebLogic 9 + private static final String WEBLOGIC_9_SAAJ_EXCEPTION_MESSAGE = "This class does not support SAAJ 1.1"; + + private static final Log logger = LogFactory.getLog(SaajUtils.class); + public static final int SAAJ_11 = 0; public static final int SAAJ_12 = 1; @@ -56,6 +69,9 @@ public abstract class SaajUtils { private static final String SAAJ_13_CLASS_NAME = "javax.xml.soap.SAAJMetaFactory"; + // Maps SOAPElement class names to Integer SAAJ versions (SAAJ_11, SAAJ_12, SAAJ_13) + private static final Map saajVersions = Collections.synchronizedMap(new HashMap()); + private static int saajVersion = SAAJ_12; static { @@ -127,6 +143,112 @@ public abstract class SaajUtils { return saajVersion; } + /** + * Gets the SAAJ version for the specified {@link SOAPMessage}. + * + * @return a code comparable to the SAAJ_XX codes in this class + * @see #SAAJ_11 + * @see #SAAJ_12 + * @see #SAAJ_13 + */ + public static int getSaajVersion(SOAPMessage soapMessage) { + Assert.notNull(soapMessage, "'soapMessage' must not be null"); + try { + return getSaajVersion(soapMessage.getSOAPPart().getEnvelope()); + } + catch (SOAPException e) { + return SAAJ_11; + } + } + + /** + * Gets the SAAJ version for the specified {@link javax.xml.soap.SOAPElement}. + * + * @return a code comparable to the SAAJ_XX codes in this class + * @see #SAAJ_11 + * @see #SAAJ_12 + * @see #SAAJ_13 + */ + public static int getSaajVersion(SOAPElement soapElement) { + Assert.notNull(soapElement, "'soapElement' must not be null"); + Class soapElementClass = soapElement.getClass(); + Integer saajVersion = (Integer) saajVersions.get(soapElementClass.getName()); + if (saajVersion == null) { + if (isSaaj12(soapElement)) { + if (isSaaj13(soapElement)) { + saajVersion = new Integer(SAAJ_13); + } + else { + saajVersion = new Integer(SAAJ_12); + } + } else { + saajVersion = new Integer(SAAJ_11); + } + saajVersions.put(soapElementClass.getName(), saajVersion); + if (logger.isTraceEnabled()) { + logger.trace("SOAPElement [" + soapElement.getClass().getName() + "] implements " + + getSaajVersionString(saajVersion.intValue())); + } + } + return saajVersion.intValue(); + } + + private static boolean isSaaj13(SOAPElement soapElement) { + try { + Method m = soapElement.getClass().getMethod("getElementQName", new Class[0]); + // we might be using the SAAJ 1.3 API, while the impl is 1.2 + // let's see if the method is not abstract + if (Modifier.isAbstract(m.getModifiers())) { + logger.warn("Detected SAAJ API version 1.3, while implementation provides version 1.2. " + + "Please replace the SAAJ API jar with a version that corresponds to your runtime" + + " implementation (which might be provided by your app server)."); + return false; + } + else { + return true; + } + } + catch (NoSuchMethodException e) { + // getElementQName not found + return false; + } + } + + /** + * Checks whether we can find a SAAJ 1.2 implementation, being aware of the broken WebLogic 9 SAAJ implementation. + *

+ * WebLogic 9 does implement SAAJ 1.2, but throws UnsupportedOperationExceptions when a SAAJ 1.2 method is called. + */ + private static boolean isSaaj12(SOAPElement soapElement) { + try { + Method m = soapElement.getClass().getMethod("getPrefix", new Class[0]); + // we might be using the SAAJ 1.2 API, while the impl is 1.1 + // let's see if the method is not abstract + if (Modifier.isAbstract(m.getModifiers())) { + logger.warn("Detected SAAJ API version 1.2, while implementation provides version 1.1. " + + "Please replace the SAAJ API jar with a version that corresponds to your runtime " + + "implementation (which might be provided by your app server)."); + return false; + } + else { + soapElement.getPrefix(); + return true; + } + } + catch (NoSuchMethodException e) { + // getPrefix not found + return false; + } + catch (UnsupportedOperationException ex) { + // getPrefix results in UOE, let's see if we're dealing with WL 9 + if (WEBLOGIC_9_SAAJ_EXCEPTION_MESSAGE.equals(ex.getMessage())) { + return false; + } else { + throw ex; + } + } + } + /** * Returns the SAAJ version as a String. The returned string will be "SAAJ 1.3", "SAAJ * 1.2", or "SAAJ 1.1". @@ -135,6 +257,10 @@ public abstract class SaajUtils { * @see #getSaajVersion() */ public static String getSaajVersionString() { + return getSaajVersionString(saajVersion); + } + + private static String getSaajVersionString(int saajVersion) { if (saajVersion >= SaajUtils.SAAJ_13) { return "SAAJ 1.3"; } @@ -166,7 +292,7 @@ public abstract class SaajUtils { } else if (StringUtils.hasLength(qName.getNamespaceURI())) { Iterator prefixes; - if (getSaajVersion() == SAAJ_11) { + if (getSaajVersion(resolveElement) == SAAJ_11) { prefixes = resolveElement.getNamespacePrefixes(); } else { diff --git a/core/src/test/java/org/springframework/ws/soap/saaj/support/SaajUtilsTest.java b/core/src/test/java/org/springframework/ws/soap/saaj/support/SaajUtilsTest.java index 3ccc0642..5c119ea6 100644 --- a/core/src/test/java/org/springframework/ws/soap/saaj/support/SaajUtilsTest.java +++ b/core/src/test/java/org/springframework/ws/soap/saaj/support/SaajUtilsTest.java @@ -25,9 +25,10 @@ import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPMessage; import org.custommonkey.xmlunit.XMLTestCase; +import org.w3c.dom.Document; + import org.springframework.core.io.ClassPathResource; import org.springframework.util.StringUtils; -import org.w3c.dom.Document; public class SaajUtilsTest extends XMLTestCase { @@ -114,4 +115,5 @@ public class SaajUtilsTest extends XMLTestCase { public void testGetSaajVersion() throws Exception { assertEquals("Invalid SAAJ version", SaajUtils.SAAJ_13, SaajUtils.getSaajVersion()); } + } \ No newline at end of file