SWS-706 - Jaxp13XPathTemplate should support StreamSources with only a systemId if it's a valid URL

This commit is contained in:
Arjen Poutsma
2011-05-19 10:27:39 +00:00
parent 95ac6687b6
commit 6cdff3ec1e
7 changed files with 214 additions and 53 deletions

View File

@@ -187,6 +187,17 @@ public abstract class AbstractXomPayloadEndpoint extends TransformerObjectSuppor
throw new XomParsingException(ex);
}
}
public void source(String systemId) throws Exception {
try {
Builder builder = new Builder();
Document document = builder.build(systemId);
element = document.getRootElement();
}
catch (ParsingException ex) {
throw new XomParsingException(ex);
}
}
}
private static class XomParsingException extends NestedRuntimeException {

View File

@@ -21,6 +21,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.xml.bind.JAXBContext;
@@ -38,6 +39,7 @@ import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.util.Assert;
@@ -227,6 +229,10 @@ public abstract class AbstractJaxb2PayloadMethodProcessor extends AbstractPayloa
public void streamSource(Reader reader) throws IOException, JAXBException {
result = unmarshaller.unmarshal(reader);
}
public void source(String systemId) throws Exception {
result = unmarshaller.unmarshal(new URL(systemId));
}
}
private class JaxbElementSourceCallback<T> implements TraxUtils.SourceCallback {
@@ -265,6 +271,10 @@ public abstract class AbstractJaxb2PayloadMethodProcessor extends AbstractPayloa
public void streamSource(Reader reader) throws IOException, JAXBException {
result = unmarshaller.unmarshal(new StreamSource(reader), declaredType);
}
public void source(String systemId) throws Exception {
result = unmarshaller.unmarshal(new StreamSource(systemId), declaredType);
}
}
private class Jaxb2ResultCallback implements TraxUtils.ResultCallback {
@@ -301,6 +311,10 @@ public abstract class AbstractJaxb2PayloadMethodProcessor extends AbstractPayloa
public void streamResult(Writer writer) throws JAXBException {
marshaller.marshal(jaxbElement, writer);
}
public void result(String systemId) throws Exception {
marshaller.marshal(jaxbElement, new StreamResult(systemId));
}
}
private class JaxbStreamingPayload implements StreamingPayload {

View File

@@ -143,6 +143,10 @@ public abstract class PayloadRootUtils {
public void streamSource(Reader reader) throws Exception {
// Do nothing
}
public void source(String systemId) throws Exception {
// Do nothing
}
}

View File

@@ -36,6 +36,7 @@ import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.StaxUtils;
import org.w3c.dom.Document;
@@ -207,38 +208,41 @@ public abstract class TraxUtils {
public static void doWithSource(Source source, SourceCallback callback) throws Exception {
if (source instanceof DOMSource) {
callback.domSource(((DOMSource) source).getNode());
return;
}
else if (isStaxSource(source)) {
XMLStreamReader streamReader = getXMLStreamReader(source);
if (streamReader != null) {
callback.staxSource(streamReader);
return;
}
else {
XMLEventReader eventReader = getXMLEventReader(source);
if (eventReader != null) {
callback.staxSource(eventReader);
}
else {
throw new IllegalArgumentException(
"StAX source contains neither XMLStreamReader nor XMLEventReader");
return;
}
}
}
else if (source instanceof SAXSource) {
SAXSource saxSource = (SAXSource) source;
callback.saxSource(saxSource.getXMLReader(), saxSource.getInputSource());
return;
}
else if (source instanceof StreamSource) {
StreamSource streamSource = (StreamSource) source;
if (streamSource.getInputStream() != null) {
callback.streamSource(streamSource.getInputStream());
return;
}
else if (streamSource.getReader() != null) {
callback.streamSource(streamSource.getReader());
return;
}
else {
throw new IllegalArgumentException("StreamSource contains neither InputStream nor Reader");
}
}
if (StringUtils.hasLength(source.getSystemId())) {
String systemId = source.getSystemId();
callback.source(systemId);
}
else {
throw new IllegalArgumentException("Unknown Source type: " + source.getClass());
@@ -246,8 +250,8 @@ public abstract class TraxUtils {
}
/**
* Performs the given {@linkplain ResultCallback callback} operation on a {@link Result}. Supports both the JAXP 1.4
* {@link StAXResult} and the Spring 3.0 {@link StaxUtils#createStaxResult StaxSource}.
* Performs the given {@linkplain org.springframework.xml.transform.TraxUtils.ResultCallback callback} operation on a {@link javax.xml.transform.Result}. Supports both the JAXP 1.4
* {@link javax.xml.transform.stax.StAXResult} and the Spring 3.0 {@link org.springframework.util.xml.StaxUtils#createStaxResult StaxSource}.
*
* @param result result to look at
* @param callback the callback to invoke for each kind of result
@@ -255,38 +259,41 @@ public abstract class TraxUtils {
public static void doWithResult(Result result, ResultCallback callback) throws Exception{
if (result instanceof DOMResult) {
callback.domResult(((DOMResult) result).getNode());
return;
}
else if (isStaxResult(result)) {
XMLStreamWriter streamWriter = getXMLStreamWriter(result);
if (streamWriter != null) {
callback.staxResult(streamWriter);
return;
}
else {
XMLEventWriter eventWriter = getXMLEventWriter(result);
if (eventWriter != null) {
callback.staxResult(eventWriter);
}
else {
throw new IllegalArgumentException(
"StAX result contains neither XMLStreamWriter nor XMLEventWriter");
return;
}
}
}
else if (result instanceof SAXResult) {
SAXResult saxSource = (SAXResult) result;
callback.saxResult(saxSource.getHandler(), saxSource.getLexicalHandler());
return;
}
else if (result instanceof StreamResult) {
StreamResult streamSource = (StreamResult) result;
if (streamSource.getOutputStream() != null) {
callback.streamResult(streamSource.getOutputStream());
return;
}
else if (streamSource.getWriter() != null) {
callback.streamResult(streamSource.getWriter());
return;
}
else {
throw new IllegalArgumentException("StreamResult contains neither OutputStream nor Writer");
}
}
if (StringUtils.hasLength(result.getSystemId())) {
String systemId = result.getSystemId();
callback.result(systemId);
}
else {
throw new IllegalArgumentException("Unknown Result type: " + result.getClass());
@@ -344,6 +351,15 @@ public abstract class TraxUtils {
* @param reader the reader
*/
void streamSource(Reader reader) throws Exception;
/**
* Perform an operation on the system identifier contained in any {@link Source}.
*
* @param systemId the system identifier
*/
void source(String systemId) throws Exception;
}
/**
@@ -398,6 +414,14 @@ public abstract class TraxUtils {
* @param writer the writer
*/
void streamResult(Writer writer) throws Exception;
/**
* Perform an operation on the system identifier contained in any {@link Result}.
*
* @param systemId the system identifier
*/
void result(String systemId) throws Exception;
}

View File

@@ -16,32 +16,40 @@
package org.springframework.xml.xpath;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.dom.DOMResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathFactoryConfigurationException;
import org.springframework.util.xml.StaxUtils;
import org.springframework.xml.namespace.SimpleNamespaceContext;
import org.springframework.xml.transform.StaxSource;
import org.springframework.xml.transform.TransformerHelper;
import org.springframework.xml.transform.TraxUtils;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
/**
* Implementation of {@link XPathOperations} that uses JAXP 1.3. JAXP 1.3 is part of Java SE since 1.5.
* <p/>
* Namespaces can be set using the <code>namespaces</code> property.
* Namespaces can be set using the {@code namespaces} property.
*
* @author Arjen Poutsma
* @see #setNamespaces(java.util.Map)
@@ -128,35 +136,9 @@ public class Jaxp13XPathTemplate extends AbstractXPathTemplate {
xpath.setNamespaceContext(namespaceContext);
}
try {
if (StaxUtils.isStaxSource(context)) {
Element element = getRootElement(context);
return xpath.evaluate(expression, element, returnType);
}
else if (context instanceof SAXSource) {
SAXSource saxSource = (SAXSource) context;
return xpath.evaluate(expression, saxSource.getInputSource(), returnType);
}
else if (context instanceof DOMSource) {
DOMSource domSource = (DOMSource) context;
return xpath.evaluate(expression, domSource.getNode(), returnType);
}
else if (context instanceof StreamSource) {
StreamSource streamSource = (StreamSource) context;
InputSource inputSource;
if (streamSource.getInputStream() != null) {
inputSource = new InputSource(streamSource.getInputStream());
}
else if (streamSource.getReader() != null) {
inputSource = new InputSource(streamSource.getReader());
}
else {
throw new IllegalArgumentException("StreamSource contains neither InputStream nor Reader");
}
return xpath.evaluate(expression, inputSource, returnType);
}
else {
throw new IllegalArgumentException("context type unknown");
}
EvaluationCallback callback = new EvaluationCallback(xpath, expression, returnType);
TraxUtils.doWithSource(context, callback);
return callback.result;
}
catch (javax.xml.xpath.XPathException ex) {
throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
@@ -164,11 +146,77 @@ public class Jaxp13XPathTemplate extends AbstractXPathTemplate {
catch (TransformerException ex) {
throw new XPathException("Could not transform context to DOM Node", ex);
}
catch (Exception ex) {
throw new XPathException(ex.getMessage(), ex);
}
}
private synchronized XPath createXPath() {
return xpathFactory.newXPath();
}
private static class EvaluationCallback implements TraxUtils.SourceCallback {
private final XPath xpath;
private final String expression;
private final QName returnType;
private final TransformerHelper transformerHelper = new TransformerHelper();
private Object result;
private EvaluationCallback(XPath xpath, String expression, QName returnType) {
this.xpath = xpath;
this.expression = expression;
this.returnType = returnType;
}
public void domSource(Node node) throws XPathExpressionException {
result = xpath.evaluate(expression, node, returnType);
}
public void saxSource(XMLReader reader, InputSource inputSource) throws XPathExpressionException {
inputSource(inputSource);
}
public void staxSource(XMLEventReader eventReader)
throws XPathExpressionException, XMLStreamException, TransformerException {
Element element = getRootElement(new StaxSource(eventReader));
domSource(element);
}
public void staxSource(XMLStreamReader streamReader) throws TransformerException, XPathExpressionException {
Element element = getRootElement(new StaxSource(streamReader));
domSource(element);
}
public void streamSource(InputStream inputStream) throws XPathExpressionException {
inputSource(new InputSource(inputStream));
}
public void streamSource(Reader reader) throws XPathExpressionException {
inputSource(new InputSource(reader));
}
public void source(String systemId) throws XPathExpressionException {
inputSource(new InputSource(systemId));
}
private void inputSource(InputSource inputSource) throws XPathExpressionException {
result = xpath.evaluate(expression, inputSource, returnType);
}
private Element getRootElement(Source source) throws TransformerException {
DOMResult domResult = new DOMResult();
transformerHelper.transform(source, domResult);
Document document = (Document) domResult.getNode();
return document.getDocumentElement();
}
}
}

View File

@@ -253,6 +253,35 @@ public class TraxUtilsTest {
verify(mock);
}
@Test
public void testDoWithSystemIdSource() throws Exception {
String systemId = "http://www.springframework.org/dtd/spring-beans.dtd";
TraxUtils.SourceCallback mock = createMock(TraxUtils.SourceCallback.class);
mock.source(systemId);
replay(mock);
TraxUtils.doWithSource(new StreamSource(systemId), mock);
verify(mock);
}
@Test
public void testDoWithSystemIdResult() throws Exception {
String systemId = "http://www.springframework.org/dtd/spring-beans.dtd";
TraxUtils.ResultCallback mock = createMock(TraxUtils.ResultCallback.class);
mock.result(systemId);
replay(mock);
TraxUtils.doWithResult(new StreamResult(systemId), mock);
verify(mock);
}
@Test
public void testDoWithInvalidSource() throws Exception {
Source source = new Source() {

View File

@@ -18,12 +18,17 @@ package org.springframework.xml.xpath;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamSource;
import org.springframework.core.io.ClassPathResource;
@@ -36,6 +41,7 @@ import org.junit.Test;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public abstract class AbstractXPathTemplateTestCase {
@@ -133,9 +139,34 @@ public abstract class AbstractXPathTemplateTestCase {
}
@Test
public void testEvaluateStreamSource() throws IOException, SAXException, ParserConfigurationException {
public void testEvaluateSAXSource() throws Exception {
InputStream in = AbstractXPathTemplateTestCase.class.getResourceAsStream("nonamespaces.xml");
String result = template.evaluateAsString("/root/child/text", new StreamSource(in));
SAXSource source = new SAXSource(new InputSource(in));
String result = template.evaluateAsString("/root/child/text", source);
Assert.assertEquals("Invalid result", "text", result);
}
@Test
public void testEvaluateStaxSource() throws Exception {
InputStream in = AbstractXPathTemplateTestCase.class.getResourceAsStream("nonamespaces.xml");
XMLStreamReader streamReader = XMLInputFactory.newFactory().createXMLStreamReader(in);
StAXSource source = new StAXSource(streamReader);
String result = template.evaluateAsString("/root/child/text", source);
Assert.assertEquals("Invalid result", "text", result);
}
@Test
public void testEvaluateStreamSourceInputStream() throws IOException, SAXException, ParserConfigurationException {
InputStream in = AbstractXPathTemplateTestCase.class.getResourceAsStream("nonamespaces.xml");
StreamSource source = new StreamSource(in);
String result = template.evaluateAsString("/root/child/text", source);
Assert.assertEquals("Invalid result", "text", result);
}
@Test
public void testEvaluateStreamSourceSystemId() throws IOException, SAXException, ParserConfigurationException {
URL url = AbstractXPathTemplateTestCase.class.getResource("nonamespaces.xml");
String result = template.evaluateAsString("/root/child/text", new StreamSource(url.toString()));
Assert.assertEquals("Invalid result", "text", result);
}
@@ -152,7 +183,7 @@ public abstract class AbstractXPathTemplateTestCase {
@Test
public void testEvaluateAsObject() throws Exception {
String result = (String) template.evaluateAsObject("/root/child", nonamespaces, new NodeMapper<String>() {
String result = template.evaluateAsObject("/root/child", nonamespaces, new NodeMapper<String>() {
public String mapNode(Node node, int nodeNum) throws DOMException {
return node.getLocalName();
}