| /* |
| * 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.minifi; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.nifi.util.LoggingXmlParserErrorHandler; |
| import org.apache.nifi.util.file.FileUtils; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.SAXException; |
| |
| import javax.xml.XMLConstants; |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| 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 java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.StandardOpenOption; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.zip.GZIPInputStream; |
| import java.util.zip.GZIPOutputStream; |
| |
| /** |
| * Parses a flow from its xml.gz format into an XML {@link Document}. This class is primarily toward utilities for assisting |
| * with the handling of component bundles. |
| * <p> |
| * Provides auxiliary methods to aid in evaluating and manipulating the flow. |
| */ |
| public class FlowParser { |
| |
| private static final Logger logger = LoggerFactory.getLogger(FlowParser.class); |
| |
| /** |
| * Generates a {@link Document} from the flow configuration file provided |
| */ |
| public Document parse(final File flowConfigurationFile) { |
| if (flowConfigurationFile == null) { |
| logger.debug("Flow Configuration file was null"); |
| return null; |
| } |
| |
| // if the flow doesn't exist or is 0 bytes, then return null |
| final Path flowPath = flowConfigurationFile.toPath(); |
| try { |
| if (!Files.exists(flowPath) || Files.size(flowPath) == 0) { |
| logger.warn("Flow Configuration does not exist or was empty"); |
| return null; |
| } |
| } catch (IOException e) { |
| logger.error("An error occurred determining the size of the Flow Configuration file"); |
| return null; |
| } |
| |
| // otherwise create the appropriate input streams to read the file |
| try (final InputStream in = Files.newInputStream(flowPath, StandardOpenOption.READ); |
| final InputStream gzipIn = new GZIPInputStream(in)) { |
| |
| final byte[] flowBytes = IOUtils.toByteArray(gzipIn); |
| if (flowBytes == null || flowBytes.length == 0) { |
| logger.warn("Could not extract root group id because Flow Configuration File was empty"); |
| return null; |
| } |
| |
| final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); |
| docFactory.setNamespaceAware(true); |
| docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); |
| |
| // parse the flow |
| final DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); |
| docBuilder.setErrorHandler(new LoggingXmlParserErrorHandler("Flow Configuration", logger)); |
| final Document document = docBuilder.parse(new ByteArrayInputStream(flowBytes)); |
| return document; |
| |
| } catch (final SAXException | ParserConfigurationException | IOException ex) { |
| logger.error("Unable to parse flow {} due to {}", new Object[]{flowPath.toAbsolutePath(), ex}); |
| return null; |
| } |
| } |
| |
| |
| /** |
| * Writes a given XML Flow out to the specified path. |
| * |
| * @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 { |
| final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
| final Source xmlSource = new DOMSource(flowDocument); |
| final Result outputTarget = new StreamResult(outputStream); |
| TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget); |
| final InputStream is = new ByteArrayInputStream(outputStream.toByteArray()); |
| |
| try (final OutputStream output = Files.newOutputStream(flowXmlPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE); |
| final OutputStream gzipOut = new GZIPOutputStream(output);) { |
| FileUtils.copy(is, gzipOut); |
| } |
| } |
| |
| /** |
| * Finds child elements with the given tagName. |
| * |
| * @param element the parent element |
| * @param tagName the child element name to find |
| * @return a list of matching child elements |
| */ |
| public static List<Element> getChildrenByTagName(final Element element, final String tagName) { |
| final List<Element> matches = new ArrayList<>(); |
| final NodeList nodeList = element.getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| final Node node = nodeList.item(i); |
| if (!(node instanceof Element)) { |
| continue; |
| } |
| final Element child = (Element) nodeList.item(i); |
| if (child.getNodeName().equals(tagName)) { |
| matches.add(child); |
| } |
| } |
| return matches; |
| } |
| |
| } |