diff --git a/build.gradle b/build.gradle index a95abc58..5da3fc30 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { configure(allprojects) { group = "org.springframework.ws" - ext.springVersion = "3.2.4.RELEASE" + ext.springVersion = "4.0.3.RELEASE" ext.axiomVersion = "1.2.14" apply plugin: "java" @@ -55,6 +55,7 @@ configure(allprojects) { } repositories { + mavenLocal() maven { url 'http://repo.spring.io/libs-release' } } @@ -64,8 +65,8 @@ configure(allprojects) { testCompile("junit:junit:4.10") testCompile("org.easymock:easymock:3.1") - testCompile("xmlunit:xmlunit:1.1") - testRuntime("org.codehaus.woodstox:woodstox-core-asl:4.1.3") + testCompile("xmlunit:xmlunit:1.5") + testRuntime("org.codehaus.woodstox:woodstox-core-asl:4.2.0") } ext.javadocLinks = [ @@ -175,7 +176,7 @@ project('spring-ws-core') { optional("wsdl4j:wsdl4j:1.6.1") // Transport - provided("javax.servlet:servlet-api:2.5") + provided("javax.servlet:javax.servlet-api:3.1.0") optional("org.apache.httpcomponents:httpclient:4.2.5") optional("commons-httpclient:commons-httpclient:3.1") testCompile("org.mortbay.jetty:jetty:6.1.26") @@ -251,7 +252,7 @@ project('spring-ws-test') { compile("org.springframework:spring-context:$springVersion") - compile("xmlunit:xmlunit:1.1") + compile("xmlunit:xmlunit:1.5") testCompile("org.springframework:spring-test:$springVersion") } diff --git a/spring-ws-core/src/main/java/org/springframework/ws/soap/axiom/AxiomHandler.java b/spring-ws-core/src/main/java/org/springframework/ws/soap/axiom/AxiomHandler.java index f742a2fd..20b3ea27 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/soap/axiom/AxiomHandler.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/soap/axiom/AxiomHandler.java @@ -20,12 +20,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamConstants; -import org.springframework.util.Assert; -import org.springframework.xml.namespace.QNameUtils; - import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMContainer; import org.apache.axiom.om.OMElement; @@ -37,145 +35,190 @@ import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.LexicalHandler; +import org.springframework.util.Assert; +import org.springframework.xml.namespace.QNameUtils; + /** - * Specific SAX {@link ContentHandler} and {@link LexicalHandler} that adds the resulting AXIOM OMElement to a specified - * parent element when endDocument is called. Used for returing SAXResults from Axiom - * elements. - * + * Specific SAX {@link ContentHandler} and {@link LexicalHandler} that adds the resulting + * AXIOM OMElement to a specified parent element when endDocument is called. + * Used for returing SAXResults from Axiom elements. * @author Arjen Poutsma * @since 1.0.0 */ class AxiomHandler implements ContentHandler, LexicalHandler { - private final OMFactory factory; + private final OMFactory factory; - private final List elements = new ArrayList(); + private final List elements = new ArrayList(); - private Map namespaces = new HashMap(); + private final OMContainer container; - private final OMContainer container; + private int charactersType = XMLStreamConstants.CHARACTERS; - private int charactersType = XMLStreamConstants.CHARACTERS; + private List> namespaceMappings = + new ArrayList>(); - AxiomHandler(OMContainer container, OMFactory factory) { - Assert.notNull(container, "'container' must not be null"); - Assert.notNull(factory, "'factory' must not be null"); - this.factory = factory; - this.container = container; - } + AxiomHandler(OMContainer container, OMFactory factory) { + Assert.notNull(container, "'container' must not be null"); + Assert.notNull(factory, "'factory' must not be null"); + this.factory = factory; + this.container = container; + } - private OMContainer getParent() { - if (!elements.isEmpty()) { - return elements.get(elements.size() - 1); - } - else { - return container; - } - } + private OMContainer getParent() { + if (!elements.isEmpty()) { + return elements.get(elements.size() - 1); + } + else { + return container; + } + } - public void startPrefixMapping(String prefix, String uri) throws SAXException { - namespaces.put(prefix, uri); - } + public void startDocument() throws SAXException { + removeAllNamespaceMappings(); + newNamespaceMapping(); + } - public void endPrefixMapping(String prefix) throws SAXException { - namespaces.remove(prefix); - } + public void endDocument() throws SAXException { + removeAllNamespaceMappings(); + } - public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { - OMContainer parent = getParent(); - OMNamespace ns = factory.createOMNamespace(uri, QNameUtils.toQName(uri, qName).getPrefix()); - OMElement element = factory.createOMElement(localName, ns, parent); - for (Map.Entry entry : namespaces.entrySet()) { - String prefix = entry.getKey(); - if (prefix.length() == 0) { - element.declareDefaultNamespace((String) entry.getValue()); - } - else { - element.declareNamespace((String) entry.getValue(), prefix); - } - } - for (int i = 0; i < atts.getLength(); i++) { - QName attrName = QNameUtils.toQName(atts.getURI(i), atts.getQName(i)); - String value = atts.getValue(i); - if (!atts.getQName(i).startsWith("xmlns")) { - OMNamespace namespace = factory.createOMNamespace(attrName.getNamespaceURI(), attrName.getPrefix()); - OMAttribute attribute = factory.createOMAttribute(attrName.getLocalPart(), namespace, value); - element.addAttribute(attribute); - } - } + public void startPrefixMapping(String prefix, String uri) throws SAXException { + currentNamespaceMapping().put(prefix, uri); + } - elements.add(element); - } + public void endPrefixMapping(String prefix) throws SAXException { + } - public void endElement(String uri, String localName, String qName) throws SAXException { - elements.remove(elements.size() - 1); - } + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + OMContainer parent = getParent(); + OMNamespace ns = factory.createOMNamespace(uri, + QNameUtils.toQName(uri, qName).getPrefix()); + OMElement element = factory.createOMElement(localName, ns, parent); - public void characters(char ch[], int start, int length) throws SAXException { - String data = new String(ch, start, length); - OMContainer parent = getParent(); - factory.createOMText(parent, data, charactersType); - } + // declare namespaces + Map namespaceMappings = currentNamespaceMapping(); + for (Map.Entry entry : namespaceMappings.entrySet()) { + String prefix = entry.getKey(); + String namespaceUri = entry.getValue(); - public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { - charactersType = XMLStreamConstants.SPACE; - characters(ch, start, length); - charactersType = XMLStreamConstants.CHARACTERS; - } + if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { + element.declareDefaultNamespace(namespaceUri); + } else { + element.declareNamespace(namespaceUri, prefix); + } + } + // declare attributes + for (int i = 0; i < attributes.getLength(); i++) { + QName attrName = + QNameUtils.toQName(attributes.getURI(i), attributes.getQName(i)); + if (!isNamespaceDeclaration(attrName)) { + OMNamespace namespace = + factory.createOMNamespace(attrName.getNamespaceURI(), + attrName.getPrefix()); + OMAttribute attribute = + factory.createOMAttribute(attrName.getLocalPart(), namespace, + attributes.getValue(i)); + element.addAttribute(attribute); + } + } + elements.add(element); + newNamespaceMapping(); + } - public void processingInstruction(String target, String data) throws SAXException { - OMContainer parent = getParent(); - factory.createOMProcessingInstruction(parent, target, data); - } + private boolean isNamespaceDeclaration(QName qName) { + String prefix = qName.getPrefix(); + String localPart = qName.getLocalPart(); + return (XMLConstants.XMLNS_ATTRIBUTE.equals(localPart) && prefix.length() == 0) || + (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) && localPart.length() != 0); + } - public void comment(char ch[], int start, int length) throws SAXException { - String content = new String(ch, start, length); - OMContainer parent = getParent(); - factory.createOMComment(parent, content); - } + public void endElement(String uri, String localName, String qName) + throws SAXException { + elements.remove(elements.size() - 1); + removeNamespaceMapping(); + } - public void startCDATA() throws SAXException { - charactersType = XMLStreamConstants.CDATA; - } + public void characters(char ch[], int start, int length) throws SAXException { + String data = new String(ch, start, length); + OMContainer parent = getParent(); + factory.createOMText(parent, data, charactersType); + } - public void endCDATA() throws SAXException { - charactersType = XMLStreamConstants.CHARACTERS; - } + public void ignorableWhitespace(char ch[], int start, int length) + throws SAXException { + charactersType = XMLStreamConstants.SPACE; + characters(ch, start, length); + charactersType = XMLStreamConstants.CHARACTERS; + } - public void startEntity(String name) throws SAXException { - if (!isPredefinedEntityReference(name)) { - charactersType = XMLStreamConstants.ENTITY_REFERENCE; - } - } + public void processingInstruction(String target, String data) throws SAXException { + OMContainer parent = getParent(); + factory.createOMProcessingInstruction(parent, target, data); + } - public void endEntity(String name) throws SAXException { - charactersType = XMLStreamConstants.CHARACTERS; - } + public void comment(char ch[], int start, int length) throws SAXException { + String content = new String(ch, start, length); + OMContainer parent = getParent(); + factory.createOMComment(parent, content); + } - private boolean isPredefinedEntityReference(String name) { - return "lt".equals(name) || "gt".equals(name) || "amp".equals(name) || "quot".equals(name) || - "apos".equals(name); - } + public void startCDATA() throws SAXException { + charactersType = XMLStreamConstants.CDATA; + } + + public void endCDATA() throws SAXException { + charactersType = XMLStreamConstants.CHARACTERS; + } + + public void startEntity(String name) throws SAXException { + if (!isPredefinedEntityReference(name)) { + charactersType = XMLStreamConstants.ENTITY_REFERENCE; + } + } + + public void endEntity(String name) throws SAXException { + charactersType = XMLStreamConstants.CHARACTERS; + } + + private boolean isPredefinedEntityReference(String name) { + return "lt".equals(name) || "gt".equals(name) || "amp".equals(name) || + "quot".equals(name) || + "apos".equals(name); + } /* * Unsupported */ - public void setDocumentLocator(Locator locator) { - } + public void setDocumentLocator(Locator locator) { + } - public void startDocument() throws SAXException { - } + public void skippedEntity(String name) throws SAXException { + } - public void endDocument() throws SAXException { - } + public void startDTD(String name, String publicId, String systemId) + throws SAXException { + } - public void skippedEntity(String name) throws SAXException { - } + public void endDTD() throws SAXException { + } - public void startDTD(String name, String publicId, String systemId) throws SAXException { - } + private Map currentNamespaceMapping() { + return namespaceMappings.get(namespaceMappings.size() - 1); + } + + private void newNamespaceMapping() { + namespaceMappings.add(new HashMap()); + } + + private void removeNamespaceMapping() { + namespaceMappings.remove(namespaceMappings.size() - 1); + } + + private void removeAllNamespaceMappings() { + namespaceMappings.clear(); + } - public void endDTD() throws SAXException { - } } diff --git a/spring-ws-core/src/test/java/org/springframework/ws/soap/axiom/AxiomHandlerTest.java b/spring-ws-core/src/test/java/org/springframework/ws/soap/axiom/AxiomHandlerTest.java index b50f3099..b0d2017e 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/soap/axiom/AxiomHandlerTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/soap/axiom/AxiomHandlerTest.java @@ -16,24 +16,33 @@ package org.springframework.ws.soap.axiom; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.StringReader; import java.util.Iterator; +import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXResult; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMDocument; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNamespace; -import org.junit.Before; -import org.junit.Test; -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; - import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; public class AxiomHandlerTest { @@ -158,4 +167,48 @@ public class AxiomHandlerTest { result.serialize(bos); assertXMLEqual("Invalid result", XML_3_ENTITY, bos.toString("UTF-8")); } + + @Test + public void testTransformDom() throws Exception { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + String XML = "" + + "" + + ""; + + Document document = + documentBuilder.parse(new ByteArrayInputStream(XML.getBytes("UTF-8"))); + + DOMSource domSource = new DOMSource(document); + handler = new AxiomHandler(result, factory); + + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + SAXResult saxResult = new SAXResult(handler); + transformer.transform(domSource, saxResult); + + OMElement root = result.getOMDocumentElement(); + assertEquals(2, getNamespaceCount(root)); + NamespaceContext namespaceContext = root.getNamespaceContext(false); + assertEquals("http://www.springframework.org/spring-ws", namespaceContext.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX)); + assertEquals("http://www.springframework.org/spring-ws/attr", + namespaceContext.getNamespaceURI("attr")); + + OMElement child = root.getFirstElement(); + assertEquals(1, getNamespaceCount(child)); + namespaceContext = child.getNamespaceContext(false); + assertEquals("http://www.springframework.org/spring-ws", namespaceContext.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX)); + assertEquals("http://www.springframework.org/spring-ws/child", namespaceContext.getNamespaceURI("prefix")); + } + + private int getNamespaceCount(OMElement element) { + int i = 0; + Iterator namespaces = element.getAllDeclaredNamespaces(); + while (namespaces.hasNext()) { + namespaces.next(); + i++; + } + return i; + } + } diff --git a/spring-ws-support/src/main/java/org/springframework/ws/transport/jms/JmsMessageReceiver.java b/spring-ws-support/src/main/java/org/springframework/ws/transport/jms/JmsMessageReceiver.java index 3514c457..0f89ee39 100644 --- a/spring-ws-support/src/main/java/org/springframework/ws/transport/jms/JmsMessageReceiver.java +++ b/spring-ws-support/src/main/java/org/springframework/ws/transport/jms/JmsMessageReceiver.java @@ -31,7 +31,7 @@ import org.springframework.ws.transport.support.SimpleWebServiceMessageReceiverO * textMessageEncoding property, which determines the encoding used to read from and write to * TextMessages. This property defaults to UTF-8. *

- * Used by {@link WebServiceMessageListener} and {@link WebServiceMessageDrivenBean}. + * Used by {@link WebServiceMessageListener}. * * @author Arjen Poutsma * @since 1.5.0 diff --git a/spring-ws-support/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageDrivenBean.java b/spring-ws-support/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageDrivenBean.java deleted file mode 100644 index 32c862a8..00000000 --- a/spring-ws-support/src/main/java/org/springframework/ws/transport/jms/WebServiceMessageDrivenBean.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2005-2010 the original author or authors. - * - * 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.springframework.ws.transport.jms; - -import javax.ejb.EJBException; -import javax.ejb.MessageDrivenBean; -import javax.jms.Connection; -import javax.jms.ConnectionFactory; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; -import javax.naming.NamingException; - -import org.springframework.ejb.support.AbstractJmsMessageDrivenBean; -import org.springframework.jms.connection.ConnectionFactoryUtils; -import org.springframework.jms.core.MessagePostProcessor; -import org.springframework.jms.support.JmsUtils; -import org.springframework.jndi.JndiLookupFailureException; -import org.springframework.ws.WebServiceMessageFactory; -import org.springframework.ws.transport.WebServiceMessageReceiver; - -/** - * EJB {@link MessageDrivenBean} that can be used to handleMessage incoming JMS messages. - *

- * This class needs a JMS {@link ConnectionFactory}, a {@link WebServiceMessageFactory} and {@link - * WebServiceMessageReceiver} to operate. By default, these are obtained by doing a bean lookup on the bean factory - * provided by {@link #getBeanFactory()} the super class. - * - * @author Arjen Poutsma - * @see #createConnectionFactory() - * @see #createMessageFactory() - * @see #createMessageReceiver() - */ -public class WebServiceMessageDrivenBean extends AbstractJmsMessageDrivenBean { - - /** Well-known name for the {@link ConnectionFactory} object in the bean factory for this bean. */ - public static final String CONNECTION_FACTORY_BEAN_NAME = "connectionFactory"; - - /** Well-known name for the {@link WebServiceMessageFactory} bean in the bean factory for this bean. */ - public static final String MESSAGE_FACTORY_BEAN_NAME = "messageFactory"; - - /** Well-known name for the {@link WebServiceMessageReceiver} object in the bean factory for this bean. */ - public static final String MESSAGE_RECEIVER_BEAN_NAME = "messageReceiver"; - - private JmsMessageReceiver delegate; - - private ConnectionFactory connectionFactory; - - /** Delegates to {@link JmsMessageReceiver#handleMessage(Message,Session)}. */ - public void onMessage(Message message) { - Connection connection = null; - Session session = null; - try { - connection = createConnection(connectionFactory); - session = createSession(connection); - delegate.handleMessage(message, session); - } - catch (JmsTransportException ex) { - throw JmsUtils.convertJmsAccessException(ex.getJmsException()); - } - catch (JMSException ex) { - throw JmsUtils.convertJmsAccessException(ex); - } - catch (Exception ex) { - throw new EJBException(ex); - } - finally { - JmsUtils.closeSession(session); - ConnectionFactoryUtils.releaseConnection(connection, connectionFactory, true); - } - } - - /** - * Creates a new {@link Connection}, {@link WebServiceMessageFactory}, and {@link WebServiceMessageReceiver}. - * - * @see #createConnectionFactory() - * @see #createMessageFactory() - * @see #createMessageReceiver() - */ - @Override - protected void onEjbCreate() { - try { - connectionFactory = createConnectionFactory(); - delegate = new JmsMessageReceiver(); - delegate.setMessageFactory(createMessageFactory()); - delegate.setMessageReceiver(createMessageReceiver()); - delegate.setPostProcessor(createPostProcessor()); - } - catch (NamingException ex) { - throw new JndiLookupFailureException("Could not create connection", ex); - } - catch (JMSException ex) { - throw JmsUtils.convertJmsAccessException(ex); - } - catch (Exception ex) { - throw new EJBException(ex); - } - } - - /** Creates a connection factory. Default implementation does a bean lookup for {@link #CONNECTION_FACTORY_BEAN_NAME}. */ - protected ConnectionFactory createConnectionFactory() throws Exception { - return (ConnectionFactory) getBeanFactory().getBean(CONNECTION_FACTORY_BEAN_NAME, ConnectionFactory.class); - } - - /** Creates a message factory. Default implementation does a bean lookup for {@link #MESSAGE_FACTORY_BEAN_NAME}. */ - protected WebServiceMessageFactory createMessageFactory() { - return (WebServiceMessageFactory) getBeanFactory() - .getBean(MESSAGE_FACTORY_BEAN_NAME, WebServiceMessageFactory.class); - } - - /** Creates a connection factory. Default implementation does a bean lookup for {@link #MESSAGE_RECEIVER_BEAN_NAME}. */ - protected WebServiceMessageReceiver createMessageReceiver() { - return (WebServiceMessageReceiver) getBeanFactory() - .getBean(MESSAGE_RECEIVER_BEAN_NAME, WebServiceMessageReceiver.class); - } - - /** - * Create a JMS {@link Connection} using the given {@link ConnectionFactory}. - *

- * This implementation uses JMS 1.1 API. - * - * @param connectionFactory the JMS ConnectionFactory to create a Connection with - * @return the new JMS Connection - * @throws JMSException if thrown by JMS API methods - * @see ConnectionFactory#createConnection() - */ - protected Connection createConnection(ConnectionFactory connectionFactory) throws JMSException { - return connectionFactory.createConnection(); - } - - /** - * Creates a JMS {@link Session}. Default implementation creates a non-transactional, {@link Session#AUTO_ACKNOWLEDGE - * auto acknowledged} session. - *

- * This implementation uses JMS 1.1 API. - * - * @param connection the JMS Connection to create a Session for - * @return the new JMS Session - * @throws JMSException if thrown by JMS API methods - * @see Connection#createSession(boolean,int) - */ - protected Session createSession(Connection connection) throws JMSException { - return connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - } - - /** - * Creates a JMS {@link MessagePostProcessor} to process JMS messages. Default - * implementation returns {@code null}, meaning that no post processor is used. - * - * @return a message post processor - */ - protected MessagePostProcessor createPostProcessor() { - return null; - } - -}