SWS-1004 - Use Axiom's getSAXResult method instead of the custom AxiomResult class

This commit is contained in:
Andreas Veithen
2017-05-20 18:27:25 +01:00
committed by Greg Turnquist
parent dcc8b8deb0
commit fbec6122ef
8 changed files with 5 additions and 554 deletions

View File

@@ -1,244 +0,0 @@
/*
* Copyright 2005-2014 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.soap.axiom;
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.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMContainer;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
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 {@code endDocument} is called.
* Used for returing {@code SAXResult}s from Axiom elements.
* @author Arjen Poutsma
* @author Tommy Winther
* @since 1.0.0
*/
class AxiomHandler implements ContentHandler, LexicalHandler {
private final OMFactory factory;
private final List<OMContainer> elements = new ArrayList<OMContainer>();
private final OMContainer container;
private int charactersType = XMLStreamConstants.CHARACTERS;
private List<Map<String, String>> namespaceMappings =
new ArrayList<Map<String, String>>();
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;
}
}
@Override
public void startDocument() throws SAXException {
removeAllNamespaceMappings();
newNamespaceMapping();
}
@Override
public void endDocument() throws SAXException {
removeAllNamespaceMappings();
}
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException {
currentNamespaceMapping().put(prefix, uri);
}
@Override
public void endPrefixMapping(String prefix) throws SAXException {
currentNamespaceMapping().remove(prefix);
}
@Override
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);
// declare namespaces
Map<String, String> namespaceMappings = currentNamespaceMapping();
for (Map.Entry<String, String> entry : namespaceMappings.entrySet()) {
String prefix = entry.getKey();
String namespaceUri = entry.getValue();
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();
}
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);
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
elements.remove(elements.size() - 1);
removeNamespaceMapping();
}
@Override
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);
}
@Override
public void ignorableWhitespace(char ch[], int start, int length)
throws SAXException {
charactersType = XMLStreamConstants.SPACE;
characters(ch, start, length);
charactersType = XMLStreamConstants.CHARACTERS;
}
@Override
public void processingInstruction(String target, String data) throws SAXException {
OMContainer parent = getParent();
factory.createOMProcessingInstruction(parent, target, data);
}
@Override
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);
}
@Override
public void startCDATA() throws SAXException {
charactersType = XMLStreamConstants.CDATA;
}
@Override
public void endCDATA() throws SAXException {
charactersType = XMLStreamConstants.CHARACTERS;
}
@Override
public void startEntity(String name) throws SAXException {
if (!isPredefinedEntityReference(name)) {
charactersType = XMLStreamConstants.ENTITY_REFERENCE;
}
}
@Override
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
*/
@Override
public void setDocumentLocator(Locator locator) {
}
@Override
public void skippedEntity(String name) throws SAXException {
}
@Override
public void startDTD(String name, String publicId, String systemId)
throws SAXException {
}
@Override
public void endDTD() throws SAXException {
}
private Map<String, String> currentNamespaceMapping() {
return namespaceMappings.get(namespaceMappings.size() - 1);
}
private void newNamespaceMapping() {
namespaceMappings.add(new HashMap<String, String>());
}
private void removeNamespaceMapping() {
namespaceMappings.remove(namespaceMappings.size() - 1);
}
private void removeAllNamespaceMappings() {
namespaceMappings.clear();
}
}

View File

@@ -1,61 +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.soap.axiom;
import javax.xml.transform.sax.SAXResult;
import org.apache.axiom.om.OMContainer;
import org.apache.axiom.om.OMFactory;
import org.xml.sax.ContentHandler;
import org.xml.sax.ext.LexicalHandler;
/**
* Specific TrAX {@link javax.xml.transform.Result} that adds the resulting AXIOM OMElement to a specified parent
* element when {@code endDocument} is called.
*
* @author Arjen Poutsma
* @see AxiomHandler
* @since 1.5.0
*/
class AxiomResult extends SAXResult {
AxiomResult(OMContainer container, OMFactory factory) {
AxiomHandler handler = new AxiomHandler(container, factory);
super.setHandler(handler);
super.setLexicalHandler(handler);
}
/**
* Throws a {@code UnsupportedOperationException}.
*
* @throws UnsupportedOperationException always
*/
@Override
public void setHandler(ContentHandler handler) {
throw new UnsupportedOperationException("setHandler is not supported");
}
/**
* Throws a {@code UnsupportedOperationException}.
*
* @throws UnsupportedOperationException always
*/
@Override
public void setLexicalHandler(LexicalHandler handler) {
throw new UnsupportedOperationException("setLexicalHandler is not supported");
}
}

