[
https://issues.apache.org/jira/browse/NIFI-6498?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16922473#comment-16922473
]
Nico Verwer edited comment on NIFI-6498 at 9/10/19 6:47 AM:
------------------------------------------------------------
This response has been updated with refactored code fragments on 2019-09-10.
Saxon uses an ErrorListener when compiling a stylesheet. The
net.sf.saxon.lib.StandardErrorListener writes messages to System.err, if
nothing else is specified. Where they end up is unclear to me.
To send the error messages to Nifi's log-file, change the following in
TransformXML.java:
{code:java}
private Templates newTemplates(final ProcessContext context, final String
path) throws TransformerConfigurationException, LookupFailureException {
final ComponentLog logger = getLogger();
final Boolean secureProcessing =
context.getProperty(SECURE_PROCESSING).asBoolean();
TransformerFactory factory = TransformerFactory.newInstance();
final boolean isFilename = context.getProperty(XSLT_FILE_NAME).isSet();
if (secureProcessing) {
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// don't be overly DTD-unfriendly forcing
http://apache.org/xml/features/disallow-doctype-decl
factory.setFeature("http://saxon.sf.net/feature/parserFeature?uri=http://xml.org/sax/features/external-parameter-entities",
false);
factory.setFeature("http://saxon.sf.net/feature/parserFeature?uri=http://xml.org/sax/features/external-general-entities",
false);
} // Set all dynamic properties that are boolean-valued Saxon
features,
// for example "http://saxon.sf.net/feature/allow-external-functions".
for (final Map.Entry<PropertyDescriptor, String> entry :
context.getProperties().entrySet()) {
if (entry.getKey().isDynamic() &&
entry.getKey().getName().startsWith("http://saxon.sf.net/")) {
factory.setFeature(entry.getKey().getName(),
Boolean.parseBoolean(entry.getValue()));
}
} ErrorListener errorListener = new ErrorListener() {
@Override
public void warning(TransformerException exception) throws
TransformerException {
logger.warn(exception.getMessageAndLocation());
}
@Override
public void error(TransformerException exception) throws
TransformerException {
logger.error(exception.getMessageAndLocation());
}
@Override
public void fatalError(TransformerException exception) throws
TransformerException {
logger.log(LogLevel.FATAL, exception.getMessageAndLocation());
}
};
factory.setErrorListener(errorListener); if(isFilename) {
return factory.newTemplates(new StreamSource(path));
} else {
final String coordinateKey =
lookupService.get().getRequiredKeys().iterator().next();
final Optional<String> attributeValue =
lookupService.get().lookup(Collections.singletonMap(coordinateKey, path));
if (attributeValue.isPresent() &&
StringUtils.isNotBlank(attributeValue.get())) {
return factory.newTemplates(new StreamSource(new
ByteArrayInputStream(attributeValue.get().getBytes(StandardCharsets.UTF_8))));
} else {
throw new TransformerConfigurationException("No XSLT definition
is associated to " + path + " in the lookup controller service.");
}
}
}
{code}
The important part is the addition of an ErrorListener. Then, exceptions are
caught and the collected messages are added to the exception.
I have also added the possibility to set binary Saxon properties like
"http://saxon.sf.net/feature/allow-external-functions" from the processor's
properties.
The above will send compile-time messages to the Nifi logging, but we need to
do something similar for run-time messages emitted by <xsl:message>. This
requires the following change inside the onTrigger() method:
{code:java}
public void process(final InputStream rawIn, final OutputStream
out) throws IOException {
try (final InputStream in = new BufferedInputStream(rawIn))
{
final Templates templates;
if (cache != null) {
templates = cache.get(path);
} else {
templates = newTemplates(context, path);
} final Transformer transformer
= templates.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT,
(indentOutput ? "yes" : "no"));
setMessageLogger(transformer); // Pass all dynamic
properties to the transformer
for (final Map.Entry<PropertyDescriptor, String> entry
: context.getProperties().entrySet()) {
if (entry.getKey().isDynamic() &&
!entry.getKey().getName().startsWith("http://saxon.sf.net/")) {
String value =
context.newPropertyValue(entry.getValue()).evaluateAttributeExpressions(original).getValue();
transformer.setParameter(entry.getKey().getName(), value);
}
} // use a StreamSource with
Saxon
StreamSource source = new StreamSource(in);
StreamResult result = new StreamResult(out);
transformer.transform(source, result);
} catch (final Exception e) {
throw new IOException(e);
}
}
{code}
The setMessageLogger method is:
{code:java}
/**
* The Saxon transformer sends messages from <xsl:message> to a
messageEmitter, making them disappear.
* Use Nifi logging for these messages instead.
* @param logger
* @param transformer
*/
private void setMessageLogger(final Transformer transformer) {
final ComponentLog logger = getLogger();
if (transformer instanceof net.sf.saxon.jaxp.TransformerImpl) {
net.sf.saxon.jaxp.TransformerImpl saxonTransformer =
(net.sf.saxon.jaxp.TransformerImpl)transformer;
Receiver messageReceiver = new net.sf.saxon.serialize.XMLEmitter() {
boolean terminate = false;
@Override
public void startDocument(int properties) throws XPathException {
setWriter(new StringWriter());
terminate = (properties & ReceiverOptions.TERMINATE) != 0;
super.startDocument(properties);
}
@Override
public void endDocument() throws XPathException {
String message = getWriter().toString();
if (terminate) {
XPathException xpe = new XPathException(message);
xpe.setErrorCode("XTMM9000");
throw xpe;
} else {
logger.warn(message);
}
}
@Override
public void close() {}
};
saxonTransformer.getUnderlyingController().setMessageEmitter(messageReceiver);
}
}
{code}
I would make a pull request if I could, but I have not been able to get Nifi
working in Eclipse on my PC (XPS15, i7, 16GB RAM). Eclipse just keeps mavening
things forever. :(
Maybe someone can test this code fragment and add it to Nifi?
was (Author: nverwer):
Saxon uses an ErrorListener when compiling a stylesheet. The
net.sf.saxon.lib.StandardErrorListener writes messages to System.err, if
nothing else is specified. Where they end up is unclear to me.
To send the error messages to Nifi's log-file, change the following in
TransformXML.java:
{code:java}
private Templates newTemplates(final ProcessContext context, final String
path) throws TransformerConfigurationException, LookupFailureException {
final Boolean secureProcessing =
context.getProperty(SECURE_PROCESSING).asBoolean();
TransformerFactory factory = TransformerFactory.newInstance();
final boolean isFilename = context.getProperty(XSLT_FILE_NAME).isSet();
StringBuffer messages = new StringBuffer(); if
(secureProcessing) {
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// don't be overly DTD-unfriendly forcing
http://apache.org/xml/features/disallow-doctype-decl
factory.setFeature("http://saxon.sf.net/feature/parserFeature?uri=http://xml.org/sax/features/external-parameter-entities",
false);
factory.setFeature("http://saxon.sf.net/feature/parserFeature?uri=http://xml.org/sax/features/external-general-entities",
false);
}
ErrorListener errorListener = new ErrorListener() {
@Override
public void warning(TransformerException exception) throws
TransformerException {
messages.append("Warning:
"+exception.getMessageAndLocation()+"\n");
}
@Override
public void error(TransformerException exception) throws
TransformerException {
messages.append("Error:
"+exception.getMessageAndLocation()+"\n");
}
@Override
public void fatalError(TransformerException exception) throws
TransformerException {
messages.append("Fatal Error:
"+exception.getMessageAndLocation()+"\n");
}
};
factory.setErrorListener(errorListener);
try {
if(isFilename) {
return factory.newTemplates(new StreamSource(path));
} else {
final String coordinateKey =
lookupService.get().getRequiredKeys().iterator().next();
final Optional<String> attributeValue =
lookupService.get().lookup(Collections.singletonMap(coordinateKey, path));
if (attributeValue.isPresent() &&
StringUtils.isNotBlank(attributeValue.get())) {
return factory.newTemplates(new StreamSource(new
ByteArrayInputStream(attributeValue.get().getBytes(StandardCharsets.UTF_8))));
} else {
throw new TransformerConfigurationException("No XSLT
definition is associated to " + path + " in the lookup controller service.");
}
}
} catch (TransformerConfigurationException tce) {
throw new TransformerConfigurationException("Errors compiling XSLT:
"+messages.toString(), tce);
}
}
{code}
The important part is the addition of an ErrorListener. Then, exceptions are
caught and the collected messages are added to the exception.
I would make a pull request if I could, but I have not been able to get Nifi
working in Eclipse on my PC (XPS15, i7, 16GB RAM). Eclipse just keeps mavening
things forever. :(
Maybe someone can test this code fragment and add it to Nifi?
> XMLTransform processor: XSLT doesn't give detail error message with line
> number
> -------------------------------------------------------------------------------
>
> Key: NIFI-6498
> URL: https://issues.apache.org/jira/browse/NIFI-6498
> Project: Apache NiFi
> Issue Type: Improvement
> Components: Core Framework
> Affects Versions: 1.9.2
> Reporter: Raymond
> Priority: Major
>
> When something is wrong with the XSLT in the XMLTransform processor you get
> an error, but this is only a general error without any details or the line
> number where the parsing error occurs. For example when using Saxon from
> example I get the following error:
> Error on line 17 column 5 SXXP0003: Error reported by XML parser: The element
> type "xsl:tomplate" must be terminated by the matching end-tag
> "</xsl:tomplate>".
> When using NiFi the following error is printed:
> 11:07:43 CEST
> ERROR
> 2b4c1dab-3a87-175f-f817-3f3c72af4102
> TransformXml[id=2b4c1dab-3a87-175f-f817-3f3c72af4102] Unable to transform
> StandardFlowFileRecord[uuid=c4dde8e5-89b0-4f29-8f2d-a97a009cf843,claim=StandardContentClaim
> [resourceClaim=StandardResourceClaim[id=1564391260760-1, container=default,
> section=1], offset=0,
> length=6380],offset=0,name=c4dde8e5-89b0-4f29-8f2d-a97a009cf843,size=6380]
> due to IOException thrown from
> TransformXml[id=2b4c1dab-3a87-175f-f817-3f3c72af4102]: java.io.IOException:
> java.util.concurrent.ExecutionException:
> javax.xml.transform.TransformerConfigurationException:
> net.sf.saxon.s9api.SaxonApiException: Stylesheet compilation failed: 1 error
> reported: org.apache.nifi.processor.exception.ProcessException: IOException
> thrown from TransformXml[id=2b4c1dab-3a87-175f-f817-3f3c72af4102]:
> java.io.IOException: java.util.concurrent.ExecutionException:
> javax.xml.transform.TransformerConfigurationException:
> net.sf.saxon.s9api.SaxonApiException: Stylesheet compilation failed: 1 error
> reported
> The error in NiFi gives almost no information for debugging. Would be a big
> improvement if the Saxon error message is catched and add to the NiFi error
> message (bulletin board).
>
>
--
This message was sent by Atlassian Jira
(v8.3.2#803003)