http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateManager.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateManager.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateManager.java
deleted file mode 100644
index a332e05..0000000
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateManager.java
+++ /dev/null
@@ -1,524 +0,0 @@
-/*
- * 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.controller;
-
-import static java.util.Objects.requireNonNull;
-
-import java.io.BufferedInputStream;
-import java.io.DataInputStream;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.persistence.TemplateDeserializer;
-import org.apache.nifi.persistence.TemplateSerializer;
-import org.apache.nifi.stream.io.ByteArrayInputStream;
-import org.apache.nifi.stream.io.ByteArrayOutputStream;
-import org.apache.nifi.stream.io.DataOutputStream;
-import org.apache.nifi.stream.io.StreamUtils;
-import org.apache.nifi.web.api.dto.ConnectableDTO;
-import org.apache.nifi.web.api.dto.ConnectionDTO;
-import org.apache.nifi.web.api.dto.ControllerServiceDTO;
-import org.apache.nifi.web.api.dto.FlowSnippetDTO;
-import org.apache.nifi.web.api.dto.ProcessGroupDTO;
-import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
-import org.apache.nifi.web.api.dto.ProcessorDTO;
-import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
-import org.apache.nifi.web.api.dto.RemoteProcessGroupContentsDTO;
-import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
-import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
-import org.apache.nifi.web.api.dto.TemplateDTO;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class TemplateManager {
-
-    private static final Logger logger = 
LoggerFactory.getLogger(TemplateManager.class);
-
-    private final Path directory;
-    private final Map<String, Template> templateMap = new HashMap<>();
-
-    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
-    private final Lock readLock = rwLock.readLock();
-    private final Lock writeLock = rwLock.writeLock();
-
-    private final FileFilter templateFileFilter = new FileFilter() {
-        @Override
-        public boolean accept(File pathname) {
-            return pathname.getName().toLowerCase().endsWith(".template");
-        }
-    };
-
-    public TemplateManager(final Path storageLocation) throws IOException {
-        directory = storageLocation;
-        if (!Files.exists(directory)) {
-            Files.createDirectories(directory);
-        } else {
-            if (!Files.isDirectory(directory)) {
-                throw new IllegalArgumentException(directory.toString() + " is 
not a directory");
-            }
-            // use toFile().canXXX, rather than Files.is... because on Windows 
7, we sometimes get the wrong result for Files.is... (running Java 7 update 9)
-            if (!directory.toFile().canExecute() || 
!directory.toFile().canWrite()) {
-                throw new IOException("Invalid permissions for directory " + 
directory.toString());
-            }
-        }
-    }
-
-    /**
-     * Adds a template to this manager. The contents of this template must be 
part of the current flow. This is going create a template based on a snippet of 
this flow. Any sensitive properties in the
-     * TemplateDTO will be removed.
-     *
-     * @param dto dto
-     * @return a copy of the given DTO
-     * @throws IOException if an I/O error occurs when persisting the Template
-     * @throws NullPointerException if the DTO is null
-     * @throws IllegalArgumentException if does not contain all required 
information, such as the template name or a processor's configuration element
-     */
-    public Template addTemplate(final TemplateDTO dto) throws IOException {
-        scrubTemplate(dto.getSnippet());
-        return importTemplate(dto);
-    }
-
-    private void verifyCanImport(final TemplateDTO dto) {
-        // ensure the template is specified
-        if (dto == null || dto.getSnippet() == null) {
-            throw new IllegalArgumentException("Template details not 
specified.");
-        }
-
-        // ensure the name is specified
-        if (StringUtils.isBlank(dto.getName())) {
-            throw new IllegalArgumentException("Template name cannot be 
blank.");
-        }
-
-        readLock.lock();
-        try {
-            for (final Template template : templateMap.values()) {
-                final TemplateDTO existingDto = template.getDetails();
-
-                // ensure a template with this name doesnt already exist
-                if (dto.getName().equals(existingDto.getName())) {
-                    throw new IllegalStateException(String.format("A template 
named '%s' already exists.", dto.getName()));
-                }
-            }
-        } finally {
-            readLock.unlock();
-        }
-    }
-
-    /**
-     * Clears all Templates from the TemplateManager
-     *
-     * @throws java.io.IOException ioe
-     */
-    public void clear() throws IOException {
-        writeLock.lock();
-        try {
-            templateMap.clear();
-
-            final File[] files = 
directory.toFile().listFiles(templateFileFilter);
-            if (files == null) {
-                return;
-            }
-
-            for (final File file : files) {
-                boolean successful = false;
-
-                for (int i = 0; i < 10; i++) {
-                    if (file.delete()) {
-                        successful = true;
-                        break;
-                    }
-                }
-
-                if (!successful && file.exists()) {
-                    throw new IOException("Failed to delete template file " + 
file.getAbsolutePath());
-                }
-            }
-        } finally {
-            writeLock.unlock();
-        }
-    }
-
-    /**
-     * @param id template id
-     * @return the template with the given id, if it exists; else, returns null
-     */
-    public Template getTemplate(final String id) {
-        readLock.lock();
-        try {
-            return templateMap.get(id);
-        } finally {
-            readLock.unlock();
-        }
-    }
-
-    /**
-     * Loads the templates from disk
-     *
-     * @throws IOException ioe
-     */
-    public void loadTemplates() throws IOException {
-        writeLock.lock();
-        try {
-            final File[] files = 
directory.toFile().listFiles(templateFileFilter);
-            if (files == null) {
-                return;
-            }
-
-            for (final File file : files) {
-                try (final FileInputStream fis = new FileInputStream(file);
-                        final BufferedInputStream bis = new 
BufferedInputStream(fis)) {
-                    final TemplateDTO templateDto = 
TemplateDeserializer.deserialize(bis);
-                    templateMap.put(templateDto.getId(), new 
Template(templateDto));
-                }
-            }
-        } finally {
-            writeLock.unlock();
-        }
-    }
-
-    public Template importTemplate(final TemplateDTO dto) throws IOException {
-        // ensure we can add this template
-        verifyCanImport(dto);
-
-        writeLock.lock();
-        try {
-            if (requireNonNull(dto).getId() == null) {
-                dto.setId(UUID.randomUUID().toString());
-            }
-
-            final Template template = new Template(dto);
-            persistTemplate(template);
-
-            templateMap.put(dto.getId(), template);
-            return template;
-        } finally {
-            writeLock.unlock();
-        }
-    }
-
-    /**
-     * Persists the given template to disk
-     *
-     * @param template template
-     * @throws IOException ioe
-     */
-    private void persistTemplate(final Template template) throws IOException {
-        final Path path = directory.resolve(template.getDetails().getId() + 
".template");
-        Files.write(path, TemplateSerializer.serialize(template.getDetails()), 
StandardOpenOption.WRITE, StandardOpenOption.CREATE);
-    }
-
-    /**
-     * Scrubs the template prior to persisting in order to remove fields that 
shouldn't be included or are unnecessary.
-     *
-     * @param snippet snippet
-     */
-    private void scrubTemplate(final FlowSnippetDTO snippet) {
-        // ensure that contents have been specified
-        if (snippet != null) {
-            // go through each processor if specified
-            if (snippet.getProcessors() != null) {
-                scrubProcessors(snippet.getProcessors());
-            }
-
-            // go through each connection if specified
-            if (snippet.getConnections() != null) {
-                scrubConnections(snippet.getConnections());
-            }
-
-            // go through each remote process group if specified
-            if (snippet.getRemoteProcessGroups() != null) {
-                scrubRemoteProcessGroups(snippet.getRemoteProcessGroups());
-            }
-
-            // go through each process group if specified
-            if (snippet.getProcessGroups() != null) {
-                scrubProcessGroups(snippet.getProcessGroups());
-            }
-
-            // go through each controller service if specified
-            if (snippet.getControllerServices() != null) {
-                scrubControllerServices(snippet.getControllerServices());
-            }
-        }
-    }
-
-    /**
-     * Scrubs process groups prior to saving.
-     *
-     * @param processGroups groups
-     */
-    private void scrubProcessGroups(final Set<ProcessGroupDTO> processGroups) {
-        // go through each process group
-        for (final ProcessGroupDTO processGroupDTO : processGroups) {
-            scrubTemplate(processGroupDTO.getContents());
-        }
-    }
-
-    /**
-     * Scrubs processors prior to saving. This includes removing sensitive 
properties, validation errors, property descriptors, etc.
-     *
-     * @param processors procs
-     */
-    private void scrubProcessors(final Set<ProcessorDTO> processors) {
-        // go through each processor
-        for (final ProcessorDTO processorDTO : processors) {
-            final ProcessorConfigDTO processorConfig = 
processorDTO.getConfig();
-
-            // ensure that some property configuration have been specified
-            if (processorConfig != null) {
-                // if properties have been specified, remove sensitive ones
-                if (processorConfig.getProperties() != null) {
-                    Map<String, String> processorProperties = 
processorConfig.getProperties();
-
-                    // look for sensitive properties and remove them
-                    if (processorConfig.getDescriptors() != null) {
-                        final Collection<PropertyDescriptorDTO> descriptors = 
processorConfig.getDescriptors().values();
-                        for (PropertyDescriptorDTO descriptor : descriptors) {
-                            if (descriptor.isSensitive()) {
-                                processorProperties.put(descriptor.getName(), 
null);
-                            }
-                        }
-                    }
-                }
-
-                processorConfig.setCustomUiUrl(null);
-            }
-
-            // remove validation errors
-            processorDTO.setValidationErrors(null);
-            processorDTO.setInputRequirement(null);
-        }
-    }
-
-    private void scrubControllerServices(final Set<ControllerServiceDTO> 
controllerServices) {
-        for (final ControllerServiceDTO serviceDTO : controllerServices) {
-            final Map<String, String> properties = serviceDTO.getProperties();
-            final Map<String, PropertyDescriptorDTO> descriptors = 
serviceDTO.getDescriptors();
-
-            if (properties != null && descriptors != null) {
-                for (final PropertyDescriptorDTO descriptor : 
descriptors.values()) {
-                    if (descriptor.isSensitive()) {
-                        properties.put(descriptor.getName(), null);
-                    }
-                }
-            }
-
-            serviceDTO.setCustomUiUrl(null);
-            serviceDTO.setValidationErrors(null);
-        }
-    }
-
-    /**
-     * Scrubs connections prior to saving. This includes removing available 
relationships.
-     *
-     * @param connections conns
-     */
-    private void scrubConnections(final Set<ConnectionDTO> connections) {
-        // go through each connection
-        for (final ConnectionDTO connectionDTO : connections) {
-            connectionDTO.setAvailableRelationships(null);
-
-            scrubConnectable(connectionDTO.getSource());
-            scrubConnectable(connectionDTO.getDestination());
-        }
-    }
-
-    /**
-     * Remove unnecessary fields in connectables prior to saving.
-     *
-     * @param connectable connectable
-     */
-    private void scrubConnectable(final ConnectableDTO connectable) {
-        if (connectable != null) {
-            connectable.setComments(null);
-            connectable.setExists(null);
-            connectable.setRunning(null);
-            connectable.setTransmitting(null);
-            connectable.setName(null);
-        }
-    }
-
-    /**
-     * Remove unnecessary fields in remote groups prior to saving.
-     *
-     * @param remoteGroups groups
-     */
-    private void scrubRemoteProcessGroups(final Set<RemoteProcessGroupDTO> 
remoteGroups) {
-        // go through each remote process group
-        for (final RemoteProcessGroupDTO remoteProcessGroupDTO : remoteGroups) 
{
-            remoteProcessGroupDTO.setFlowRefreshed(null);
-            remoteProcessGroupDTO.setInputPortCount(null);
-            remoteProcessGroupDTO.setOutputPortCount(null);
-            remoteProcessGroupDTO.setTransmitting(null);
-
-            // if this remote process group has contents
-            if (remoteProcessGroupDTO.getContents() != null) {
-                RemoteProcessGroupContentsDTO contents = 
remoteProcessGroupDTO.getContents();
-
-                // scrub any remote input ports
-                if (contents.getInputPorts() != null) {
-                    scrubRemotePorts(contents.getInputPorts());
-                }
-
-                // scrub and remote output ports
-                if (contents.getOutputPorts() != null) {
-                    scrubRemotePorts(contents.getOutputPorts());
-                }
-            }
-        }
-    }
-
-    /**
-     * Remove unnecessary fields in remote ports prior to saving.
-     *
-     * @param remotePorts ports
-     */
-    private void scrubRemotePorts(final Set<RemoteProcessGroupPortDTO> 
remotePorts) {
-        for (final Iterator<RemoteProcessGroupPortDTO> remotePortIter = 
remotePorts.iterator(); remotePortIter.hasNext();) {
-            final RemoteProcessGroupPortDTO remotePortDTO = 
remotePortIter.next();
-
-            // if the flow is not connected to this remote port, remove it
-            if (remotePortDTO.isConnected() == null || 
!remotePortDTO.isConnected().booleanValue()) {
-                remotePortIter.remove();
-                continue;
-            }
-
-            remotePortDTO.setExists(null);
-            remotePortDTO.setTargetRunning(null);
-        }
-    }
-
-    /**
-     * Removes the template with the given ID.
-     *
-     * @param id the ID of the template to remove
-     * @throws NullPointerException if the argument is null
-     * @throws IllegalStateException if no template exists with the given ID
-     * @throws IOException if template could not be removed
-     */
-    public void removeTemplate(final String id) throws IOException, 
IllegalStateException {
-        writeLock.lock();
-        try {
-            final Template removed = templateMap.remove(requireNonNull(id));
-
-            // ensure the template exists
-            if (removed == null) {
-                throw new IllegalStateException("No template with ID " + id + 
" exists");
-            } else {
-
-                try {
-                    // remove the template from the archive directory
-                    final Path path = 
directory.resolve(removed.getDetails().getId() + ".template");
-                    Files.delete(path);
-                } catch (final NoSuchFileException e) {
-                    logger.warn(String.format("Template file for template %s 
not found when attempting to remove. Continuing...", id));
-                } catch (final IOException e) {
-                    logger.error(String.format("Unable to remove template file 
for template %s.", id));
-
-                    // since the template file existed and we were unable to 
remove it, rollback
-                    // by returning it to the template map
-                    templateMap.put(id, removed);
-
-                    // rethrow
-                    throw e;
-                }
-            }
-        } finally {
-            writeLock.unlock();
-        }
-    }
-
-    public Set<Template> getTemplates() {
-        readLock.lock();
-        try {
-            return new HashSet<>(templateMap.values());
-        } finally {
-            readLock.unlock();
-        }
-    }
-
-    public static List<Template> parseBytes(final byte[] bytes) {
-        final List<Template> templates = new ArrayList<>();
-
-        try (final InputStream rawIn = new ByteArrayInputStream(bytes);
-                final DataInputStream in = new DataInputStream(rawIn)) {
-
-            while (isMoreData(in)) {
-                final int length = in.readInt();
-                final byte[] buffer = new byte[length];
-                StreamUtils.fillBuffer(in, buffer, true);
-                final TemplateDTO dto = TemplateDeserializer.deserialize(new 
ByteArrayInputStream(buffer));
-                templates.add(new Template(dto));
-            }
-        } catch (final IOException e) {
-            throw new RuntimeException("Could not parse bytes", e);  // won't 
happen because of the types of streams being used
-        }
-
-        return templates;
-    }
-
-    private static boolean isMoreData(final InputStream in) throws IOException 
{
-        in.mark(1);
-        final int nextByte = in.read();
-        if (nextByte == -1) {
-            return false;
-        }
-
-        in.reset();
-        return true;
-    }
-
-    public byte[] export() {
-        readLock.lock();
-        try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                final DataOutputStream dos = new DataOutputStream(baos)) {
-            for (final Template template : templateMap.values()) {
-                final TemplateDTO dto = template.getDetails();
-                final byte[] bytes = TemplateSerializer.serialize(dto);
-                dos.writeInt(bytes.length);
-                dos.write(bytes);
-            }
-
-            return baos.toByteArray();
-        } catch (final IOException e) {
-            // won't happen
-            return null;
-        } finally {
-            readLock.unlock();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateUtils.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateUtils.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateUtils.java
new file mode 100644
index 0000000..bcf692d
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateUtils.java
@@ -0,0 +1,287 @@
+/*
+ * 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.controller;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.dom.DOMSource;
+
+import org.apache.nifi.persistence.TemplateDeserializer;
+import org.apache.nifi.stream.io.ByteArrayInputStream;
+import org.apache.nifi.stream.io.StreamUtils;
+import org.apache.nifi.web.api.dto.ConnectableDTO;
+import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.api.dto.FlowSnippetDTO;
+import org.apache.nifi.web.api.dto.ProcessGroupDTO;
+import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
+import org.apache.nifi.web.api.dto.ProcessorDTO;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
+import org.apache.nifi.web.api.dto.RemoteProcessGroupContentsDTO;
+import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
+import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
+import org.apache.nifi.web.api.dto.TemplateDTO;
+import org.w3c.dom.Element;
+
+public class TemplateUtils {
+
+    public static TemplateDTO parseDto(final Element templateElement) {
+        try {
+            JAXBContext context = JAXBContext.newInstance(TemplateDTO.class);
+            Unmarshaller unmarshaller = context.createUnmarshaller();
+            return unmarshaller.unmarshal(new DOMSource(templateElement), 
TemplateDTO.class).getValue();
+        } catch (final Exception e) {
+            throw new RuntimeException("Could not parse XML as a valid 
template", e);
+        }
+    }
+
+    public static TemplateDTO parseDto(final byte[] bytes) {
+        try (final InputStream in = new ByteArrayInputStream(bytes)) {
+            return TemplateDeserializer.deserialize(in);
+        } catch (final IOException ioe) {
+            throw new RuntimeException("Could not parse bytes as template", 
ioe); // won't happen because of the types of streams being used
+        }
+    }
+
+    public static List<Template> parseTemplateStream(final byte[] bytes) {
+        final List<Template> templates = new ArrayList<>();
+
+        try (final InputStream rawIn = new ByteArrayInputStream(bytes);
+            final DataInputStream in = new DataInputStream(rawIn)) {
+
+            while (isMoreData(in)) {
+                final int length = in.readInt();
+                final byte[] buffer = new byte[length];
+                StreamUtils.fillBuffer(in, buffer, true);
+                final TemplateDTO dto = TemplateDeserializer.deserialize(new 
ByteArrayInputStream(buffer));
+                templates.add(new Template(dto));
+            }
+        } catch (final IOException e) {
+            throw new RuntimeException("Could not parse bytes", e);  // won't 
happen because of the types of streams being used
+        }
+
+        return templates;
+    }
+
+
+    private static boolean isMoreData(final InputStream in) throws IOException 
{
+        in.mark(1);
+        final int nextByte = in.read();
+        if (nextByte == -1) {
+            return false;
+        }
+
+        in.reset();
+        return true;
+    }
+
+    /**
+     * Scrubs the template prior to persisting in order to remove fields that 
shouldn't be included or are unnecessary.
+     *
+     * @param templateDto template
+     */
+    public static void scrubTemplate(final TemplateDTO templateDto) {
+        scrubSnippet(templateDto.getSnippet());
+    }
+
+    private static void scrubSnippet(final FlowSnippetDTO snippet) {
+        // ensure that contents have been specified
+        if (snippet != null) {
+            // go through each processor if specified
+            if (snippet.getProcessors() != null) {
+                scrubProcessors(snippet.getProcessors());
+            }
+
+            // go through each connection if specified
+            if (snippet.getConnections() != null) {
+                scrubConnections(snippet.getConnections());
+            }
+
+            // go through each remote process group if specified
+            if (snippet.getRemoteProcessGroups() != null) {
+                scrubRemoteProcessGroups(snippet.getRemoteProcessGroups());
+            }
+
+            // go through each process group if specified
+            if (snippet.getProcessGroups() != null) {
+                scrubProcessGroups(snippet.getProcessGroups());
+            }
+
+            // go through each controller service if specified
+            if (snippet.getControllerServices() != null) {
+                scrubControllerServices(snippet.getControllerServices());
+            }
+        }
+    }
+
+    /**
+     * Scrubs process groups prior to saving.
+     *
+     * @param processGroups groups
+     */
+    private static void scrubProcessGroups(final Set<ProcessGroupDTO> 
processGroups) {
+        // go through each process group
+        for (final ProcessGroupDTO processGroupDTO : processGroups) {
+            scrubSnippet(processGroupDTO.getContents());
+        }
+    }
+
+    /**
+     * Scrubs processors prior to saving. This includes removing sensitive 
properties, validation errors, property descriptors, etc.
+     *
+     * @param processors procs
+     */
+    private static void scrubProcessors(final Set<ProcessorDTO> processors) {
+        // go through each processor
+        for (final ProcessorDTO processorDTO : processors) {
+            final ProcessorConfigDTO processorConfig = 
processorDTO.getConfig();
+
+            // ensure that some property configuration have been specified
+            if (processorConfig != null) {
+                // if properties have been specified, remove sensitive ones
+                if (processorConfig.getProperties() != null) {
+                    Map<String, String> processorProperties = 
processorConfig.getProperties();
+
+                    // look for sensitive properties and remove them
+                    if (processorConfig.getDescriptors() != null) {
+                        final Collection<PropertyDescriptorDTO> descriptors = 
processorConfig.getDescriptors().values();
+                        for (PropertyDescriptorDTO descriptor : descriptors) {
+                            if (descriptor.isSensitive()) {
+                                processorProperties.put(descriptor.getName(), 
null);
+                            }
+                        }
+                    }
+                }
+
+                processorConfig.setCustomUiUrl(null);
+            }
+
+            // remove validation errors
+            processorDTO.setValidationErrors(null);
+            processorDTO.setInputRequirement(null);
+        }
+    }
+
+    private static void scrubControllerServices(final 
Set<ControllerServiceDTO> controllerServices) {
+        for (final ControllerServiceDTO serviceDTO : controllerServices) {
+            final Map<String, String> properties = serviceDTO.getProperties();
+            final Map<String, PropertyDescriptorDTO> descriptors = 
serviceDTO.getDescriptors();
+
+            if (properties != null && descriptors != null) {
+                for (final PropertyDescriptorDTO descriptor : 
descriptors.values()) {
+                    if (descriptor.isSensitive()) {
+                        properties.put(descriptor.getName(), null);
+                    }
+                }
+            }
+
+            serviceDTO.setCustomUiUrl(null);
+            serviceDTO.setValidationErrors(null);
+        }
+    }
+
+    /**
+     * Scrubs connections prior to saving. This includes removing available 
relationships.
+     *
+     * @param connections conns
+     */
+    private static void scrubConnections(final Set<ConnectionDTO> connections) 
{
+        // go through each connection
+        for (final ConnectionDTO connectionDTO : connections) {
+            connectionDTO.setAvailableRelationships(null);
+
+            scrubConnectable(connectionDTO.getSource());
+            scrubConnectable(connectionDTO.getDestination());
+        }
+    }
+
+    /**
+     * Remove unnecessary fields in connectables prior to saving.
+     *
+     * @param connectable connectable
+     */
+    private static void scrubConnectable(final ConnectableDTO connectable) {
+        if (connectable != null) {
+            connectable.setComments(null);
+            connectable.setExists(null);
+            connectable.setRunning(null);
+            connectable.setTransmitting(null);
+            connectable.setName(null);
+        }
+    }
+
+    /**
+     * Remove unnecessary fields in remote groups prior to saving.
+     *
+     * @param remoteGroups groups
+     */
+    private static void scrubRemoteProcessGroups(final 
Set<RemoteProcessGroupDTO> remoteGroups) {
+        // go through each remote process group
+        for (final RemoteProcessGroupDTO remoteProcessGroupDTO : remoteGroups) 
{
+            remoteProcessGroupDTO.setFlowRefreshed(null);
+            remoteProcessGroupDTO.setInputPortCount(null);
+            remoteProcessGroupDTO.setOutputPortCount(null);
+            remoteProcessGroupDTO.setTransmitting(null);
+
+            // if this remote process group has contents
+            if (remoteProcessGroupDTO.getContents() != null) {
+                RemoteProcessGroupContentsDTO contents = 
remoteProcessGroupDTO.getContents();
+
+                // scrub any remote input ports
+                if (contents.getInputPorts() != null) {
+                    scrubRemotePorts(contents.getInputPorts());
+                }
+
+                // scrub and remote output ports
+                if (contents.getOutputPorts() != null) {
+                    scrubRemotePorts(contents.getOutputPorts());
+                }
+            }
+        }
+    }
+
+    /**
+     * Remove unnecessary fields in remote ports prior to saving.
+     *
+     * @param remotePorts ports
+     */
+    private static void scrubRemotePorts(final Set<RemoteProcessGroupPortDTO> 
remotePorts) {
+        for (final Iterator<RemoteProcessGroupPortDTO> remotePortIter = 
remotePorts.iterator(); remotePortIter.hasNext();) {
+            final RemoteProcessGroupPortDTO remotePortDTO = 
remotePortIter.next();
+
+            // if the flow is not connected to this remote port, remove it
+            if (remotePortDTO.isConnected() == null || 
!remotePortDTO.isConnected().booleanValue()) {
+                remotePortIter.remove();
+                continue;
+            }
+
+            remotePortDTO.setExists(null);
+            remotePortDTO.setTargetRunning(null);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/serialization/StandardFlowSerializer.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/serialization/StandardFlowSerializer.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/serialization/StandardFlowSerializer.java
index e2ce751..4d0cd9d 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/serialization/StandardFlowSerializer.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/serialization/StandardFlowSerializer.java
@@ -17,6 +17,8 @@
 package org.apache.nifi.controller.serialization;
 
 import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
@@ -42,6 +44,7 @@ import org.apache.nifi.connectable.Size;
 import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ReportingTaskNode;
+import org.apache.nifi.controller.Template;
 import org.apache.nifi.controller.label.Label;
 import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceState;
@@ -49,12 +52,14 @@ import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.flowfile.FlowFilePrioritizer;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.RemoteProcessGroup;
+import org.apache.nifi.persistence.TemplateSerializer;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.remote.RemoteGroupPort;
 import org.apache.nifi.remote.RootGroupPort;
 import org.w3c.dom.DOMException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 
 /**
  * Serializes a Flow Controller as XML to an output stream.
@@ -186,6 +191,10 @@ public class StandardFlowSerializer implements 
FlowSerializer {
         for (final ControllerServiceNode service : 
group.getControllerServices(false)) {
             addControllerService(element, service);
         }
+
+        for (final Template template : group.getTemplates()) {
+            addTemplate(element, template);
+        }
     }
 
     private void addStyle(final Element parentElement, final Map<String, 
String> style) {
@@ -456,4 +465,21 @@ public class StandardFlowSerializer implements 
FlowSerializer {
         element.appendChild(toAdd);
     }
 
+    public static void addTemplate(final Element element, final Template 
template) {
+        try {
+            final byte[] serialized = 
TemplateSerializer.serialize(template.getDetails());
+
+            final DocumentBuilderFactory docBuilderFactory = 
DocumentBuilderFactory.newInstance();
+            final DocumentBuilder docBuilder = 
docBuilderFactory.newDocumentBuilder();
+            final Document document;
+            try (final InputStream in = new ByteArrayInputStream(serialized)) {
+                document = docBuilder.parse(in);
+            }
+
+            final Node templateNode = 
element.getOwnerDocument().importNode(document.getDocumentElement(), true);
+            element.appendChild(templateNode);
+        } catch (final Exception e) {
+            throw new FlowSerializationException(e);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
index 5ede175..be58d22 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
@@ -39,6 +39,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.validation.Schema;
 import javax.xml.validation.SchemaFactory;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.Template;
@@ -47,12 +48,12 @@ import 
org.apache.nifi.controller.serialization.FlowFromDOMFactory;
 import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.processor.Processor;
 import org.apache.nifi.util.DomUtils;
+import org.apache.nifi.web.api.dto.ComponentDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
 import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.FunnelDTO;
 import org.apache.nifi.web.api.dto.LabelDTO;
-import org.apache.nifi.web.api.dto.ComponentDTO;
 import org.apache.nifi.web.api.dto.PortDTO;
 import org.apache.nifi.web.api.dto.ProcessGroupDTO;
 import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
@@ -62,7 +63,6 @@ import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
 import org.apache.nifi.web.api.dto.ReportingTaskDTO;
 import org.apache.nifi.web.api.dto.TemplateDTO;
-import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -311,6 +311,7 @@ public final class FingerprintFactory {
 
     private StringBuilder addTemplateFingerprint(final StringBuilder builder, 
final TemplateDTO dto) {
         builder.append(dto.getId());
+        builder.append(dto.getGroupId());
         builder.append(dto.getName());
         builder.append(dto.getDescription());
         final FlowSnippetDTO snippet = dto.getSnippet();

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
index 8567c85..1a3ab52 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
@@ -41,6 +41,7 @@ import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.Snippet;
+import org.apache.nifi.controller.Template;
 import org.apache.nifi.controller.exception.ComponentLifeCycleException;
 import org.apache.nifi.controller.label.Label;
 import org.apache.nifi.controller.scheduling.StandardProcessScheduler;
@@ -95,6 +96,7 @@ public final class StandardProcessGroup implements 
ProcessGroup {
     private final Map<String, ProcessorNode> processors = new HashMap<>();
     private final Map<String, Funnel> funnels = new HashMap<>();
     private final Map<String, ControllerServiceNode> controllerServices = new 
HashMap<>();
+    private final Map<String, Template> templates = new HashMap<>();
     private final StringEncryptor encryptor;
 
     private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
@@ -1883,6 +1885,99 @@ public final class StandardProcessGroup implements 
ProcessGroup {
 
 
     @Override
+    public void addTemplate(final Template template) {
+        requireNonNull(template);
+
+        writeLock.lock();
+        try {
+            final String id = template.getDetails().getId();
+            if (id == null) {
+                throw new IllegalStateException("Cannot add template that has 
no ID");
+            }
+
+            if (templates.containsKey(id)) {
+                throw new IllegalStateException("Process Group already 
contains a Template with ID " + id);
+            }
+
+            templates.put(id, template);
+            template.setProcessGroup(this);
+            LOG.info("{} added to {}", template, this);
+        } finally {
+            writeLock.unlock();
+        }
+    }
+
+    @Override
+    public Template getTemplate(final String id) {
+        readLock.lock();
+        try {
+            return templates.get(id);
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    @Override
+    public Template findTemplate(final String id) {
+        return findTemplate(id, this);
+    }
+
+    private Template findTemplate(final String id, final ProcessGroup start) {
+        final Template template = start.getTemplate(id);
+        if (template != null) {
+            return template;
+        }
+
+        for (final ProcessGroup child : start.getProcessGroups()) {
+            final Template childTemplate = findTemplate(id, child);
+            if (childTemplate != null) {
+                return childTemplate;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public Set<Template> getTemplates() {
+        readLock.lock();
+        try {
+            return new HashSet<>(templates.values());
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    @Override
+    public Set<Template> findAllTemplates() {
+        return findAllTemplates(this);
+    }
+
+    private Set<Template> findAllTemplates(final ProcessGroup group) {
+        final Set<Template> templates = new HashSet<>(group.getTemplates());
+        for (final ProcessGroup childGroup : group.getProcessGroups()) {
+            templates.addAll(findAllTemplates(childGroup));
+        }
+        return templates;
+    }
+
+    @Override
+    public void removeTemplate(final Template template) {
+        writeLock.lock();
+        try {
+            final Template existing = 
templates.get(requireNonNull(template).getIdentifier());
+            if (existing == null) {
+                throw new IllegalStateException(template + " is not a member 
of this ProcessGroup");
+            }
+
+            templates.remove(template.getIdentifier());
+            LOG.info("{} removed from flow", template);
+        } finally {
+            writeLock.unlock();
+        }
+    }
+
+    @Override
     public void remove(final Snippet snippet) {
         writeLock.lock();
         try {

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/TemplateSerializer.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/TemplateSerializer.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/TemplateSerializer.java
index 72d7f56..7c9d9e6 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/TemplateSerializer.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/TemplateSerializer.java
@@ -44,4 +44,5 @@ public final class TemplateSerializer {
             throw new FlowSerializationException(e);
         }
     }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/StandardFlowServiceTest.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/StandardFlowServiceTest.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/StandardFlowServiceTest.java
index 15efd86..771bda6 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/StandardFlowServiceTest.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/StandardFlowServiceTest.java
@@ -80,7 +80,7 @@ public class StandardFlowServiceTest {
     @Test
     public void testLoadWithFlow() throws IOException {
         byte[] flowBytes = 
IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow.xml"));
-        flowService.load(new StandardDataFlow(flowBytes, null, null));
+        flowService.load(new StandardDataFlow(flowBytes, null));
 
         FlowSerializer serializer = new StandardFlowSerializer(mockEncryptor);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -95,16 +95,16 @@ public class StandardFlowServiceTest {
     @Test(expected = FlowSerializationException.class)
     public void testLoadWithCorruptFlow() throws IOException {
         byte[] flowBytes = 
IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow-corrupt.xml"));
-        flowService.load(new StandardDataFlow(flowBytes, null, null));
+        flowService.load(new StandardDataFlow(flowBytes, null));
     }
 
     @Test
     public void testLoadExistingFlow() throws IOException {
         byte[] flowBytes = 
IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow.xml"));
-        flowService.load(new StandardDataFlow(flowBytes, null, null));
+        flowService.load(new StandardDataFlow(flowBytes, null));
 
         flowBytes = 
IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow-inheritable.xml"));
-        flowService.load(new StandardDataFlow(flowBytes, null, null));
+        flowService.load(new StandardDataFlow(flowBytes, null));
 
         FlowSerializer serializer = new StandardFlowSerializer(mockEncryptor);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -118,11 +118,11 @@ public class StandardFlowServiceTest {
     @Test
     public void testLoadExistingFlowWithUninheritableFlow() throws IOException 
{
         byte[] originalBytes = 
IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow.xml"));
-        flowService.load(new StandardDataFlow(originalBytes, null, null));
+        flowService.load(new StandardDataFlow(originalBytes, null));
 
         try {
             byte[] updatedBytes = 
IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow-uninheritable.xml"));
-            flowService.load(new StandardDataFlow(updatedBytes, null, null));
+            flowService.load(new StandardDataFlow(updatedBytes, null));
             fail("should have thrown " + UninheritableFlowException.class);
         } catch (UninheritableFlowException ufe) {
 
@@ -140,11 +140,11 @@ public class StandardFlowServiceTest {
     @Test
     public void testLoadExistingFlowWithCorruptFlow() throws IOException {
         byte[] originalBytes = 
IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow.xml"));
-        flowService.load(new StandardDataFlow(originalBytes, null, null));
+        flowService.load(new StandardDataFlow(originalBytes, null));
 
         try {
             byte[] updatedBytes = 
IOUtils.toByteArray(StandardFlowServiceTest.class.getResourceAsStream("/conf/all-flow-corrupt.xml"));
-            flowService.load(new StandardDataFlow(updatedBytes, null, null));
+            flowService.load(new StandardDataFlow(updatedBytes, null));
             fail("should have thrown " + FlowSerializationException.class);
         } catch (FlowSerializationException ufe) {
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/MockProcessGroup.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/MockProcessGroup.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/MockProcessGroup.java
index 98e2571..11724f4 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/MockProcessGroup.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/MockProcessGroup.java
@@ -32,6 +32,7 @@ import org.apache.nifi.connectable.Port;
 import org.apache.nifi.connectable.Position;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.Snippet;
+import org.apache.nifi.controller.Template;
 import org.apache.nifi.controller.label.Label;
 import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.groups.ProcessGroup;
@@ -527,4 +528,33 @@ public class MockProcessGroup implements ProcessGroup {
 
     }
 
+    @Override
+    public void addTemplate(Template template) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeTemplate(Template template) {
+    }
+
+    @Override
+    public Template getTemplate(String id) {
+        return null;
+    }
+
+    @Override
+    public Template findTemplate(String id) {
+        return null;
+    }
+
+    @Override
+    public Set<Template> getTemplates() {
+        return null;
+    }
+
+    @Override
+    public Set<Template> findAllTemplates() {
+        return null;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index 47d0594..ecd6195 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -304,17 +304,20 @@ public interface NiFiServiceFacade {
      * @param name name
      * @param description description
      * @param snippetId id
+     * @param groupId id of the process group
      * @return template
      */
-    TemplateDTO createTemplate(String name, String description, String 
snippetId);
+    TemplateDTO createTemplate(String name, String description, String 
snippetId, String groupId);
 
     /**
      * Imports the specified Template.
      *
      * @param templateDTO The template dto
+     * @param groupId id of the process group
+     *
      * @return The new template dto
      */
-    TemplateDTO importTemplate(TemplateDTO templateDTO);
+    TemplateDTO importTemplate(TemplateDTO templateDTO, String groupId);
 
     /**
      * Instantiate the corresponding template.

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index 4ba8b33..ace31a3 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -16,8 +16,31 @@
  */
 package org.apache.nifi.web;
 
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.WebApplicationException;
+
 import org.apache.commons.lang3.StringUtils;
-import org.apache.log4j.Logger;
 import org.apache.nifi.action.Action;
 import org.apache.nifi.action.Component;
 import org.apache.nifi.action.FlowChangeAction;
@@ -166,35 +189,14 @@ import org.apache.nifi.web.revision.StandardRevisionClaim;
 import org.apache.nifi.web.revision.StandardRevisionUpdate;
 import org.apache.nifi.web.revision.UpdateRevisionTask;
 import org.apache.nifi.web.util.SnippetUtils;
-
-import javax.ws.rs.WebApplicationException;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.TimeZone;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Implementation of NiFiServiceFacade that performs revision checking.
  */
 public class StandardNiFiServiceFacade implements NiFiServiceFacade {
-    private static final Logger logger = 
Logger.getLogger(StandardNiFiServiceFacade.class);
+    private static final Logger logger = 
LoggerFactory.getLogger(StandardNiFiServiceFacade.class);
 
     // nifi core components
     private ControllerFacade controllerFacade;
@@ -258,136 +260,236 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
     @Override
     public void verifyUpdateConnection(ConnectionDTO connectionDTO) {
-        // if connection does not exist, then the update request is likely 
creating it
-        // so we don't verify since it will fail
-        if (connectionDAO.hasConnection(connectionDTO.getId())) {
-            connectionDAO.verifyUpdate(connectionDTO);
-        } else {
-            connectionDAO.verifyCreate(connectionDTO.getParentGroupId(), 
connectionDTO);
+        try {
+            // if connection does not exist, then the update request is likely 
creating it
+            // so we don't verify since it will fail
+            if (connectionDAO.hasConnection(connectionDTO.getId())) {
+                connectionDAO.verifyUpdate(connectionDTO);
+            } else {
+                connectionDAO.verifyCreate(connectionDTO.getParentGroupId(), 
connectionDTO);
+            }
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(connectionDTO.getId());
+            throw e;
         }
     }
 
     @Override
     public void verifyDeleteConnection(String connectionId) {
-        connectionDAO.verifyDelete(connectionId);
+        try {
+            connectionDAO.verifyDelete(connectionId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(connectionId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyDeleteFunnel(String funnelId) {
-        funnelDAO.verifyDelete(funnelId);
+        try {
+            funnelDAO.verifyDelete(funnelId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(funnelId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyUpdateInputPort(PortDTO inputPortDTO) {
-        // if connection does not exist, then the update request is likely 
creating it
-        // so we don't verify since it will fail
-        if (inputPortDAO.hasPort(inputPortDTO.getId())) {
-            inputPortDAO.verifyUpdate(inputPortDTO);
+        try {
+            // if connection does not exist, then the update request is likely 
creating it
+            // so we don't verify since it will fail
+            if (inputPortDAO.hasPort(inputPortDTO.getId())) {
+                inputPortDAO.verifyUpdate(inputPortDTO);
+            }
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(inputPortDTO.getId());
+            throw e;
         }
     }
 
     @Override
     public void verifyDeleteInputPort(String inputPortId) {
-        inputPortDAO.verifyDelete(inputPortId);
+        try {
+            inputPortDAO.verifyDelete(inputPortId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(inputPortId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyUpdateOutputPort(PortDTO outputPortDTO) {
-        // if connection does not exist, then the update request is likely 
creating it
-        // so we don't verify since it will fail
-        if (outputPortDAO.hasPort(outputPortDTO.getId())) {
-            outputPortDAO.verifyUpdate(outputPortDTO);
+        try {
+            // if connection does not exist, then the update request is likely 
creating it
+            // so we don't verify since it will fail
+            if (outputPortDAO.hasPort(outputPortDTO.getId())) {
+                outputPortDAO.verifyUpdate(outputPortDTO);
+            }
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(outputPortDTO.getId());
+            throw e;
         }
     }
 
     @Override
     public void verifyDeleteOutputPort(String outputPortId) {
-        outputPortDAO.verifyDelete(outputPortId);
+        try {
+            outputPortDAO.verifyDelete(outputPortId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(outputPortId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyUpdateProcessor(ProcessorDTO processorDTO) {
-        // if group does not exist, then the update request is likely creating 
it
-        // so we don't verify since it will fail
-        if (processorDAO.hasProcessor(processorDTO.getId())) {
-            processorDAO.verifyUpdate(processorDTO);
+        try {
+            // if group does not exist, then the update request is likely 
creating it
+            // so we don't verify since it will fail
+            if (processorDAO.hasProcessor(processorDTO.getId())) {
+                processorDAO.verifyUpdate(processorDTO);
+            }
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(processorDTO.getId());
+            throw e;
         }
     }
 
     @Override
     public void verifyDeleteProcessor(String processorId) {
-        processorDAO.verifyDelete(processorId);
+        try {
+            processorDAO.verifyDelete(processorId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(processorId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyUpdateProcessGroup(ProcessGroupDTO processGroupDTO) {
-        // if group does not exist, then the update request is likely creating 
it
-        // so we don't verify since it will fail
-        if (processGroupDAO.hasProcessGroup(processGroupDTO.getId())) {
-            processGroupDAO.verifyUpdate(processGroupDTO);
+        try {
+            // if group does not exist, then the update request is likely 
creating it
+            // so we don't verify since it will fail
+            if (processGroupDAO.hasProcessGroup(processGroupDTO.getId())) {
+                processGroupDAO.verifyUpdate(processGroupDTO);
+            }
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(processGroupDTO.getId());
+            throw e;
         }
     }
 
     @Override
     public void verifyDeleteProcessGroup(String groupId) {
-        processGroupDAO.verifyDelete(groupId);
+        try {
+            processGroupDAO.verifyDelete(groupId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(groupId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyUpdateRemoteProcessGroup(RemoteProcessGroupDTO 
remoteProcessGroupDTO) {
-        // if remote group does not exist, then the update request is likely 
creating it
-        // so we don't verify since it will fail
-        if 
(remoteProcessGroupDAO.hasRemoteProcessGroup(remoteProcessGroupDTO.getId())) {
-            remoteProcessGroupDAO.verifyUpdate(remoteProcessGroupDTO);
+        try {
+            // if remote group does not exist, then the update request is 
likely creating it
+            // so we don't verify since it will fail
+            if 
(remoteProcessGroupDAO.hasRemoteProcessGroup(remoteProcessGroupDTO.getId())) {
+                remoteProcessGroupDAO.verifyUpdate(remoteProcessGroupDTO);
+            }
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(remoteProcessGroupDTO.getId());
+            throw e;
         }
     }
 
     @Override
     public void verifyUpdateRemoteProcessGroupInputPort(String 
remoteProcessGroupId, RemoteProcessGroupPortDTO remoteProcessGroupPortDTO) {
-        remoteProcessGroupDAO.verifyUpdateInputPort(remoteProcessGroupId, 
remoteProcessGroupPortDTO);
+        try {
+            remoteProcessGroupDAO.verifyUpdateInputPort(remoteProcessGroupId, 
remoteProcessGroupPortDTO);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(remoteProcessGroupId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyUpdateRemoteProcessGroupOutputPort(String 
remoteProcessGroupId, RemoteProcessGroupPortDTO remoteProcessGroupPortDTO) {
-        remoteProcessGroupDAO.verifyUpdateOutputPort(remoteProcessGroupId, 
remoteProcessGroupPortDTO);
+        try {
+            remoteProcessGroupDAO.verifyUpdateOutputPort(remoteProcessGroupId, 
remoteProcessGroupPortDTO);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(remoteProcessGroupId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyDeleteRemoteProcessGroup(String remoteProcessGroupId) {
-        remoteProcessGroupDAO.verifyDelete(remoteProcessGroupId);
+        try {
+            remoteProcessGroupDAO.verifyDelete(remoteProcessGroupId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(remoteProcessGroupId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyUpdateControllerService(ControllerServiceDTO 
controllerServiceDTO) {
-        // if service does not exist, then the update request is likely 
creating it
-        // so we don't verify since it will fail
-        if 
(controllerServiceDAO.hasControllerService(controllerServiceDTO.getId())) {
-            controllerServiceDAO.verifyUpdate(controllerServiceDTO);
+        try {
+            // if service does not exist, then the update request is likely 
creating it
+            // so we don't verify since it will fail
+            if 
(controllerServiceDAO.hasControllerService(controllerServiceDTO.getId())) {
+                controllerServiceDAO.verifyUpdate(controllerServiceDTO);
+            }
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(controllerServiceDTO.getId());
+            throw e;
         }
     }
 
     @Override
     public void verifyUpdateControllerServiceReferencingComponents(String 
controllerServiceId, ScheduledState scheduledState, ControllerServiceState 
controllerServiceState) {
-        
controllerServiceDAO.verifyUpdateReferencingComponents(controllerServiceId, 
scheduledState, controllerServiceState);
+        try {
+            
controllerServiceDAO.verifyUpdateReferencingComponents(controllerServiceId, 
scheduledState, controllerServiceState);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(controllerServiceId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyDeleteControllerService(String controllerServiceId) {
-        controllerServiceDAO.verifyDelete(controllerServiceId);
+        try {
+            controllerServiceDAO.verifyDelete(controllerServiceId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(controllerServiceId);
+            throw e;
+        }
     }
 
     @Override
     public void verifyUpdateReportingTask(ReportingTaskDTO reportingTaskDTO) {
-        // if tasks does not exist, then the update request is likely creating 
it
-        // so we don't verify since it will fail
-        if (reportingTaskDAO.hasReportingTask(reportingTaskDTO.getId())) {
-            reportingTaskDAO.verifyUpdate(reportingTaskDTO);
+        try {
+            // if tasks does not exist, then the update request is likely 
creating it
+            // so we don't verify since it will fail
+            if (reportingTaskDAO.hasReportingTask(reportingTaskDTO.getId())) {
+                reportingTaskDAO.verifyUpdate(reportingTaskDTO);
+            }
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(reportingTaskDTO.getId());
+            throw e;
         }
     }
 
     @Override
     public void verifyDeleteReportingTask(String reportingTaskId) {
-        reportingTaskDAO.verifyDelete(reportingTaskId);
+        try {
+            reportingTaskDAO.verifyDelete(reportingTaskId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(reportingTaskId);
+            throw e;
+        }
     }
 
     // -----------------------------------------
@@ -515,10 +617,15 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
     @Override
     public void verifyUpdateSnippet(SnippetDTO snippetDto) {
-        // if snippet does not exist, then the update request is likely 
creating it
-        // so we don't verify since it will fail
-        if (snippetDAO.hasSnippet(snippetDto.getId())) {
-            snippetDAO.verifyUpdate(snippetDto);
+        try {
+            // if snippet does not exist, then the update request is likely 
creating it
+            // so we don't verify since it will fail
+            if (snippetDAO.hasSnippet(snippetDto.getId())) {
+                snippetDAO.verifyUpdate(snippetDto);
+            }
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(snippetDto.getId());
+            throw e;
         }
     }
 
@@ -843,7 +950,12 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
     @Override
     public void verifyCanClearProcessorState(final String processorId) {
-        processorDAO.verifyClearState(processorId);
+        try {
+            processorDAO.verifyClearState(processorId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(processorId);
+            throw e;
+        }
     }
 
     @Override
@@ -874,7 +986,12 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
     @Override
     public void verifyCanClearControllerServiceState(final String 
controllerServiceId) {
-        controllerServiceDAO.verifyClearState(controllerServiceId);
+        try {
+            controllerServiceDAO.verifyClearState(controllerServiceId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(controllerServiceId);
+            throw e;
+        }
     }
 
     @Override
@@ -884,7 +1001,12 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
     @Override
     public void verifyCanClearReportingTaskState(final String reportingTaskId) 
{
-        reportingTaskDAO.verifyClearState(reportingTaskId);
+        try {
+            reportingTaskDAO.verifyClearState(reportingTaskId);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(reportingTaskId);
+            throw e;
+        }
     }
 
     @Override
@@ -978,6 +1100,8 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
         return revisionManager.deleteRevision(claim, new 
DeleteRevisionTask<D>() {
             @Override
             public D performTask() {
+                logger.debug("Attempting to delete component {} with claim 
{}", authorizable, claim);
+
                 // ensure access to the component
                 authorizable.authorize(authorizer, RequestAction.WRITE);
 
@@ -985,6 +1109,7 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
                 // save the flow
                 controllerFacade.save();
+                logger.debug("Deletion of component {} was successful", 
authorizable);
 
                 return dto;
             }
@@ -993,7 +1118,12 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
     @Override
     public void verifyDeleteSnippet(String id) {
-        snippetDAO.verifyDelete(id);
+        try {
+            snippetDAO.verifyDelete(id);
+        } catch (final Exception e) {
+            revisionManager.cancelClaim(id);
+            throw e;
+        }
     }
 
     @Override
@@ -1394,7 +1524,7 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
     }
 
     @Override
-    public TemplateDTO createTemplate(String name, String description, String 
snippetId) {
+    public TemplateDTO createTemplate(String name, String description, String 
snippetId, String groupId) {
         // get the specified snippet
         Snippet snippet = snippetDAO.getSnippet(snippetId);
 
@@ -1409,27 +1539,31 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
         final ClusterContext clusterContext = 
ClusterContextThreadLocal.getContext();
         if (clusterContext != null) {
             
templateDTO.setId(UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString());
+        } else {
+            templateDTO.setId(UUID.randomUUID().toString());
         }
 
         // create the template
-        Template template = templateDAO.createTemplate(templateDTO);
+        Template template = templateDAO.createTemplate(templateDTO, groupId);
 
         return dtoFactory.createTemplateDTO(template);
     }
 
     @Override
-    public TemplateDTO importTemplate(TemplateDTO templateDTO) {
+    public TemplateDTO importTemplate(TemplateDTO templateDTO, String groupId) 
{
         // ensure id is set
         final ClusterContext clusterContext = 
ClusterContextThreadLocal.getContext();
         if (clusterContext != null) {
             
templateDTO.setId(UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString());
+        } else {
+            templateDTO.setId(UUID.randomUUID().toString());
         }
 
         // mark the timestamp
         templateDTO.setTimestamp(new Date());
 
         // import the template
-        final Template template = templateDAO.importTemplate(templateDTO);
+        final Template template = templateDAO.importTemplate(templateDTO, 
groupId);
 
         // return the template dto
         return dtoFactory.createTemplateDTO(template);

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
index c835254..955d0bb 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
@@ -96,6 +96,7 @@ import java.io.InputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
@@ -2187,7 +2188,8 @@ public class ProcessGroupResource extends 
ApplicationResource {
 
         // create the template and generate the json
         final RevisionDTO revisionDTO = 
createTemplateRequestEntity.getRevision();
-        final TemplateDTO template = 
serviceFacade.createTemplate(createTemplateRequestEntity.getName(), 
createTemplateRequestEntity.getDescription(), 
createTemplateRequestEntity.getSnippetId());
+        final TemplateDTO template = 
serviceFacade.createTemplate(createTemplateRequestEntity.getName(), 
createTemplateRequestEntity.getDescription(),
+            createTemplateRequestEntity.getSnippetId(), groupId);
         templateResource.populateRemainingTemplateContent(template);
 
         // create the revision
@@ -2320,7 +2322,7 @@ public class ProcessGroupResource extends 
ApplicationResource {
             }
 
             // import the template
-            final TemplateDTO template = 
serviceFacade.importTemplate(templateEntity.getTemplate());
+            final TemplateDTO template = 
serviceFacade.importTemplate(templateEntity.getTemplate(), groupId);
             templateResource.populateRemainingTemplateContent(template);
 
             // create the revision
@@ -2502,6 +2504,11 @@ public class ProcessGroupResource extends 
ApplicationResource {
         // replicate if cluster manager
         if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
             return clusterManager.applyRequest(HttpMethod.GET, 
getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        } else if (properties.isClusterManager()) {
+            // create the response entity
+            final ControllerServicesEntity entity = new 
ControllerServicesEntity();
+            entity.setControllerServices(Collections.emptySet());
+            return clusterContext(generateOkResponse(entity)).build();
         }
 
         // get all the controller services

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
index e0cc08b..6356a18 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
@@ -46,6 +46,8 @@ import org.apache.nifi.web.api.entity.ProcessorEntity;
 import org.apache.nifi.web.api.entity.PropertyDescriptorEntity;
 import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.nifi.web.api.request.LongParameter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
@@ -77,6 +79,7 @@ import java.util.Set;
     description = "Endpoint for managing a Processor."
 )
 public class ProcessorResource extends ApplicationResource {
+    private static final Logger logger = 
LoggerFactory.getLogger(ProcessorResource.class);
 
     private static final List<Long> POSSIBLE_RUN_DURATIONS = Arrays.asList(0L, 
25L, 50L, 100L, 250L, 500L, 1000L, 2000L);
 
@@ -481,16 +484,32 @@ public class ProcessorResource extends 
ApplicationResource {
         // handle expects request (usually from the cluster manager)
         final Revision revision = getRevision(processorEntity, id);
         final boolean validationPhase = isValidationPhase(httpServletRequest);
-        if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
+        final boolean twoPhaseRequest = isTwoPhaseRequest(httpServletRequest);
+        final String requestId = getHeaders().get("X-RequestTransactionId");
+
+        logger.debug("For Update Processor, Validation Phase = {}, Two-phase 
request = {}, Request ID = {}", validationPhase, twoPhaseRequest, requestId);
+        if (validationPhase || !twoPhaseRequest) {
             serviceFacade.claimRevision(revision);
+            logger.debug("Claimed Revision {}", revision);
         }
         if (validationPhase) {
             serviceFacade.verifyUpdateProcessor(requestProcessorDTO);
+            logger.debug("Verified Update of Processor");
             return generateContinueResponse().build();
         }
 
         // update the processor
-        final UpdateResult<ProcessorEntity> result = 
serviceFacade.updateProcessor(revision, requestProcessorDTO);
+        final UpdateResult<ProcessorEntity> result;
+        try {
+            logger.debug("Updating Processor with Revision {}", revision);
+            result = serviceFacade.updateProcessor(revision, 
requestProcessorDTO);
+            logger.debug("Updated Processor with Revision {}", revision);
+        } catch (final Exception e) {
+            final boolean tpr = isTwoPhaseRequest(httpServletRequest);
+            logger.error("Got Exception trying to update processor. two-phase 
request = {}, validation phase = {}, revision = {}", tpr, validationPhase, 
revision);
+            logger.error("", e);
+            throw e;
+        }
         final ProcessorEntity entity = result.getResult();
         populateRemainingProcessorEntityContent(entity);
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/IllegalArgumentExceptionMapper.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/IllegalArgumentExceptionMapper.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/IllegalArgumentExceptionMapper.java
index a152775..e9edd8f 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/IllegalArgumentExceptionMapper.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/IllegalArgumentExceptionMapper.java
@@ -35,10 +35,7 @@ public class IllegalArgumentExceptionMapper implements 
ExceptionMapper<IllegalAr
     public Response toResponse(IllegalArgumentException exception) {
         // log the error
         logger.info(String.format("%s. Returning %s response.", exception, 
Response.Status.BAD_REQUEST));
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(StringUtils.EMPTY, exception);
-        }
+        logger.debug(StringUtils.EMPTY, exception);
 
         return 
Response.status(Response.Status.BAD_REQUEST).entity(exception.getMessage()).type("text/plain").build();
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index 29858b9..82a79b9 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -780,7 +780,7 @@ public class ControllerFacade implements Authorizable {
         }
 
         // add each template
-        for (final Template template : flowController.getTemplates()) {
+        for (final Template template : root.findAllTemplates()) {
             final TemplateDTO details = template.getDetails();
             
resources.add(ResourceFactory.getComponentResource(ResourceType.Template, 
details.getId(), details.getName()));
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/270944ec/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/TemplateDAO.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/TemplateDAO.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/TemplateDAO.java
index 13fe66d..02e8f0a 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/TemplateDAO.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/TemplateDAO.java
@@ -28,17 +28,19 @@ public interface TemplateDAO {
      * Creates a template.
      *
      * @param templateDTO The template DTO
+     * @param groupId the ID of the group to add the template to
      * @return The template
      */
-    Template createTemplate(TemplateDTO templateDTO);
+    Template createTemplate(TemplateDTO templateDTO, String groupId);
 
     /**
      * Import the specified template.
      *
      * @param templateDTO dto
+     * @param groupId the ID of the group to add the template to
      * @return template
      */
-    Template importTemplate(TemplateDTO templateDTO);
+    Template importTemplate(TemplateDTO templateDTO, String groupId);
 
     /**
      * Instantiate the corresponding template.

Reply via email to