View File

@@ -60,7 +60,7 @@ class AxiomSoapFaultDetail extends AxiomSoapElement implements SoapFaultDetail {
@Override
public Result getResult() {
return new AxiomResult(getAxiomFaultDetail(), getAxiomFactory());
return getAxiomFaultDetail().getSAXResult();
}
protected SOAPFaultDetail getAxiomFaultDetail() {

View File

@@ -39,7 +39,7 @@ class AxiomSoapFaultDetailElement extends AxiomSoapElement implements SoapFaultD
@Override
public Result getResult() {
try {
return new AxiomResult(getAxiomElement(), getAxiomFactory());
return getAxiomElement().getSAXResult();
}
catch (OMException ex) {
throw new AxiomSoapFaultException(ex);

View File

@@ -45,7 +45,7 @@ abstract class AxiomSoapHeader extends AxiomSoapElement implements SoapHeader {
@Override
public Result getResult() {
return new AxiomResult(getAxiomHeader(), getAxiomFactory());
return getAxiomHeader().getSAXResult();
}
@Override

View File

@@ -54,7 +54,7 @@ class AxiomSoapHeaderElement extends AxiomSoapElement implements SoapHeaderEleme
@Override
public Result getResult() {
try {
return new AxiomResult(getAxiomHeaderBlock(), getAxiomFactory());
return getAxiomHeaderBlock().getSAXResult();
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);

View File

@@ -46,7 +46,7 @@ class CachingPayload extends AbstractPayload {
@Override
public Result getResultInternal() {
return new AxiomResult(getAxiomBody(), getAxiomFactory());
return getAxiomBody().getSAXResult();
}
}

View File

@@ -1,244 +0,0 @@
/*
* Copyright 2005-2012 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.soap.axiom;
import static org.custommonkey.xmlunit.XMLAssert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
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.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
public class AxiomHandlerTest {
private static final String XML_1 =
"<?xml version='1.0' encoding='UTF-8'?>" + "<?pi content?>" + "<root xmlns='namespace'>" +
"<prefix:child xmlns:prefix='namespace2' xmlns:prefix2='namespace3' prefix2:attr='value'>content</prefix:child>" +
"</root>";
private static final String XML_2_EXPECTED =
"<?xml version='1.0' encoding='UTF-8'?>" + "<root xmlns='namespace'>" + "<child xmlns='namespace2' />" +
"</root>";
private static final String XML_2_SNIPPET =
"<?xml version='1.0' encoding='UTF-8'?>" + "<child xmlns='namespace2' />";
private static final String XML_3_ENTITY =
"<predefined-entity-reference>&lt;&gt;&amp;&quot;&apos;</predefined-entity-reference>";
private static final String XML_4_SNIPPET = "<?xml version='1.0' encoding='UTF-8'?>" + "<child xmlns='namespace1' />";
private static final String XML_5_SNIPPET = "<?xml version='1.0' encoding='UTF-8'?>" + "<x:child xmlns:x='namespace1' />";
private static final String XML_6_SNIPPET = "<?xml version='1.0' encoding='UTF-8'?>" + "<parent xmlns='parent-namespace'><c:first-sibling xmlns:c='child-namespace' /><second-sibling /></parent>";
private AxiomHandler handler;
private OMDocument result;
private XMLReader xmlReader;
private OMFactory factory;
@Before
public void setUp() throws Exception {
factory = OMAbstractFactory.getOMFactory();
result = factory.createOMDocument();
xmlReader = XMLReaderFactory.createXMLReader();
}
@Test
public void testContentHandlerDocumentNamespacePrefixes() throws Exception {
xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
handler = new AxiomHandler(result, factory);
xmlReader.setContentHandler(handler);
xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
xmlReader.parse(new InputSource(new StringReader(XML_1)));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
result.serialize(bos);
assertXMLEqual("Invalid result", XML_1, bos.toString("UTF-8"));
}
@Test
public void testContentHandlerDocumentNoNamespacePrefixes() throws Exception {
xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
handler = new AxiomHandler(result, factory);
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(XML_1)));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
result.serialize(bos);
assertXMLEqual("Invalid result", XML_1, bos.toString("UTF-8"));
}
@Test
public void testContentHandlerElement() throws Exception {
OMNamespace namespace = factory.createOMNamespace("namespace", "");
OMElement rootElement = factory.createOMElement("root", namespace, result);
handler = new AxiomHandler(rootElement, factory);
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(XML_2_SNIPPET)));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
result.serialize(bos);
assertXMLEqual("Invalid result", XML_2_EXPECTED, bos.toString("UTF-8"));
}
@Test
public void testContentHandlerElementWithSamePrefixAndDifferentNamespace() throws Exception {
OMNamespace namespace = factory.createOMNamespace("namespace1", "");
OMElement rootElement = factory.createOMElement("root", namespace, result);
handler = new AxiomHandler(rootElement, factory);
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(XML_2_SNIPPET)));
Iterator<?> it = result.getOMDocumentElement().getChildrenWithLocalName("child");
assertTrue(it.hasNext());
OMElement child = (OMElement) it.next();
assertEquals("", child.getQName().getPrefix());
assertEquals("namespace2", child.getQName().getNamespaceURI());
}
@Test
public void testContentHandlerElementWithSameNamespacesAndPrefix() throws Exception {
OMNamespace namespace = factory.createOMNamespace("namespace1", "");
OMElement rootElement = factory.createOMElement("root", namespace, result);
handler = new AxiomHandler(rootElement, factory);
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(XML_4_SNIPPET)));
Iterator<?> it = result.getOMDocumentElement().getChildrenWithLocalName("child");
assertTrue(it.hasNext());
OMElement child = (OMElement) it.next();
assertEquals("", child.getQName().getPrefix());
assertEquals("namespace1", child.getQName().getNamespaceURI());
}
@Test
public void testContentHandlerElementWithSameNamespacesAndDifferentPrefix() throws Exception {
OMNamespace namespace = factory.createOMNamespace("namespace1", "");
OMElement rootElement = factory.createOMElement("root", namespace, result);
handler = new AxiomHandler(rootElement, factory);
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(XML_5_SNIPPET)));
Iterator<?> it = result.getOMDocumentElement().getChildrenWithLocalName("child");
assertTrue(it.hasNext());
OMElement child = (OMElement) it.next();
assertEquals("x", child.getQName().getPrefix());
assertEquals("namespace1", child.getQName().getNamespaceURI());
}
@Test
public void testContentHandlerSiblingPrefixMapping() throws Exception {
handler = new AxiomHandler(result, factory);
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(XML_6_SNIPPET)));
Iterator<?> it = result.getOMDocumentElement().getChildren();
assertTrue(it.hasNext());
OMElement firstSibling = (OMElement) it.next();
assertEquals("first-sibling", firstSibling.getLocalName());
Iterator<?> firstSiblingNsIt = firstSibling.getAllDeclaredNamespaces();
// Verify first sibling has a single namespace declaration (with child-namespace URI)
assertTrue(firstSiblingNsIt.hasNext());
assertEquals("child-namespace", ((OMNamespace) firstSiblingNsIt.next()).getNamespaceURI());
assertFalse(firstSiblingNsIt.hasNext());
assertTrue(it.hasNext());
OMElement secondSibling = (OMElement) it.next();
// Verify second sibling has no namespace declarations as it's covered by <parent />
// This also verifies the child-namespace from the <first-sibling /> element isn't copied to second-sibling
assertEquals("second-sibling", secondSibling.getLocalName());
Iterator<?> secondSiblingNsIt = secondSibling.getAllDeclaredNamespaces();
assertFalse(secondSiblingNsIt.hasNext());
}
@Test
public void testContentHandlerPredefinedEntityReference() throws Exception {
handler = new AxiomHandler(result, factory);
xmlReader.setContentHandler(handler);
xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
xmlReader.parse(new InputSource(new StringReader(XML_3_ENTITY)));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
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 = "<root xmlns=\"http://www.springframework.org/spring-ws\" xmlns:attr=\"http://www.springframework.org/spring-ws/attr\" attr:attribute=\"value\">" +
"<prefix:child xmlns:prefix=\"http://www.springframework.org/spring-ws/child\"/>" +
"</root>";
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;
}
}