NIFI-9943 Added Transform Provider to nifi-xml-processing

- Refactored TransformerFactory references using StandardTransformerProvider

This closes #5986
Signed-off-by: Paul Grey <greyp@apache.org>
This commit is contained in:
exceptionfactory 2022-04-20 19:13:23 -05:00 committed by Paul Grey
parent 4450f4ce50
commit b288810316
No known key found for this signature in database
GPG Key ID: 8DDF32B9C7EE39D0
15 changed files with 364 additions and 165 deletions

View File

@ -48,17 +48,13 @@ import org.apache.nifi.minifi.commons.schema.serialization.SchemaLoader;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.FileOutputStream;
@ -147,20 +143,18 @@ public final class ConfigTransformer {
}
}
protected static void writeFlowXmlFile(ConfigSchema configSchema, OutputStream outputStream) throws TransformerException, ConfigTransformerException {
protected static void writeFlowXmlFile(ConfigSchema configSchema, OutputStream outputStream) throws ConfigTransformerException {
final StreamResult streamResult = new StreamResult(outputStream);
// configure the transformer and convert the DOM
final TransformerFactory transformFactory = TransformerFactory.newInstance();
final Transformer transformer = transformFactory.newTransformer();
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
final StandardTransformProvider transformProvider = new StandardTransformProvider();
transformProvider.setIndent(true);
// transform the document to byte stream
transformer.transform(createFlowXml(configSchema), streamResult);
transformProvider.transform(createFlowXml(configSchema), streamResult);
}
protected static void writeFlowXmlFile(ConfigSchema configSchema, String path) throws IOException, TransformerException, ConfigurationChangeException, ConfigTransformerException {
protected static void writeFlowXmlFile(ConfigSchema configSchema, String path) throws IOException, ConfigTransformerException {
try (OutputStream fileOut = Files.newOutputStream(Paths.get(path, "flow.xml.gz"))) {
try (OutputStream outStream = new GZIPOutputStream(fileOut)) {
writeFlowXmlFile(configSchema, outStream);
@ -362,7 +356,7 @@ public final class ConfigTransformer {
}
return new DOMSource(doc);
} catch (final ProcessingException | DOMException | TransformerFactoryConfigurationError | IllegalArgumentException e) {
} catch (final ProcessingException | DOMException | IllegalArgumentException e) {
throw new ConfigTransformerException(e);
} catch (Exception e) {
throw new ConfigTransformerException("Failed to parse the config YAML while writing the top level of the flow xml", e);

View File

@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.xml.processing;
import javax.xml.XMLConstants;
/**
* XML Processing Attributes
*/
public enum ProcessingAttribute {
/** Access External Document Type Declaration with an empty string to deny all access to external references */
ACCESS_EXTERNAL_DTD(XMLConstants.ACCESS_EXTERNAL_DTD, ""),
/** Access External Stylesheet with an empty string to deny all access to external references */
ACCESS_EXTERNAL_STYLESHEET(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
private final String attribute;
private final Object value;
ProcessingAttribute(final String attribute, final Object value) {
this.attribute = attribute;
this.value = value;
}
public String getAttribute() {
return attribute;
}
public Object getValue() {
return value;
}
}

View File

@ -0,0 +1,118 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.xml.processing.transform;
import org.apache.nifi.xml.processing.ProcessingAttribute;
import org.apache.nifi.xml.processing.ProcessingException;
import javax.xml.XMLConstants;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import java.util.Objects;
/**
* Standard implementation of Transform Provider with secure processing enabled
*/
public class StandardTransformProvider implements TransformProvider {
private static final boolean SECURE_PROCESSING_ENABLED = true;
private static final String ENABLED_PROPERTY = "yes";
private static final String INDENT_AMOUNT_OUTPUT_KEY = "{http://xml.apache.org/xslt}indent-amount";
private static final String INDENT_AMOUNT = "2";
private boolean indent;
private boolean omitXmlDeclaration;
private String method;
/**
* Set Indent Status
*
* @param indent Indent Status
*/
public void setIndent(final boolean indent) {
this.indent = indent;
}
/**
* Set Output Method
*
* @param method Method or null when default configuration should be used
*/
public void setMethod(final String method) {
this.method = method;
}
/**
* Set Omit XML Declaration
*
* @param omitXmlDeclaration Omit XML Declaration
*/
public void setOmitXmlDeclaration(final boolean omitXmlDeclaration) {
this.omitXmlDeclaration = omitXmlDeclaration;
}
/**
* Transform Source to Result
*
* @param source Source to be transformed
* @param result Result containing transformed information
*/
@Override
public void transform(final Source source, final Result result) {
Objects.requireNonNull(source, "Source required");
Objects.requireNonNull(result, "Result required");
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
final Transformer transformer;
try {
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ProcessingAttribute.ACCESS_EXTERNAL_DTD.getValue());
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ProcessingAttribute.ACCESS_EXTERNAL_STYLESHEET.getValue());
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, SECURE_PROCESSING_ENABLED);
transformer = transformerFactory.newTransformer();
} catch (final TransformerConfigurationException e) {
throw new ProcessingException("Transformer configuration failed", e);
}
if (indent) {
transformer.setOutputProperty(OutputKeys.INDENT, ENABLED_PROPERTY);
transformer.setOutputProperty(INDENT_AMOUNT_OUTPUT_KEY, INDENT_AMOUNT);
}
if (method != null) {
transformer.setOutputProperty(OutputKeys.METHOD, method);
}
if (omitXmlDeclaration) {
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, ENABLED_PROPERTY);
}
try {
transformer.transform(source, result);
} catch (final TransformerException e) {
throw new ProcessingException("Transform failed", e);
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.xml.processing.transform;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
/**
* XML Transformation Provider
*/
public interface TransformProvider {
/**
* Transform Source to Result
*
* @param source Source to be transformed
* @param result Result containing transformed information
*/
void transform(Source source, Result result);
}

View File

@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.xml.processing.transform;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.ResourceProvider;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Node;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.io.InputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
class StandardTransformProviderTest {
private static final String METHOD = "xml";
@Test
void testTransformStandard() throws IOException {
final StandardTransformProvider provider = new StandardTransformProvider();
final DOMResult result = new DOMResult();
try (final InputStream inputStream = ResourceProvider.getStandardDocument()) {
final Source source = new StreamSource(inputStream);
provider.transform(source, result);
}
assertDocumentNodeFound(result);
}
@Test
void testTransformStandardDocumentTypeDeclaration() throws IOException {
final StandardTransformProvider provider = new StandardTransformProvider();
provider.setIndent(true);
provider.setOmitXmlDeclaration(true);
provider.setMethod(METHOD);
final DOMResult result = new DOMResult();
try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocType()) {
final Source source = new StreamSource(inputStream);
provider.transform(source, result);
}
assertDocumentNodeFound(result);
}
@Test
void testTransformStandardExternalEntityException() throws IOException {
final StandardTransformProvider provider = new StandardTransformProvider();
final DOMResult result = new DOMResult();
try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocTypeEntity()) {
final Source source = new StreamSource(inputStream);
final ProcessingException processingException = assertThrows(ProcessingException.class, () -> provider.transform(source, result));
assertInstanceOf(TransformerException.class, processingException.getCause());
}
}
private void assertDocumentNodeFound(final DOMResult result) {
final Node node = result.getNode();
assertNotNull(node);
assertEquals(Node.DOCUMENT_NODE, node.getNodeType());
}
}

View File

@ -32,10 +32,10 @@ import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
import org.apache.nifi.web.api.dto.RelationshipDTO;
import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
import org.apache.nifi.web.api.dto.TemplateDTO;
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
import org.apache.nifi.xml.processing.transform.TransformProvider;
import org.w3c.dom.Element;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
@ -58,9 +58,8 @@ public class TemplateUtils {
// need to stream the template element as the TemplateDeserializer.deserialize operation needs to re-parse
// in order to apply explicit properties on the XMLInputFactory
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
final Transformer transformer = transformerFactory.newTransformer();
transformer.transform(domSource, streamResult);
final TransformProvider transformProvider = new StandardTransformProvider();
transformProvider.transform(domSource, streamResult);
return parseDto(baos.toByteArray());
} catch (final Exception e) {

View File

@ -52,16 +52,12 @@ import org.apache.nifi.util.CharacterFilterUtils;
import org.apache.nifi.util.StringUtils;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.BufferedOutputStream;
@ -127,7 +123,7 @@ public class StandardFlowSerializer implements FlowSerializer<Document> {
}
return doc;
} catch (final ProcessingException | DOMException | TransformerFactoryConfigurationError | IllegalArgumentException e) {
} catch (final ProcessingException | DOMException | IllegalArgumentException e) {
throw new FlowSerializationException(e);
}
}
@ -139,15 +135,13 @@ public class StandardFlowSerializer implements FlowSerializer<Document> {
final StreamResult streamResult = new StreamResult(new BufferedOutputStream(os));
// configure the transformer and convert the DOM
final TransformerFactory transformFactory = TransformerFactory.newInstance();
final Transformer transformer = transformFactory.newTransformer();
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
final StandardTransformProvider transformProvider = new StandardTransformProvider();
transformProvider.setIndent(true);
// transform the document to byte stream
transformer.transform(domSource, streamResult);
transformProvider.transform(domSource, streamResult);
} catch (final DOMException | TransformerFactoryConfigurationError | IllegalArgumentException | TransformerException e) {
} catch (final DOMException | IllegalArgumentException | ProcessingException e) {
throw new FlowSerializationException(e);
}
}

View File

@ -27,6 +27,8 @@ import org.apache.nifi.web.api.dto.PortDTO;
import org.apache.nifi.web.api.dto.PositionDTO;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
import org.apache.nifi.xml.processing.transform.TransformProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
@ -40,8 +42,6 @@ import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.validation.Schema;
@ -278,13 +278,14 @@ public class FlowParser {
* @param flowDocument flowDocument of the associated XML content to write to disk
* @param flowXmlPath path on disk to write the flow
* @throws IOException if there are issues in accessing the target destination for the flow
* @throws TransformerException if there are issues in the xml transformation process
*/
public void writeFlow(final Document flowDocument, final Path flowXmlPath) throws IOException, TransformerException {
public void writeFlow(final Document flowDocument, final Path flowXmlPath) throws IOException {
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
final Source xmlSource = new DOMSource(flowDocument);
final Result outputTarget = new StreamResult(outputStream);
TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget);
final TransformProvider transformProvider = new StandardTransformProvider();
transformProvider.transform(xmlSource, outputTarget);
final InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
try (final OutputStream output = Files.newOutputStream(flowXmlPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

View File

@ -23,9 +23,6 @@ import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.lang.StringUtils;
@ -45,6 +42,8 @@ import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
import org.apache.nifi.xml.processing.transform.TransformProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@ -142,9 +141,9 @@ public class ManagedRangerAuthorizer extends RangerNiFiAuthorizer implements Man
userGroupProviderElement.appendChild(document.createTextNode(((ConfigurableUserGroupProvider) userGroupProvider).getFingerprint()));
}
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(document), new StreamResult(out));
} catch (final ProcessingException | TransformerException e) {
final TransformProvider transformProvider = new StandardTransformProvider();
transformProvider.transform(new DOMSource(document), new StreamResult(out));
} catch (final ProcessingException e) {
throw new AuthorizationAccessException("Unable to generate fingerprint", e);
}

View File

@ -34,6 +34,11 @@
<version>1.17.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
<version>1.17.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>

View File

@ -25,6 +25,8 @@ import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.io.DatumReader;
import org.apache.nifi.web.ViewableContent.DisplayMode;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
@ -33,11 +35,6 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
@ -94,13 +91,11 @@ public class StandardContentViewerController extends HttpServlet {
final StreamSource source = new StreamSource(content.getContentStream());
final StreamResult result = new StreamResult(writer);
final TransformerFactory transformFactory = TransformerFactory.newInstance();
final Transformer transformer = transformFactory.newTransformer();
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
final StandardTransformProvider transformProvider = new StandardTransformProvider();
transformProvider.setIndent(true);
transformer.transform(source, result);
} catch (final TransformerFactoryConfigurationError | TransformerException te) {
transformProvider.transform(source, result);
} catch (final ProcessingException te) {
throw new IOException("Unable to transform content as XML: " + te, te);
}

View File

@ -32,18 +32,11 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import javax.xml.namespace.QName;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPathExpression;
@ -77,7 +70,9 @@ import org.apache.nifi.processor.ProcessorInitializationContext;
import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processors.standard.xml.DocumentTypeAllowedDocumentProvider;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
import org.w3c.dom.Document;
import net.sf.saxon.xpath.XPathEvaluator;
@ -278,6 +273,8 @@ public class EvaluateXPath extends AbstractProcessor {
final boolean validatingDeclaration = context.getProperty(VALIDATE_DTD).asBoolean();
final StandardTransformProvider transformProvider = new StandardTransformProvider();
transformProvider.setIndent(true);
flowFileLoop:
for (FlowFile flowFile : flowFiles) {
final AtomicReference<Throwable> error = new AtomicReference<>(null);
@ -331,17 +328,19 @@ public class EvaluateXPath extends AbstractProcessor {
if (DESTINATION_ATTRIBUTE.equals(destination)) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doTransform(sourceNode, baos);
final StreamResult streamResult = new StreamResult(baos);
transformProvider.transform(sourceNode, streamResult);
xpathResults.put(entry.getKey(), new String(baos.toByteArray(), StandardCharsets.UTF_8));
} catch (TransformerException e) {
} catch (final ProcessingException e) {
error.set(e);
}
} else if (DESTINATION_CONTENT.equals(destination)) {
flowFile = session.write(flowFile, rawOut -> {
try (final OutputStream out = new BufferedOutputStream(rawOut)) {
doTransform(sourceNode, out);
} catch (TransformerException e) {
final StreamResult streamResult = new StreamResult(out);
transformProvider.transform(sourceNode, streamResult);
} catch (final ProcessingException e) {
error.set(e);
}
});
@ -381,48 +380,6 @@ public class EvaluateXPath extends AbstractProcessor {
}
}
private void doTransform(final Source sourceNode, OutputStream out) throws TransformerFactoryConfigurationError, TransformerException {
final Transformer transformer;
try {
transformer = TransformerFactory.newInstance().newTransformer();
} catch (final Exception e) {
throw new ProcessException(e);
}
final Properties props = new Properties();
props.setProperty(OutputKeys.METHOD, "xml");
props.setProperty(OutputKeys.INDENT, "no");
props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperties(props);
final ComponentLog logger = getLogger();
final AtomicReference<TransformerException> error = new AtomicReference<>(null);
transformer.setErrorListener(new ErrorListener() {
@Override
public void warning(final TransformerException exception) {
logger.warn("Encountered warning from XPath Engine", exception);
}
@Override
public void error(final TransformerException exception) {
logger.error("Encountered error from XPath Engine", exception);
error.set(exception);
}
@Override
public void fatalError(final TransformerException exception) {
logger.error("Encountered warning from XPath Engine", exception);
error.set(exception);
}
});
transformer.transform(sourceNode, new StreamResult(out));
if (error.get() != null) {
throw error.get();
}
}
private static class XPathValidator implements Validator {
@Override

View File

@ -30,14 +30,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import net.sf.saxon.s9api.Processor;
@ -75,7 +69,9 @@ import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processors.standard.xml.DocumentTypeAllowedDocumentProvider;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
import org.w3c.dom.Document;
@EventDriven
@ -315,9 +311,9 @@ public class EvaluateXQuery extends AbstractProcessor {
final XdmItem item = result.itemAt(0);
flowFile = session.write(flowFile, rawOut -> {
try (final OutputStream out = new BufferedOutputStream(rawOut)) {
writeformattedItem(item, context, out);
} catch (TransformerFactoryConfigurationError | TransformerException e) {
throw new IOException(e);
writeFormattedItem(item, context, out);
} catch (final ProcessingException e) {
throw new IOException("Writing Formatted Output failed", e);
}
});
} else {
@ -326,9 +322,9 @@ public class EvaluateXQuery extends AbstractProcessor {
ff = session.write(ff, rawOut -> {
try (final OutputStream out = new BufferedOutputStream(rawOut)) {
try {
writeformattedItem(item, context, out);
} catch (TransformerFactoryConfigurationError | TransformerException e) {
throw new IOException(e);
writeFormattedItem(item, context, out);
} catch (final ProcessingException e) {
throw new IOException("Writing Formatted Output failed", e);
}
}
});
@ -341,7 +337,7 @@ public class EvaluateXQuery extends AbstractProcessor {
session.transfer(flowFile, REL_FAILURE);
session.remove(childrenFlowFiles);
continue flowFileLoop;
} catch (TransformerFactoryConfigurationError | TransformerException | IOException e) {
} catch (final IOException e) {
logger.error("XQuery Property [{}] configuration failed", entry.getKey(), e);
session.transfer(flowFile, REL_FAILURE);
session.remove(childrenFlowFiles);
@ -369,14 +365,13 @@ public class EvaluateXQuery extends AbstractProcessor {
} // end flowFileLoop
}
private String formatItem(XdmItem item, ProcessContext context) throws TransformerFactoryConfigurationError, TransformerException, IOException {
private String formatItem(XdmItem item, ProcessContext context) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeformattedItem(item, context, baos);
writeFormattedItem(item, context, baos);
return baos.toString();
}
void writeformattedItem(XdmItem item, ProcessContext context, OutputStream out)
throws TransformerFactoryConfigurationError, TransformerException, IOException {
void writeFormattedItem(XdmItem item, ProcessContext context, OutputStream out) throws IOException {
if (item.isAtomicValue()) {
out.write(item.getStringValue().getBytes(StandardCharsets.UTF_8));
@ -385,10 +380,18 @@ public class EvaluateXQuery extends AbstractProcessor {
switch (node.getNodeKind()) {
case DOCUMENT:
case ELEMENT:
Transformer transformer = TransformerFactory.newInstance().newTransformer();
final Properties props = getTransformerProperties(context);
transformer.setOutputProperties(props);
transformer.transform(node.asSource(), new StreamResult(out));
final StandardTransformProvider transformProvider = new StandardTransformProvider();
final String method = context.getProperty(XML_OUTPUT_METHOD).getValue();
transformProvider.setMethod(method);
final boolean indentEnabled = context.getProperty(XML_OUTPUT_INDENT).asBoolean();
transformProvider.setIndent(indentEnabled);
final boolean omitXmlDeclaration = context.getProperty(XML_OUTPUT_OMIT_XML_DECLARATION).asBoolean();
transformProvider.setOmitXmlDeclaration(omitXmlDeclaration);
final StreamResult result = new StreamResult(out);
transformProvider.transform(node.asSource(), result);
break;
default:
out.write(node.getStringValue().getBytes(StandardCharsets.UTF_8));
@ -396,21 +399,6 @@ public class EvaluateXQuery extends AbstractProcessor {
}
}
private Properties getTransformerProperties(ProcessContext context) {
final String method = context.getProperty(XML_OUTPUT_METHOD).getValue();
boolean indent = context.getProperty(XML_OUTPUT_INDENT).asBoolean();
boolean omitDeclaration = context.getProperty(XML_OUTPUT_OMIT_XML_DECLARATION).asBoolean();
return getTransformerProperties(method, indent, omitDeclaration);
}
static Properties getTransformerProperties(final String method, final boolean indent, final boolean omitDeclaration) {
final Properties props = new Properties();
props.setProperty(OutputKeys.METHOD, method);
props.setProperty(OutputKeys.INDENT, indent ? "yes" : "no");
props.setProperty(OutputKeys.OMIT_XML_DECLARATION, omitDeclaration ? "yes" : "no");
return props;
}
private static class XQueryValidator implements Validator {
@Override

View File

@ -32,9 +32,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javax.xml.transform.OutputKeys;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
@ -53,21 +50,6 @@ public class TestEvaluateXQuery {
private static final String[] methods = {EvaluateXQuery.OUTPUT_METHOD_XML, EvaluateXQuery.OUTPUT_METHOD_HTML, EvaluateXQuery.OUTPUT_METHOD_TEXT};
private static final boolean[] booleans = {true, false};
@Test
public void testSetTransformerProperties() {
for (final String method : methods) {
for (final boolean indent : booleans) {
for (final boolean omitDeclaration : booleans) {
Properties props = EvaluateXQuery.getTransformerProperties(method, indent, omitDeclaration);
assertEquals(3, props.size());
assertEquals(method, props.getProperty(OutputKeys.METHOD));
assertEquals(indent ? "yes" : "no", props.getProperty(OutputKeys.INDENT));
assertEquals(omitDeclaration ? "yes" : "no", props.getProperty(OutputKeys.OMIT_XML_DECLARATION));
}
}
}
}
@Test
public void testFormatting() throws Exception {

View File

@ -42,6 +42,8 @@ import org.apache.nifi.registry.util.PropertyValue;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.DocumentProvider;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.apache.nifi.xml.processing.transform.StandardTransformProvider;
import org.apache.nifi.xml.processing.transform.TransformProvider;
import org.apache.ranger.audit.model.AuthzAuditEvent;
import org.apache.ranger.authorization.hadoop.config.RangerConfiguration;
import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig;
@ -56,9 +58,6 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
@ -339,9 +338,9 @@ public class RangerAuthorizer implements ManagedAuthorizer, AuthorizationAuditor
userGroupProviderElement.appendChild(document.createTextNode(((ConfigurableUserGroupProvider) userGroupProvider).getFingerprint()));
}
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(document), new StreamResult(out));
} catch (final ProcessingException | TransformerException e) {
final TransformProvider transformProvider = new StandardTransformProvider();
transformProvider.transform(new DOMSource(document), new StreamResult(out));
} catch (final ProcessingException e) {
throw new AuthorizationAccessException("Unable to generate fingerprint", e);
}