MINIFI-238 - MiNiFi Initial Command and Control Server Implementation

This closes #75.

Signed-off-by: Aldrin Piri <aldrin@apache.org>
diff --git a/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/PullHttpChangeIngestor.java b/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/PullHttpChangeIngestor.java
index a8e7105..3ec26d0 100644
--- a/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/PullHttpChangeIngestor.java
+++ b/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/PullHttpChangeIngestor.java
@@ -27,6 +27,7 @@
 import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeNotifier;
 import org.apache.nifi.minifi.bootstrap.configuration.differentiators.WholeConfigDifferentiator;
 import org.apache.nifi.minifi.bootstrap.configuration.differentiators.interfaces.Differentiator;
+import org.apache.nifi.minifi.commons.schema.common.StringUtil;
 import org.slf4j.LoggerFactory;
 
 import javax.net.ssl.KeyManagerFactory;
@@ -71,6 +72,7 @@
     public static final String PORT_KEY = PULL_HTTP_BASE_KEY + ".port";
     public static final String HOST_KEY = PULL_HTTP_BASE_KEY + ".hostname";
     public static final String PATH_KEY = PULL_HTTP_BASE_KEY + ".path";
+    public static final String QUERY_KEY = PULL_HTTP_BASE_KEY + ".query";
     public static final String TRUSTSTORE_LOCATION_KEY = PULL_HTTP_BASE_KEY + ".truststore.location";
     public static final String TRUSTSTORE_PASSWORD_KEY = PULL_HTTP_BASE_KEY + ".truststore.password";
     public static final String TRUSTSTORE_TYPE_KEY = PULL_HTTP_BASE_KEY + ".truststore.type";
@@ -86,6 +88,7 @@
     private final AtomicReference<Integer> portReference = new AtomicReference<>();
     private final AtomicReference<String> hostReference = new AtomicReference<>();
     private final AtomicReference<String> pathReference = new AtomicReference<>();
+    private final AtomicReference<String> queryReference = new AtomicReference<>();
     private volatile Differentiator<ByteBuffer> differentiator;
     private volatile String connectionScheme;
     private volatile String lastEtag = "";
@@ -110,6 +113,7 @@
         }
 
         final String path = properties.getProperty(PATH_KEY, "/");
+        final String query = properties.getProperty(QUERY_KEY, "");
 
         final String portString = (String) properties.get(PORT_KEY);
         final Integer port;
@@ -122,6 +126,7 @@
         portReference.set(port);
         hostReference.set(host);
         pathReference.set(path);
+        queryReference.set(query);
 
         final String useEtagString = (String) properties.getOrDefault(USE_ETAG_KEY, "false");
         if ("true".equalsIgnoreCase(useEtagString) || "false".equalsIgnoreCase(useEtagString)){
@@ -175,10 +180,15 @@
     public void run() {
         try {
             logger.debug("Attempting to pull new config");
-            final HttpUrl url = new HttpUrl.Builder()
+            HttpUrl.Builder builder = new HttpUrl.Builder()
                     .host(hostReference.get())
                     .port(portReference.get())
-                    .encodedPath(pathReference.get())
+                    .encodedPath(pathReference.get());
+            String query = queryReference.get();
+            if (!StringUtil.isNullOrEmpty(query)) {
+                builder = builder.encodedQuery(query);
+            }
+            final HttpUrl url = builder
                     .scheme(connectionScheme)
                     .build();
 
diff --git a/minifi-c2/minifi-c2-api/pom.xml b/minifi-c2/minifi-c2-api/pom.xml
new file mode 100644
index 0000000..4f1a397
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-api</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>jsr311-api</artifactId>
+            <version>1.1.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-util</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/Configuration.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/Configuration.java
new file mode 100644
index 0000000..a008f0a
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/Configuration.java
@@ -0,0 +1,45 @@
+/*
+ * 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.c2.api;
+
+import java.io.InputStream;
+
+/**
+ * Represents a MiNiFi configuration of a given version, format matches the format of the ConfigurationProvider
+ */
+public interface Configuration {
+    /**
+     * Gets the version
+     *
+     * @return the version
+     */
+    String getVersion();
+
+    /**
+     * Returns a boolean indicating whether this version exists
+     *
+     * @return a boolean indicating whether this version exists
+     */
+    boolean exists();
+    /**
+     * Returns an input stream to read the configuration with
+     *
+     * @return an input stream to read the configuration with
+     */
+    InputStream getInputStream() throws ConfigurationProviderException;
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/ConfigurationProvider.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/ConfigurationProvider.java
new file mode 100644
index 0000000..a743be3
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/ConfigurationProvider.java
@@ -0,0 +1,43 @@
+/*
+ * 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.c2.api;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A configuration provider is capable of taking a parameter map and returning a configuration with a given content type
+ */
+public interface ConfigurationProvider {
+    /**
+     * Gets the content type that this provider returns
+     *
+     * @return the content type that this provider returns
+     */
+    String getContentType();
+
+    /**
+     * Gets the configuration that corresponds to the passed in parameters
+     *
+     * @param version the version of the configuration to get
+     * @param parameters the parameters passed in by the client (please note that these are provided by a client and should NOT be trusted to be sanitized)
+     * @return an input stream of the configuration
+     * @throws ConfigurationProviderException if there is an error in the configuration
+     */
+    Configuration getConfiguration(Integer version, Map<String, List<String>> parameters) throws ConfigurationProviderException;
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/ConfigurationProviderException.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/ConfigurationProviderException.java
new file mode 100644
index 0000000..16b1ed2
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/ConfigurationProviderException.java
@@ -0,0 +1,28 @@
+/*
+ * 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.c2.api;
+
+public class ConfigurationProviderException extends Exception {
+    public ConfigurationProviderException(String message) {
+        super(message);
+    }
+
+    public ConfigurationProviderException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/InvalidParameterException.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/InvalidParameterException.java
new file mode 100644
index 0000000..33f8aa0
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/InvalidParameterException.java
@@ -0,0 +1,28 @@
+/*
+ * 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.c2.api;
+
+public class InvalidParameterException extends ConfigurationProviderException {
+    public InvalidParameterException(String message) {
+        super(message);
+    }
+
+    public InvalidParameterException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/cache/ConfigurationCache.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/cache/ConfigurationCache.java
new file mode 100644
index 0000000..00c271e
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/cache/ConfigurationCache.java
@@ -0,0 +1,37 @@
+/*
+ * 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.c2.api.cache;
+
+import org.apache.nifi.minifi.c2.api.InvalidParameterException;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Cache for storing configurations so they don't have to be pulled from the provider more often than necessary
+ */
+public interface ConfigurationCache {
+    /**
+     * Returns the information on a given cache entry
+     *
+     * @param parameters the parameters that identify the entry
+     * @return information on the entry
+     * @throws InvalidParameterException if there are illegal/invalid parameters
+     */
+    ConfigurationCacheFileInfo getCacheFileInfo(Map<String, List<String>> parameters) throws InvalidParameterException;
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/cache/ConfigurationCacheFileInfo.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/cache/ConfigurationCacheFileInfo.java
new file mode 100644
index 0000000..73578ba
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/cache/ConfigurationCacheFileInfo.java
@@ -0,0 +1,53 @@
+/*
+ * 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.c2.api.cache;
+
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+
+import java.io.IOException;
+import java.util.stream.Stream;
+
+/**
+ * Information on the different versions of a cache entry
+ */
+public interface ConfigurationCacheFileInfo {
+    /**
+     * Returns the version the file would be assigned based on its name
+     *
+     * @param filename the filename
+     * @return the version
+     */
+    Integer getVersionIfMatch(String filename);
+
+    /**
+     * Returns a stream of WritableConfigurations for this cache entry
+     *
+     * @return the stream
+     * @throws IOException if there is an error getting the configurations
+     */
+    Stream<WriteableConfiguration> getCachedConfigurations() throws IOException;
+
+    /**
+     * Returns a WritableConfiguration for the given version of this cache entry
+     *
+     * @param version the version
+     * @return the configuration
+     * @throws ConfigurationProviderException if there is a problem getting the configuration
+     */
+    WriteableConfiguration getConfiguration(Integer version) throws ConfigurationProviderException;
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/cache/WriteableConfiguration.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/cache/WriteableConfiguration.java
new file mode 100644
index 0000000..20d2d2e
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/cache/WriteableConfiguration.java
@@ -0,0 +1,29 @@
+/*
+ * 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.c2.api.cache;
+
+import org.apache.nifi.minifi.c2.api.Configuration;
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+
+import java.io.OutputStream;
+
+public interface WriteableConfiguration extends Configuration {
+    OutputStream getOutputStream() throws ConfigurationProviderException;
+
+    String getName();
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/properties/C2Properties.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/properties/C2Properties.java
new file mode 100644
index 0000000..2a8df3a
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/properties/C2Properties.java
@@ -0,0 +1,89 @@
+/*
+ * 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.c2.api.properties;
+
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.util.Properties;
+
+public class C2Properties extends Properties {
+    public static final String MINIFI_C2_SERVER_SECURE = "minifi.c2.server.secure";
+    public static final String MINIFI_C2_SERVER_KEYSTORE_TYPE = "minifi.c2.server.keystoreType";
+    public static final String MINIFI_C2_SERVER_KEYSTORE = "minifi.c2.server.keystore";
+    public static final String MINIFI_C2_SERVER_KEYSTORE_PASSWD = "minifi.c2.server.keystorePasswd";
+    public static final String MINIFI_C2_SERVER_KEY_PASSWD = "minifi.c2.server.keyPasswd";
+    public static final String MINIFI_C2_SERVER_TRUSTSTORE = "minifi.c2.server.truststore";
+    public static final String MINIFI_C2_SERVER_TRUSTSTORE_TYPE = "minifi.c2.server.truststoreType";
+    public static final String MINIFI_C2_SERVER_TRUSTSTORE_PASSWD = "minifi.c2.server.truststorePasswd";
+
+    private static final Logger logger = LoggerFactory.getLogger(C2Properties.class);
+    private static final C2Properties properties = initProperties();
+    private static final String C2_SERVER_HOME = System.getenv("C2_SERVER_HOME");
+
+    private static C2Properties initProperties() {
+        C2Properties properties = new C2Properties();
+        try (InputStream inputStream = C2Properties.class.getClassLoader().getResourceAsStream("c2.properties")) {
+            properties.load(inputStream);
+        } catch (IOException e) {
+            throw new RuntimeException("Unable to load c2.properties", e);
+        }
+        return properties;
+    }
+
+    public static C2Properties getInstance() {
+        return properties;
+    }
+
+    public SslContextFactory getSslContextFactory() throws GeneralSecurityException, IOException {
+        if (!Boolean.valueOf(getProperty(MINIFI_C2_SERVER_SECURE, "false"))) {
+            return null;
+        }
+
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        KeyStore keyStore = KeyStore.getInstance(properties.getProperty(MINIFI_C2_SERVER_KEYSTORE_TYPE));
+        Path keyStorePath = Paths.get(C2_SERVER_HOME).resolve(properties.getProperty(MINIFI_C2_SERVER_KEYSTORE)).toAbsolutePath();
+        logger.debug("keystore path: " + keyStorePath);
+        try (InputStream inputStream = Files.newInputStream(keyStorePath)) {
+            keyStore.load(inputStream, properties.getProperty(MINIFI_C2_SERVER_KEYSTORE_PASSWD).toCharArray());
+        }
+        sslContextFactory.setKeyStore(keyStore);
+        sslContextFactory.setKeyManagerPassword(properties.getProperty(MINIFI_C2_SERVER_KEY_PASSWD));
+        sslContextFactory.setWantClientAuth(true);
+
+        String trustStorePath = Paths.get(C2_SERVER_HOME).resolve(properties.getProperty(MINIFI_C2_SERVER_TRUSTSTORE)).toAbsolutePath().toFile().getAbsolutePath();
+        logger.debug("truststore path: " + trustStorePath);
+        sslContextFactory.setTrustStorePath(trustStorePath);
+        sslContextFactory.setTrustStoreType(properties.getProperty(MINIFI_C2_SERVER_TRUSTSTORE_TYPE));
+        sslContextFactory.setTrustStorePassword(properties.getProperty(MINIFI_C2_SERVER_TRUSTSTORE_PASSWD));
+        try {
+            sslContextFactory.start();
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+        return sslContextFactory;
+    }
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/security/authorization/AuthorityGranter.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/security/authorization/AuthorityGranter.java
new file mode 100644
index 0000000..2a7d913
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/security/authorization/AuthorityGranter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.c2.api.security.authorization;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Collection;
+
+/**
+ * Interface responsible for taking an authentication object and returning the GrantedAuthorities it should have
+ */
+public interface AuthorityGranter {
+    /**
+     * Returns the authorities a given Authentication object should receive
+     *
+     * @param authentication the authentication
+     * @return the authorities it should receive
+     */
+    Collection<GrantedAuthority> grantAuthorities(Authentication authentication);
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/security/authorization/AuthorizationException.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/security/authorization/AuthorizationException.java
new file mode 100644
index 0000000..2c459e0
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/security/authorization/AuthorizationException.java
@@ -0,0 +1,24 @@
+/*
+ * 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.c2.api.security.authorization;
+
+public class AuthorizationException extends Exception {
+    public AuthorizationException(String message) {
+        super(message);
+    }
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/security/authorization/Authorizer.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/security/authorization/Authorizer.java
new file mode 100644
index 0000000..fbdf3f5
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/security/authorization/Authorizer.java
@@ -0,0 +1,36 @@
+/*
+ * 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.c2.api.security.authorization;
+
+import org.springframework.security.core.Authentication;
+
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Interface responsible for authorizing a given authentication to access a given uri
+ */
+public interface Authorizer {
+    /**
+     * Throws an AuthorizationException if the authentication should not access the given uri
+     *
+     * @param authentication the authentication
+     * @param uriInfo the uri
+     * @throws AuthorizationException if the authentication should not access the uri
+     */
+    void authorize(Authentication authentication, UriInfo uriInfo) throws AuthorizationException;
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/util/DelegatingOutputStream.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/util/DelegatingOutputStream.java
new file mode 100644
index 0000000..a3f5254
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/util/DelegatingOutputStream.java
@@ -0,0 +1,54 @@
+/*
+ * 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.c2.api.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class DelegatingOutputStream extends OutputStream {
+    private final OutputStream delegate;
+
+    public DelegatingOutputStream(OutputStream delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        delegate.write(b);
+    }
+
+    @Override
+    public void write(byte[] b) throws IOException {
+        delegate.write(b);
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        delegate.write(b, off, len);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        delegate.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+}
diff --git a/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/util/Pair.java b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/util/Pair.java
new file mode 100644
index 0000000..edca7d0
--- /dev/null
+++ b/minifi-c2/minifi-c2-api/src/main/java/org/apache/nifi/minifi/c2/api/util/Pair.java
@@ -0,0 +1,44 @@
+/*
+ * 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.c2.api.util;
+
+public class Pair<T, U> {
+    private final T first;
+    private final U second;
+
+    public Pair(T first, U second) {
+        this.first = first;
+        this.second = second;
+    }
+
+    public T getFirst() {
+        return first;
+    }
+
+    public U getSecond() {
+        return second;
+    }
+
+    @Override
+    public String toString() {
+        return "Pair{" +
+                "first=" + first +
+                ", second=" + second +
+                '}';
+    }
+}
diff --git a/minifi-c2/minifi-c2-assembly/LICENSE b/minifi-c2/minifi-c2-assembly/LICENSE
new file mode 100644
index 0000000..a4b4e87
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/LICENSE
@@ -0,0 +1,330 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
+
+
+APACHE MINIFI SUBCOMPONENTS:
+
+The Apache MiNiFi project contains subcomponents with separate copyright
+notices and license terms. Your use of the source code for the these
+subcomponents is subject to the terms and conditions of the following
+licenses.
+
+The binary distribution of this product bundles 'Slf4j' which is available
+under a "3-clause BSD" license.  For details see http://www.slf4j.org/
+
+    Copyright (c) 2004-2013 QOS.ch
+     All rights reserved.
+
+     Permission is hereby granted, free  of charge, to any person obtaining
+     a  copy  of this  software  and  associated  documentation files  (the
+     "Software"), to  deal in  the Software without  restriction, including
+     without limitation  the rights to  use, copy, modify,  merge, publish,
+     distribute,  sublicense, and/or sell  copies of  the Software,  and to
+     permit persons to whom the Software  is furnished to do so, subject to
+     the following conditions:
+
+     The  above  copyright  notice  and  this permission  notice  shall  be
+     included in all copies or substantial portions of the Software.
+
+     THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
+     EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
+     MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
+     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+     LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+     OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
+     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+The binary distribution of this product bundles 'Antlr 3' which is available
+under a "3-clause BSD" license.  For details see http://www.antlr3.org/license.html
+
+    Copyright (c) 2010 Terence Parr
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+    Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+    Neither the name of the author nor the names of its contributors may be used
+    to endorse or promote products derived from this software without specific
+    prior written permission.
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+    THE POSSIBILITY OF SUCH DAMAGE.
+
+This product bundles 'asm' which is available under a 3-Clause BSD style license.
+For details see http://asm.ow2.org/asmdex-license.html
+
+    Copyright (c) 2012 France Télécom
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+    1. Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the names of its
+       contributors may be used to endorse or promote products derived from
+       this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+    THE POSSIBILITY OF SUCH DAMAGE.
+
+The binary distribution of this product bundles 'JLine' under a BSD
+style license.
+
+    Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or
+    without modification, are permitted provided that the following
+    conditions are met:
+
+    Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer
+    in the documentation and/or other materials provided with
+    the distribution.
+
+    Neither the name of JLine nor the names of its contributors
+    may be used to endorse or promote products derived from this
+    software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+    BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+    AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+    EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+    OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-assembly/NOTICE b/minifi-c2/minifi-c2-assembly/NOTICE
new file mode 100644
index 0000000..ebdfa69
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/NOTICE
@@ -0,0 +1,240 @@
+Apache NiFi
+Copyright 2014-2017 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+===========================================
+Apache Software License v2
+===========================================
+
+The following binary components are provided under the Apache Software License v2
+
+  (ASLv2) Apache NiFi
+    The following NOTICE information applies:
+        Apache NiFi
+        Copyright 2014-2017 The Apache Software Foundation
+
+        This product includes software developed at
+        The Apache Software Foundation (http://www.apache.org/).
+
+        This product includes the following work from the Apache Hadoop project:
+
+        BoundedByteArrayOutputStream.java adapted to SoftLimitBoundedByteArrayOutputStream.java
+
+        This includes derived works from the Apache Software License V2 library python-evtx (https://github.com/williballenthin/python-evtx)
+        Copyright 2012, 2013 Willi Ballenthin william.ballenthin@mandiant.com
+        while at Mandiant http://www.mandiant.com
+        The derived work is adapted from Evtx/Evtx.py, Evtx/BinaryParser.py, Evtx/Nodes.py, Evtx/Views.py and can be found in the org.apache.nifi.processors.evtx.parser package.
+
+
+
+        This includes derived works from the Apache Storm (ASLv2 licensed) project (https://github.com/apache/storm):
+        Copyright 2015 The Apache Software Foundation
+        The derived work is adapted from
+          org/apache/storm/hive/common/HiveWriter.java
+          org/apache/storm/hive/common/HiveOptions.java
+        and can be found in the org.apache.nifi.util.hive package
+
+        This includes derived works from the Apache Hive (ASLv2 licensed) project (https://github.com/apache/hive):
+        Copyright 2008-2017 The Apache Software Foundation
+        The derived work is adapted from
+          release-1.2.1/ql/src/java/org/apache/hadoop/hive/ql/io/orc/WriterImpl.java
+        and can be found in the org.apache.hadoop.hive.ql.io.orc package
+
+  (ASLv2) Apache Commons Lang
+    The following NOTICE information applies:
+      Apache Commons Lang
+      Copyright 2001-2015 The Apache Software Foundation
+
+      This product includes software from the Spring Framework,
+      under the Apache License 2.0 (see: StringUtils.containsWhitespace())
+
+  (ASLv2) Apache Commons Codec
+    The following NOTICE information applies:
+      Apache Commons Codec
+      Copyright 2002-2014 The Apache Software Foundation
+
+      src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java
+      contains test data from http://aspell.net/test/orig/batch0.tab.
+      Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org)
+
+      ===============================================================================
+
+      The content of package org.apache.commons.codec.language.bm has been translated
+      from the original php source code available at http://stevemorse.org/phoneticinfo.htm
+      with permission from the original authors.
+      Original source copyright:
+      Copyright (c) 2008 Alexander Beider & Stephen P. Morse.
+
+   (ASLv2) JsonPath
+     The following NOTICE information applies:
+       Copyright 2011 JsonPath authors
+
+  (ASLv2) JSON-SMART
+    The following NOTICE information applies:
+      Copyright 2011 JSON-SMART authors
+
+  (ASLv2) Jackson JSON processor
+    The following NOTICE information applies:
+      # Jackson JSON processor
+
+      Jackson is a high-performance, Free/Open Source JSON processing library.
+      It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has
+      been in development since 2007.
+      It is currently developed by a community of developers, as well as supported
+      commercially by FasterXML.com.
+
+      ## Licensing
+
+      Jackson core and extension components may licensed under different licenses.
+      To find the details that apply to this artifact see the accompanying LICENSE file.
+      For more information, including possible other licensing options, contact
+      FasterXML.com (http://fasterxml.com).
+
+      ## Credits
+
+      A list of contributors may be found from CREDITS file, which is included
+      in some artifacts (usually source distributions); but is always available
+      from the source code management (SCM) system project uses.
+
+  (ASLv2) Apache Commons Collections
+    The following NOTICE information applies:
+      Apache Commons Collections
+      Copyright 2001-2013 The Apache Software Foundation
+
+  (ASLv2) Jettison
+    The following NOTICE information applies:
+         Copyright 2006 Envoi Solutions LLC
+
+  (ASLv2) Apache HttpComponents
+    The following NOTICE information applies:
+      Apache HttpClient
+      Copyright 1999-2015 The Apache Software Foundation
+
+      Apache HttpCore
+      Copyright 2005-2015 The Apache Software Foundation
+
+      Apache HttpMime
+      Copyright 1999-2013 The Apache Software Foundation
+
+      This project contains annotations derived from JCIP-ANNOTATIONS
+      Copyright (c) 2005 Brian Goetz and Tim Peierls. See http://www.jcip.net
+
+  (ASLv2) Apache Commons Logging
+    The following NOTICE information applies:
+      Apache Commons Logging
+      Copyright 2003-2014 The Apache Software Foundation
+
+  (ASLv2) Apache Commons Net
+    The following NOTICE information applies:
+      Apache Commons Net
+      Copyright 2001-2013 The Apache Software Foundation
+
+  (ASLv2) Swagger Core
+    The following NOTICE information applies:
+      Swagger Core 1.5.3-M1
+      Copyright 2015 Reverb Technologies, Inc.
+
+  (ASLv2) Spring Security
+    The following NOTICE information applies:
+          Spring Framework 4.0.3.RELEASE
+          Copyright (c) 2002-2015 Pivotal, Inc.
+
+  (ASLv2) Spring Framework
+    The following NOTICE information applies:
+      Spring Framework 4.1.4.RELEASE
+      Copyright (c) 2002-2015 Pivotal, Inc.
+
+    (ASLv2) Quartz
+      The following NOTICE information applies:
+        Copyright Declaration:
+        Copyright © 2003-2016 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors.
+
+        Trademark and Patent declaration
+        The name Software AG and all Software AG product names are either trademarks or registered trademarks of Software AG and/or Software AG USA Inc. and/or its subsidiaries and/or its affiliates
+        and/or their licensors. Other company and product names mentioned herein may be trademarks of their respective owners.
+
+        Detailed information on trademarks and patents owned by Software AG and/or its subsidiaries is located at http://softwareag.com/licenses.
+
+        Third Party declaration
+        This software may include portions of third-party products. For third-party copyright notices, license terms, additional rights or restrictions, please refer to "License Texts, Copyright
+        Notices and Disclaimers of Third Party Products". For certain specific third-party license restrictions, please refer to section E of the Legal Notices available under "License Terms and
+        Conditions for Use of Software AG Products / Copyright and Trademark Notices of Software AG Products". These documents are part of the product documentation, located at
+        http://softwareag.com/licenses and/or in the root installation directory of the licensed product(s).
+
+        Confidentiality Disclaimer:
+        Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG.
+        Contact GitHub API Training Shop Blog About
+
+  (ASLv2) Jasypt
+    The following NOTICE information applies:
+	  Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org)
+
+  (ASLv2) Apache Commons IO
+    The following NOTICE information applies:
+      Apache Commons IO
+      Copyright 2002-2017 The Apache Software Foundation
+
+  (ASLv2) Apache ZooKeeper
+    The following NOTICE information applies:
+      Apache ZooKeeper
+      Copyright 2009-2012 The Apache Software Foundation
+
+  (ASLv2) Apache log4j
+    The following NOTICE information applies:
+      Apache log4j
+      Copyright 2007 The Apache Software Foundation
+
+  (ASLv2) The Netty Project
+    The following NOTICE information applies:
+      The Netty Project
+      Copyright 2011 The Netty Project
+
+  (ASLv2) Apache Curator
+    The following NOTICE information applies:
+      Curator Framework
+      Copyright 2011-2014 The Apache Software Foundation
+
+      Curator Client
+      Copyright 2011-2014 The Apache Software Foundation
+
+      Curator Recipes
+      Copyright 2011-2014 The Apache Software Foundation
+
+************************
+Common Development and Distribution License 1.1
+************************
+
+The following binary components are provided under the Common Development and Distribution License 1.1. See project link for details.
+
+    (CDDL 1.1) (GPL2 w/ CPE) JavaMail API (compat) (javax.mail:mail:jar:1.4.7 - http://kenai.com/projects/javamail/mail)
+    (CDDL 1.1) (GPL2 w/ CPE) jersey-json (com.sun.jersey:jersey-json:jar:1.19 - https://jersey.java.net/jersey-json/)
+    (CDDL 1.1) (GPL2 w/ CPE) Old JAXB Runtime (com.sun.xml.bind:jaxb-impl:jar:2.2.3-1 - http://jaxb.java.net/)
+    (CDDL 1.1) (GPL2 w/ CPE) Java Architecture For XML Binding (javax.xml.bind:jaxb-api:jar:2.2.2 - https://jaxb.dev.java.net/)
+    (CDDL 1.1) (GPL2 w/ CPE) aopalliance version 1.0 repackaged as a module (org.glassfish.hk2.external:aopalliance-repackaged:jar:2.4.0-b25 - https://hk2.java.net/external/aopalliance-repackaged)
+    (CDDL 1.1) (GPL2 w/ CPE) jersey-client (com.sun.jersey:jersey-client:jar:1.19 - https://jersey.java.net/jersey-client/)
+    (CDDL 1.1) (GPL2 w/ CPE) jersey-core (com.sun.jersey:jersey-core:jar:1.19 - https://jersey.java.net/jersey-core/)
+
+
+************************
+Common Development and Distribution License 1.0
+************************
+
+The following binary components are provided under the Common Development and Distribution License 1.0.  See project link for details.
+
+    (CDDL 1.0) JavaBeans Activation Framework (JAF) (javax.activation:activation:jar:1.1 - http://java.sun.com/products/javabeans/jaf/index.jsp)
+    (CDDL 1.0) (GPL3) Streaming API For XML (javax.xml.stream:stax-api:jar:1.0-2 - no url provided)
+    (CDDL 1.0) JSR311 API (javax.ws.rs:jsr311-api:jar:1.1.1 - https://jsr311.dev.java.net)
+
+
+************************
+Eclipse Public License 1.0
+************************
+
+The following binary components are provided under the Eclipse Public License 1.0.  See project link for details.
+
+    (EPL 1.0) AspectJ Weaver (org.aspectj:aspectjweaver:jar:1.8.5 - http://www.eclipse.org/aspectj/)
+    (EPL 1.0)(LGPL 2.1) Logback Classic (ch.qos.logback:logback-classic:jar:1.1.3 - http://logback.qos.ch/)
+    (EPL 1.0)(LGPL 2.1) Logback Core (ch.qos.logback:logback-core:jar:1.1.3 - http://logback.qos.ch/)
+    (EPL 1.0)(MPL 2.0) H2 Database (com.h2database:h2:jar:1.3.176 - http://www.h2database.com/html/license.html)
diff --git a/minifi-c2/minifi-c2-assembly/README.md b/minifi-c2/minifi-c2-assembly/README.md
new file mode 100644
index 0000000..c77e3e2
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/README.md
@@ -0,0 +1,97 @@
+<!--
+  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
+      https://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.
+-->
+# Apache NiFi - MiNiFi - Command and Control (C2) Server
+
+MiNiFi is a child project effort of Apache NiFi.  The MiNiFi C2 Server aids in transforming and distributing MiNiFi configuration files to MiNiFi instances over HTTP.
+
+## Table of Contents
+
+- [Requirements](#requirements)
+- [Getting Started](#getting-started)
+- [Getting Help](#getting-help)
+- [Documentation](#documentation)
+- [License](#license)
+- [Export Control](#export-control)
+
+## Requirements
+* JRE 1.8
+* Apache Maven 3
+
+## Getting Started
+
+The C2 server is built either as part of the top level MiNiFi build or as a build of the minifi-c2 module.
+
+```
+mvn clean install
+```
+
+It can be started with either c2.sh or c2.bat depending on platform.
+
+## Getting Help
+If you have questions, you can reach out to our mailing list: dev@nifi.apache.org
+([archive](https://mail-archives.apache.org/mod_mbox/nifi-dev)).
+We're also often available in IRC: #nifi on
+[irc.freenode.net](https://webchat.freenode.net/?channels=#nifi).
+
+## Documentation
+
+See https://nifi.apache.org/minifi and https://cwiki.apache.org/confluence/display/NIFI/MiNiFi for the latest documentation.
+
+## License
+
+Except as otherwise noted this software is licensed under the
+[Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.html)
+
+Licensed 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
+
+  https://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.
+
+## Export Control
+
+This distribution includes cryptographic software. The country in which you
+currently reside may have restrictions on the import, possession, use, and/or
+re-export to another country, of encryption software. BEFORE using any
+encryption software, please check your country's laws, regulations and
+policies concerning the import, possession, or use, and re-export of encryption
+software, to see if this is permitted. See <http://www.wassenaar.org/> for more
+information.
+
+The U.S. Government Department of Commerce, Bureau of Industry and Security
+(BIS), has classified this software as Export Commodity Control Number (ECCN)
+5D002.C.1, which includes information security software using or performing
+cryptographic functions with asymmetric algorithms. The form and manner of this
+Apache Software Foundation distribution makes it eligible for export under the
+License Exception ENC Technology Software Unrestricted (TSU) exception (see the
+BIS Export Administration Regulations, Section 740.13) for both object code and
+source code.
+
+The following provides more details on the included cryptographic software:
+
+Apache NiFi - MiNiFi uses BouncyCastle, Jasypt, JCraft Inc., and the built-in
+java cryptography libraries for SSL, SSH, and the protection
+of sensitive configuration parameters. See
+https://bouncycastle.org/about.html
+http://www.jasypt.org/faq.html
+http://jcraft.com/c-info.html
+https://www.oracle.com/us/products/export/export-regulations-345813.html
+for more details on each of these libraries cryptography features.
diff --git a/minifi-c2/minifi-c2-assembly/pom.xml b/minifi-c2/minifi-c2-assembly/pom.xml
new file mode 100644
index 0000000..957f8bd
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/pom.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-assembly</artifactId>
+    <packaging>pom</packaging>
+    <description>This is the assembly of Apache MiNiFi's - Command And Control Server</description>
+    <properties>
+        <minifi.c2.server.port>10080</minifi.c2.server.port>
+
+        <minifi.c2.server.secure>false</minifi.c2.server.secure>
+        <minifi.c2.server.keystore>./conf/keystore.jks</minifi.c2.server.keystore>
+        <minifi.c2.server.keystoreType>jks</minifi.c2.server.keystoreType>
+        <minifi.c2.server.keystorePasswd/>
+        <minifi.c2.server.keyPasswd/>
+        <minifi.c2.server.truststore>./conf/truststore.jks</minifi.c2.server.truststore>
+        <minifi.c2.server.truststoreType>jks</minifi.c2.server.truststoreType>
+        <minifi.c2.server.truststorePasswd/>
+
+        <minifi.c2.server.provider.nifi.rest.api.url>http://localhost:8080/nifi-api</minifi.c2.server.provider.nifi.rest.api.url>
+    </properties>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <finalName>minifi-c2-${project.version}</finalName>
+                    <attach>false</attach>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make shared resource</id>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <archiverConfig>
+                                <defaultDirectoryMode>0755</defaultDirectoryMode>
+                                <directoryMode>0755</directoryMode>
+                                <fileMode>0644</fileMode>
+                            </archiverConfig>
+                            <descriptors>
+                                <descriptor>src/main/assembly/dependencies.xml</descriptor>
+                            </descriptors>
+                            <tarLongFileMode>posix</tarLongFileMode>
+                            <escapeString>\</escapeString>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-cache-filesystem</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-provider-cache</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-provider-nifi-rest</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-jetty</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlet</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-config</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-web</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey.contribs</groupId>
+            <artifactId>jersey-spring</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-util</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-service</artifactId>
+            <version>${project.version}</version>
+            <type>war</type>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/minifi-c2/minifi-c2-assembly/src/main/assembly/dependencies.xml b/minifi-c2/minifi-c2-assembly/src/main/assembly/dependencies.xml
new file mode 100644
index 0000000..026200c
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/assembly/dependencies.xml
@@ -0,0 +1,80 @@
+<?xml version="1.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.
+-->
+<assembly>
+    <id>bin</id>
+    <formats>
+        <format>dir</format>
+        <format>zip</format>
+        <format>tar.gz</format>
+    </formats>
+    <includeBaseDirectory>true</includeBaseDirectory>
+    <baseDirectory>minifi-c2-${project.version}</baseDirectory>
+
+    <dependencySets>
+        <!-- Write out all non-war dependency artifacts to lib directory -->
+        <dependencySet>
+            <outputDirectory>lib</outputDirectory>
+            <useProjectArtifact>false</useProjectArtifact>
+            <directoryMode>0770</directoryMode>
+            <fileMode>0660</fileMode>
+            <excludes>
+                <exclude>*:war</exclude>
+            </excludes>
+        </dependencySet>
+
+        <!-- Write out all war dependency artifacts to lib directory -->
+        <dependencySet>
+            <outputDirectory>webapps</outputDirectory>
+            <useProjectArtifact>false</useProjectArtifact>
+            <directoryMode>0770</directoryMode>
+            <fileMode>0660</fileMode>
+            <includes>
+                <include>*:war</include>
+            </includes>
+        </dependencySet>
+    </dependencySets>
+    <fileSets>
+        <fileSet>
+            <directory>${project.basedir}/src/main/resources</directory>
+            <outputDirectory>/</outputDirectory>
+            <fileMode>0755</fileMode>
+            <filtered>true</filtered>
+        </fileSet>
+    </fileSets>
+    <files>
+        <file>
+            <source>./README.md</source>
+            <outputDirectory>./</outputDirectory>
+            <destName>README</destName>
+            <fileMode>0644</fileMode>
+            <filtered>true</filtered>
+        </file>
+        <file>
+            <source>./LICENSE</source>
+            <outputDirectory>./</outputDirectory>
+            <destName>LICENSE</destName>
+            <fileMode>0644</fileMode>
+            <filtered>true</filtered>
+        </file>
+        <file>
+            <source>./NOTICE</source>
+            <outputDirectory>./</outputDirectory>
+            <destName>NOTICE</destName>
+            <fileMode>0644</fileMode>
+            <filtered>true</filtered>
+        </file>
+    </files>
+</assembly>
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/bin/c2.bat b/minifi-c2/minifi-c2-assembly/src/main/resources/bin/c2.bat
new file mode 100644
index 0000000..6c02746
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/bin/c2.bat
@@ -0,0 +1,41 @@
+@echo off
+rem
+rem    Licensed to the Apache Software Foundation (ASF) under one or more
+rem    contributor license agreements.  See the NOTICE file distributed with
+rem    this work for additional information regarding copyright ownership.
+rem    The ASF licenses this file to You under the Apache License, Version 2.0
+rem    (the "License"); you may not use this file except in compliance with
+rem    the License.  You may obtain a copy of the License at
+rem
+rem       http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem    Unless required by applicable law or agreed to in writing, software
+rem    distributed under the License is distributed on an "AS IS" BASIS,
+rem    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem    See the License for the specific language governing permissions and
+rem    limitations under the License.
+rem
+
+rem Use JAVA_HOME if it's set; otherwise, just use java
+
+if "%JAVA_HOME%" == "" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+set JAVA_EXE=%JAVA_HOME%\bin\java.exe
+goto startConfig
+
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly.
+echo Instead the PATH will be used to find the java executable.
+echo.
+set JAVA_EXE=java
+goto startConfig
+
+:startConfig
+set LIB_DIR=%~dp0..\conf;%~dp0..\lib
+
+set C2_SERVER_HOME=%~sdp0..\
+
+SET JAVA_PARAMS=-cp %LIB_DIR%\* -Xms12m -Xmx24m %JAVA_ARGS% -Djava.net.preferIPv4Stack=true org.apache.nifi.minifi.c2.jetty.JettyServer
+
+cmd.exe /C "%JAVA_EXE%" %JAVA_PARAMS% %*
+
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/bin/c2.sh b/minifi-c2/minifi-c2-assembly/src/main/resources/bin/c2.sh
new file mode 100755
index 0000000..c6dc8de
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/bin/c2.sh
@@ -0,0 +1,125 @@
+#!/bin/sh
+#
+#    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.
+#
+#
+
+# Script structure inspired from Apache Karaf and other Apache projects with similar startup approaches
+
+SCRIPT_DIR=$(dirname "$0")
+SCRIPT_NAME=$(basename "$0")
+export C2_SERVER_HOME=$(cd "${SCRIPT_DIR}" && cd .. && pwd)
+PROGNAME=$(basename "$0")
+
+
+warn() {
+    echo "${PROGNAME}: $*"
+}
+
+die() {
+    warn "$*"
+    exit 1
+}
+
+detectOS() {
+    # OS specific support (must be 'true' or 'false').
+    cygwin=false;
+    aix=false;
+    os400=false;
+    darwin=false;
+    case "$(uname)" in
+        CYGWIN*)
+            cygwin=true
+            ;;
+        AIX*)
+            aix=true
+            ;;
+        OS400*)
+            os400=true
+            ;;
+        Darwin)
+            darwin=true
+            ;;
+    esac
+    # For AIX, set an environment variable
+    if ${aix}; then
+         export LDR_CNTRL=MAXDATA=0xB0000000@DSA
+         echo ${LDR_CNTRL}
+    fi
+}
+
+locateJava() {
+    # Setup the Java Virtual Machine
+    if $cygwin ; then
+        [ -n "${JAVA}" ] && JAVA=$(cygpath --unix "${JAVA}")
+        [ -n "${JAVA_HOME}" ] && JAVA_HOME=$(cygpath --unix "${JAVA_HOME}")
+    fi
+
+    if [ "x${JAVA}" = "x" ] && [ -r /etc/gentoo-release ] ; then
+        JAVA_HOME=$(java-config --jre-home)
+    fi
+    if [ "x${JAVA}" = "x" ]; then
+        if [ "x${JAVA_HOME}" != "x" ]; then
+            if [ ! -d "${JAVA_HOME}" ]; then
+                die "JAVA_HOME is not valid: ${JAVA_HOME}"
+            fi
+            JAVA="${JAVA_HOME}/bin/java"
+        else
+            warn "JAVA_HOME not set; results may vary"
+            JAVA=$(type java)
+            JAVA=$(expr "${JAVA}" : '.* \(/.*\)$')
+            if [ "x${JAVA}" = "x" ]; then
+                die "java command not found"
+            fi
+        fi
+    fi
+}
+
+init() {
+    # Determine if there is special OS handling we must perform
+    detectOS
+
+    # Locate the Java VM to execute
+    locateJava
+}
+
+run() {
+    LIBS="${C2_SERVER_HOME}/lib/*"
+
+    sudo_cmd_prefix=""
+    if $cygwin; then
+        C2_SERVER_HOME=$(cygpath --path --windows "${C2_SERVER_HOME}")
+        CLASSPATH="$C2_SERVER_HOME/conf;$(cygpath --path --windows "${LIBS}")"
+    else
+        CLASSPATH="$C2_SERVER_HOME/conf:${LIBS}"
+    fi
+
+    echo
+    echo "Java home: ${JAVA_HOME}"
+    echo "C2 Server home: ${C2_SERVER_HOME}"
+    echo
+    echo
+
+  if [ "$1" = "debug" ]; then
+    "${JAVA}" -cp "${CLASSPATH}"  -Xms12m -Xmx24m -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -Djava.net.preferIPv4Stack=true org.apache.nifi.minifi.c2.jetty.JettyServer $@
+  else
+    "${JAVA}" -cp "${CLASSPATH}" -Xms12m -Xmx24m -Djava.net.preferIPv4Stack=true org.apache.nifi.minifi.c2.jetty.JettyServer $@
+  fi
+   return $?
+}
+
+init
+run "$@"
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/conf/authorities.yaml b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/authorities.yaml
new file mode 100644
index 0000000..a57dd4c
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/authorities.yaml
@@ -0,0 +1,17 @@
+# 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.
+
+CN=raspi3, OU=NIFI:
+  - CLASS_RASPI_3
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/conf/authorizations.yaml b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/authorizations.yaml
new file mode 100644
index 0000000..7016ff4
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/authorizations.yaml
@@ -0,0 +1,30 @@
+# 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.
+
+Default Action: deny
+Paths:
+  /c2/config:
+    Default Action: deny
+    Actions:
+    - Authorization: CLASS_RASPI_3
+      Query Parameters:
+        class: raspi3
+      Action: allow
+    - Authorization: ROLE_SUPERUSER
+      Action: allow
+
+    # Default authorization lets anonymous pull any config.  Remove below to change that.
+    - Authorization: ROLE_ANONYMOUS
+      Action: allow
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/conf/c2.properties b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/c2.properties
new file mode 100644
index 0000000..c68a3e3
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/c2.properties
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+minifi.c2.server.port=${minifi.c2.server.port}
+
+minifi.c2.server.secure=${minifi.c2.server.secure}
+minifi.c2.server.keystore=${minifi.c2.server.keystore}
+minifi.c2.server.keystoreType=${minifi.c2.server.keystoreType}
+minifi.c2.server.keystorePasswd=${minifi.c2.server.keystorePasswd}
+minifi.c2.server.keyPasswd=${minifi.c2.server.keyPasswd}
+minifi.c2.server.truststore=${minifi.c2.server.truststore}
+minifi.c2.server.truststoreType=${minifi.c2.server.truststoreType}
+minifi.c2.server.truststorePasswd=${minifi.c2.server.truststorePasswd}
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/conf/logback.xml b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/logback.xml
new file mode 100644
index 0000000..b9a8129
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/logback.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ /*
+  ~  * 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.
+  ~  */
+  -->
+
+<configuration scan="true" scanPeriod="30 seconds">
+    <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
+        <resetJUL>true</resetJUL>
+    </contextListener>
+	
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%date %level [%thread] %logger{40} %msg%n</pattern>
+        </encoder>
+	</appender>
+
+    <logger name="org.apache.nifi.minifi.c2" level="DEBUG"/>
+
+    <root level="INFO">
+        <appender-ref ref="CONSOLE"/>
+    </root>
+    
+</configuration>
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/conf/minifi-c2-context.xml b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/minifi-c2-context.xml
new file mode 100644
index 0000000..82a3dfc
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/minifi-c2-context.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<beans default-lazy-init="true"
+       xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
+    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
+    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
+
+    <bean id="configService" class="org.apache.nifi.minifi.c2.service.ConfigService" scope="singleton">
+        <constructor-arg>
+            <list>
+                <bean class="org.apache.nifi.minifi.c2.provider.cache.CacheConfigurationProvider">
+                    <constructor-arg>
+                        <value>text/yml</value>
+                    </constructor-arg>
+                    <constructor-arg>
+                        <bean class="org.apache.nifi.minifi.c2.cache.filesystem.FileSystemConfigurationCache">
+                            <constructor-arg>
+                                <value>./files</value>
+                            </constructor-arg>
+                            <constructor-arg>
+                                <value>\${class}/config.yml</value>
+                            </constructor-arg>
+                        </bean>
+                    </constructor-arg>
+                </bean>
+                <!--<bean class="org.apache.nifi.minifi.c2.provider.nifi.rest.NiFiRestConfigurationProvider">
+                    <constructor-arg>
+                        <bean class="org.apache.nifi.minifi.c2.cache.filesystem.FileSystemConfigurationCache">
+                            <constructor-arg>
+                                <value>./cache</value>
+                            </constructor-arg>
+                            <constructor-arg>
+                                <value>\${class}/\${class}</value>
+                            </constructor-arg>
+                        </bean>
+                    </constructor-arg>
+                    <constructor-arg>
+                        <value>${minifi.c2.server.provider.nifi.rest.api.url}</value>
+                    </constructor-arg>
+                </bean>-->
+            </list>
+        </constructor-arg>
+        <constructor-arg>
+            <bean class="org.apache.nifi.minifi.c2.security.authorization.GrantedAuthorityAuthorizer">
+                <constructor-arg value="classpath:authorizations.yaml"/>
+            </bean>
+        </constructor-arg>
+    </bean>
+</beans>
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/conf/minifi-c2-web-security-context.xml b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/minifi-c2-web-security-context.xml
new file mode 100644
index 0000000..9b3f15c
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/minifi-c2-web-security-context.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
+
+    <bean id="x509AuthenticationProvider" class="org.apache.nifi.minifi.c2.security.authentication.X509AuthenticationProvider">
+        <constructor-arg>
+            <bean class="org.apache.nifi.minifi.c2.security.authorization.PrincipalStringAuthorityGranter">
+                <constructor-arg value="classpath:authorities.yaml"/>
+            </bean>
+        </constructor-arg>
+    </bean>
+
+    <bean id="x509AuthenticationFilter" class="org.apache.nifi.minifi.c2.security.authentication.X509AuthenticationFilter"/>
+
+    <bean id="c2AnonymousAuthenticationFilter" class="org.apache.nifi.minifi.c2.security.authentication.C2AnonymousAuthenticationFilter"/>
+</beans>
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/conf/webdefault.xml b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/webdefault.xml
new file mode 100644
index 0000000..8a89e1b
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/conf/webdefault.xml
@@ -0,0 +1,556 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<web-app 
+    xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+    metadata-complete="false"
+    version="3.1"> 
+
+    <!-- ===================================================================== -->
+    <!-- This file contains the default descriptor for web applications.       -->
+    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+    <!-- The intent of this descriptor is to include jetty specific or common  -->
+    <!-- configuration for all webapps.   If a context has a webdefault.xml    -->
+    <!-- descriptor, it is applied before the contexts own web.xml file        -->
+    <!--                                                                       -->
+    <!-- A context may be assigned a default descriptor by:                    -->
+    <!--  + Calling WebApplicationContext.setDefaultsDescriptor                -->
+    <!--  + Passed an arg to addWebApplications                                -->
+    <!--                                                                       -->
+    <!-- This file is used both as the resource within the jetty.jar (which is -->
+    <!-- used as the default if no explicit defaults descriptor is set) and it -->
+    <!-- is copied to the etc directory of the Jetty distro and explicitly     -->
+    <!-- by the jetty.xml file.                                                -->
+    <!--                                                                       -->
+    <!-- ===================================================================== -->
+
+    <description>
+        Default web.xml file.  
+        This file is applied to a Web application before it's own WEB_INF/web.xml file
+    </description>
+
+    <!-- ==================================================================== -->
+    <!-- Removes static references to beans from javax.el.BeanELResolver to   -->
+    <!-- ensure webapp classloader can be released on undeploy                -->
+    <!-- ==================================================================== -->
+    <listener>
+        <listener-class>org.eclipse.jetty.servlet.listener.ELContextCleaner</listener-class>
+    </listener>
+  
+    <!-- ==================================================================== -->
+    <!-- Removes static configurationCache of Methods from java.beans.Introspector to      -->
+    <!-- ensure webapp classloader can be released on undeploy                -->
+    <!-- ==================================================================== -->  
+    <listener>
+        <listener-class>org.eclipse.jetty.servlet.listener.IntrospectorCleaner</listener-class>
+    </listener>
+  
+
+    <!-- ==================================================================== -->
+    <!-- Context params to control Session Cookies                            -->
+    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+    <!--
+      UNCOMMENT TO ACTIVATE 
+      <context-param> 
+        <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name> 
+        <param-value>127.0.0.1</param-value> 
+      </context-param> 
+      <context-param>
+        <param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
+        <param-value>/</param-value>
+      </context-param>
+      <context-param>
+        <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
+        <param-value>-1</param-value>
+      </context-param>
+    -->
+
+    <!-- ==================================================================== -->
+    <!-- The default servlet.                                                 -->
+    <!-- This servlet, normally mapped to /, provides the handling for static -->
+    <!-- content, OPTIONS and TRACE methods for the context.                  -->
+    <!-- The following initParameters are supported:                          -->
+    <!--  
+    *  acceptRanges      If true, range requests and responses are
+    *                    supported
+    *
+    *  dirAllowed        If true, directory listings are returned if no
+    *                    welcome file is found. Else 403 Forbidden.
+    *
+    *  welcomeServlets   If true, attempt to dispatch to welcome files
+    *                    that are servlets, but only after no matching static
+    *                    resources could be found. If false, then a welcome
+    *                    file must exist on disk. If "exact", then exact
+    *                    servlet matches are supported without an existing file.
+    *                    Default is true.
+    *
+    *                    This must be false if you want directory listings,
+    *                    but have index.jsp in your welcome file list.
+    *
+    *  redirectWelcome   If true, welcome files are redirected rather than
+    *                    forwarded to.
+    *
+    *  gzip              If set to true, then static content will be served as
+    *                    gzip content encoded if a matching resource is
+    *                    found ending with ".gz"
+    *
+    *  resourceBase      Set to replace the context resource base
+    *
+    *  resourceCache     If set, this is a context attribute name, which the servlet
+    *                    will use to look for a shared ResourceCache instance.
+    *
+    *  relativeResourceBase
+    *                    Set with a pathname relative to the base of the
+    *                    servlet context root. Useful for only serving static content out
+    *                    of only specific subdirectories.
+    *
+    *  pathInfoOnly      If true, only the path info will be applied to the resourceBase
+    *
+    *  stylesheet        Set with the location of an optional stylesheet that will be used
+    *                    to decorate the directory listing html.
+    *
+    *  aliases           If True, aliases of resources are allowed (eg. symbolic
+    *                    links and caps variations). May bypass security constraints.
+    *                    
+    *  etags             If True, weak etags will be generated and handled.
+    *
+    *  maxCacheSize      The maximum total size of the configurationCache or 0 for no configurationCache.
+    *  maxCachedFileSize The maximum size of a file to configurationCache
+    *  maxCachedFiles    The maximum number of files to configurationCache
+    *
+    *  useFileMappedBuffer
+    *                    If set to true, it will use mapped file buffer to serve static content
+    *                    when using NIO connector. Setting this value to false means that
+    *                    a direct buffer will be used instead of a mapped file buffer.
+    *                    By default, this is set to true.
+    *
+    *  cacheControl      If set, all static content will have this value set as the configurationCache-control
+    *                    header.
+    *
+    -->
+ 
+ 
+    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+    <servlet>
+        <servlet-name>default</servlet-name>
+        <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
+        <init-param>
+            <param-name>aliases</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>acceptRanges</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <init-param>
+            <param-name>dirAllowed</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>welcomeServlets</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <init-param>
+            <param-name>redirectWelcome</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>maxCacheSize</param-name>
+            <param-value>256000000</param-value>
+        </init-param>
+        <init-param>
+            <param-name>maxCachedFileSize</param-name>
+            <param-value>200000000</param-value>
+        </init-param>
+        <init-param>
+            <param-name>maxCachedFiles</param-name>
+            <param-value>2048</param-value>
+        </init-param>
+        <init-param>
+            <param-name>gzip</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <init-param>
+            <param-name>etags</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>useFileMappedBuffer</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <!--
+        <init-param>
+          <param-name>resourceCache</param-name>
+          <param-value>resourceCache</param-value>
+        </init-param>
+        -->
+        <!--
+        <init-param>
+          <param-name>cacheControl</param-name>
+          <param-value>max-age=3600,public</param-value>
+        </init-param>
+        -->
+        <load-on-startup>0</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>default</servlet-name>
+        <url-pattern>/</url-pattern>
+    </servlet-mapping>
+
+
+    <!-- ==================================================================== -->
+    <!-- JSP Servlet                                                          -->
+    <!-- This is the jasper JSP servlet from the jakarta project              -->
+    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+    <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
+    <!-- used by Glassfish to support JSP pages.  Traditionally, this servlet -->
+    <!-- is mapped to URL patterh "*.jsp".  This servlet supports the         -->
+    <!-- following initialization parameters (default values are in square    -->
+    <!-- brackets):                                                           -->
+    <!--                                                                      -->
+    <!--   checkInterval       If development is false and reloading is true, -->
+    <!--                       background compiles are enabled. checkInterval -->
+    <!--                       is the time in seconds between checks to see   -->
+    <!--                       if a JSP page needs to be recompiled. [300]    -->
+    <!--                                                                      -->
+    <!--   compiler            Which compiler Ant should use to compile JSP   -->
+    <!--                       pages.  See the Ant documenation for more      -->
+    <!--                       information. [javac]                           -->
+    <!--                                                                      -->
+    <!--   classdebuginfo      Should the class file be compiled with         -->
+    <!--                       debugging information?  [true]                 -->
+    <!--                                                                      -->
+    <!--   classpath           What class path should I use while compiling   -->
+    <!--                       generated servlets?  [Created dynamically      -->
+    <!--                       based on the current web application]          -->
+    <!--                       Set to ? to make the container explicitly set  -->
+    <!--                       this parameter.                                -->
+    <!--                                                                      -->
+    <!--   development         Is Jasper used in development mode (will check -->
+    <!--                       for JSP modification on every access)?  [true] -->
+    <!--                                                                      -->
+    <!--   enablePooling       Determines whether tag handler pooling is      -->
+    <!--                       enabled  [true]                                -->
+    <!--                                                                      -->
+    <!--   fork                Tell Ant to fork compiles of JSP pages so that -->
+    <!--                       a separate JVM is used for JSP page compiles   -->
+    <!--                       from the one Tomcat is running in. [true]      -->
+    <!--                                                                      -->
+    <!--   ieClassId           The class-id value to be sent to Internet      -->
+    <!--                       Explorer when using <jsp:plugin> tags.         -->
+    <!--                       [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93]   -->
+    <!--                                                                      -->
+    <!--   javaEncoding        Java file encoding to use for generating java  -->
+    <!--                       source files. [UTF-8]                          -->
+    <!--                                                                      -->
+    <!--   keepgenerated       Should we keep the generated Java source code  -->
+    <!--                       for each page instead of deleting it? [true]   -->
+    <!--                                                                      -->
+    <!--   logVerbosityLevel   The level of detailed messages to be produced  -->
+    <!--                       by this servlet.  Increasing levels cause the  -->
+    <!--                       generation of more messages.  Valid values are -->
+    <!--                       FATAL, ERROR, WARNING, INFORMATION, and DEBUG. -->
+    <!--                       [WARNING]                                      -->
+    <!--                                                                      -->
+    <!--   mappedfile          Should we generate static content with one     -->
+    <!--                       print statement per input line, to ease        -->
+    <!--                       debugging?  [false]                            -->
+    <!--                                                                      -->
+    <!--                                                                      -->
+    <!--   reloading           Should Jasper check for modified JSPs?  [true] -->
+    <!--                                                                      -->
+    <!--   suppressSmap        Should the generation of SMAP info for JSR45   -->
+    <!--                       debugging be suppressed?  [false]              -->
+    <!--                                                                      -->
+    <!--   dumpSmap            Should the SMAP info for JSR45 debugging be    -->
+    <!--                       dumped to a file? [false]                      -->
+    <!--                       False if suppressSmap is true                  -->
+    <!--                                                                      -->
+    <!--   scratchdir          What scratch directory should we use when      -->
+    <!--                       compiling JSP pages?  [default work directory  -->
+    <!--                       for the current web application]               -->
+    <!--                                                                      -->
+    <!--   tagpoolMaxSize      The maximum tag handler pool size  [5]         -->
+    <!--                                                                      -->
+    <!--   xpoweredBy          Determines whether X-Powered-By response       -->
+    <!--                       header is added by generated servlet  [false]  -->
+    <!--                                                                      -->
+    <!-- If you wish to use Jikes to compile JSP pages:                       -->
+    <!--   Set the init parameter "compiler" to "jikes".  Define              -->
+    <!--   the property "-Dbuild.compiler.emacs=true" when starting Jetty     -->
+    <!--   to cause Jikes to emit error messages in a format compatible with  -->
+    <!--   Jasper.                                                            -->
+    <!--   If you get an error reporting that jikes can't use UTF-8 encoding, -->
+    <!--   try setting the init parameter "javaEncoding" to "ISO-8859-1".     -->
+    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+    <servlet id="jsp">
+        <servlet-name>jsp</servlet-name>
+        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+        <init-param>
+            <param-name>logVerbosityLevel</param-name>
+            <param-value>DEBUG</param-value>
+        </init-param>
+        <init-param>
+            <param-name>fork</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>keepgenerated</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <init-param>
+            <param-name>development</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>xpoweredBy</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>compilerTargetVM</param-name>
+            <param-value>1.7</param-value>
+        </init-param>
+        <init-param>
+            <param-name>compilerSourceVM</param-name>
+            <param-value>1.7</param-value>
+        </init-param>
+        <!--  
+        <init-param>
+            <param-name>classpath</param-name>
+            <param-value>?</param-value>
+        </init-param>
+        -->
+        <load-on-startup>0</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>jsp</servlet-name>
+        <url-pattern>*.jsp</url-pattern>
+        <url-pattern>*.jspf</url-pattern>
+        <url-pattern>*.jspx</url-pattern>
+        <url-pattern>*.xsp</url-pattern>
+        <url-pattern>*.JSP</url-pattern>
+        <url-pattern>*.JSPF</url-pattern>
+        <url-pattern>*.JSPX</url-pattern>
+        <url-pattern>*.XSP</url-pattern>
+    </servlet-mapping>
+
+
+    <!-- ==================================================================== -->
+    <session-config>
+        <session-timeout>30</session-timeout>
+    </session-config>
+
+    <!-- ==================================================================== -->
+    <!-- Default MIME mappings                                                -->
+    <!-- The default MIME mappings are provided by the mime.properties        -->
+    <!-- resource in the org.eclipse.jetty.server.jar file.  Additional or modified  -->
+    <!-- mappings may be specified here                                       -->
+    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+    <!-- UNCOMMENT TO ACTIVATE
+    <mime-mapping>
+      <extension>mysuffix</extension>
+      <mime-type>mymime/type</mime-type>
+    </mime-mapping>
+    -->
+
+    <!-- ==================================================================== -->
+    <welcome-file-list>
+        <welcome-file>index.html</welcome-file>
+        <welcome-file>index.htm</welcome-file>
+        <welcome-file>index.jsp</welcome-file>
+    </welcome-file-list>
+
+    <!-- ==================================================================== -->
+    <locale-encoding-mapping-list>
+        <locale-encoding-mapping>
+            <locale>ar</locale>
+            <encoding>ISO-8859-6</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>be</locale>
+            <encoding>ISO-8859-5</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>bg</locale>
+            <encoding>ISO-8859-5</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>ca</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>cs</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>da</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>de</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>el</locale>
+            <encoding>ISO-8859-7</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>en</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>es</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>et</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>fi</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>fr</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>hr</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>hu</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>is</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>it</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>iw</locale>
+            <encoding>ISO-8859-8</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>ja</locale>
+            <encoding>Shift_JIS</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>ko</locale>
+            <encoding>EUC-KR</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>lt</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>lv</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>mk</locale>
+            <encoding>ISO-8859-5</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>nl</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>no</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>pl</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>pt</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>ro</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>ru</locale>
+            <encoding>ISO-8859-5</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>sh</locale>
+            <encoding>ISO-8859-5</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>sk</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>sl</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>sq</locale>
+            <encoding>ISO-8859-2</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>sr</locale>
+            <encoding>ISO-8859-5</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>sv</locale>
+            <encoding>ISO-8859-1</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>tr</locale>
+            <encoding>ISO-8859-9</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>uk</locale>
+            <encoding>ISO-8859-5</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>zh</locale>
+            <encoding>GB2312</encoding>
+        </locale-encoding-mapping>
+        <locale-encoding-mapping>
+            <locale>zh_TW</locale>
+            <encoding>Big5</encoding>
+        </locale-encoding-mapping>
+    </locale-encoding-mapping-list>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Disable TRACE</web-resource-name>
+            <url-pattern>/</url-pattern>
+            <http-method>TRACE</http-method>
+        </web-resource-collection>
+        <auth-constraint/>
+    </security-constraint>
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Enable everything but TRACE</web-resource-name>
+            <url-pattern>/</url-pattern>
+            <http-method-omission>TRACE</http-method-omission>
+        </web-resource-collection>
+    </security-constraint>
+
+</web-app>
+
diff --git a/minifi-c2/minifi-c2-assembly/src/main/resources/files/raspi3/config.yml.v1 b/minifi-c2/minifi-c2-assembly/src/main/resources/files/raspi3/config.yml.v1
new file mode 100644
index 0000000..1a7f872
--- /dev/null
+++ b/minifi-c2/minifi-c2-assembly/src/main/resources/files/raspi3/config.yml.v1
@@ -0,0 +1,63 @@
+# 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.
+
+MiNiFi Config Version: 3
+Flow Controller:
+  name: MiNiFi Flow
+  comment: ''
+Core Properties:
+  flow controller graceful shutdown period: 10 sec
+  flow service write delay interval: 500 ms
+  administrative yield duration: 30 sec
+  bored yield duration: 10 millis
+  max concurrent threads: 1
+FlowFile Repository:
+  partitions: 256
+  checkpoint interval: 2 mins
+  always sync: false
+  Swap:
+    threshold: 20000
+    in period: 5 sec
+    in threads: 1
+    out period: 5 sec
+    out threads: 4
+Content Repository:
+  content claim max appendable size: 10 MB
+  content claim max flow files: 100
+  always sync: false
+Provenance Repository:
+  provenance rollover time: 1 min
+Component Status Repository:
+  buffer size: 1440
+  snapshot frequency: 1 min
+Security Properties:
+  keystore: ''
+  keystore type: ''
+  keystore password: ''
+  key password: ''
+  truststore: ''
+  truststore type: ''
+  truststore password: ''
+  ssl protocol: ''
+  Sensitive Props:
+    key: ''
+    algorithm: PBEWITHMD5AND256BITAES-CBC-OPENSSL
+    provider: BC
+Processors: []
+Process Groups: []
+Funnels: []
+Connections: []
+Remote Process Groups: []
+NiFi Properties Overrides: {}
diff --git a/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/pom.xml b/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/pom.xml
new file mode 100644
index 0000000..b11ddfa
--- /dev/null
+++ b/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/pom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2-cache</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-cache-filesystem</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/src/main/java/org/apache/nifi/minifi/c2/cache/filesystem/FileSystemCacheFileInfoImpl.java b/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/src/main/java/org/apache/nifi/minifi/c2/cache/filesystem/FileSystemCacheFileInfoImpl.java
new file mode 100644
index 0000000..ae2c1ae
--- /dev/null
+++ b/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/src/main/java/org/apache/nifi/minifi/c2/cache/filesystem/FileSystemCacheFileInfoImpl.java
@@ -0,0 +1,86 @@
+/*
+ * 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.c2.cache.filesystem;
+
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+import org.apache.nifi.minifi.c2.api.InvalidParameterException;
+import org.apache.nifi.minifi.c2.api.cache.ConfigurationCacheFileInfo;
+import org.apache.nifi.minifi.c2.api.cache.WriteableConfiguration;
+import org.apache.nifi.minifi.c2.api.util.Pair;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+public class FileSystemCacheFileInfoImpl implements ConfigurationCacheFileInfo {
+    private final FileSystemConfigurationCache cache;
+    private final Path dirPath;
+    private final String expectedFilename;
+    private final int expectedFilenameLength;
+
+    public FileSystemCacheFileInfoImpl(FileSystemConfigurationCache cache, Path dirPath, String expectedFilename) {
+        this.cache = cache;
+        this.dirPath = dirPath;
+        this.expectedFilename = expectedFilename;
+        this.expectedFilenameLength = expectedFilename.length();
+    }
+
+    @Override
+    public Integer getVersionIfMatch(String filename) {
+        if (!filename.startsWith(expectedFilename) || filename.length() == expectedFilenameLength) {
+            return null;
+        }
+        try {
+            return Integer.parseInt(filename.substring(expectedFilenameLength));
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public Stream<WriteableConfiguration> getCachedConfigurations() throws IOException {
+        return Files.list(dirPath).map(p -> {
+            Integer version = getVersionIfMatch(p.getFileName().toString());
+            if (version == null) {
+                return null;
+            }
+            return new Pair<>(version, p);
+        }).filter(Objects::nonNull)
+                .sorted(Comparator.comparing(pair -> ((Pair<Integer, Path>) pair).getFirst())
+                        .reversed()).map(pair -> new FileSystemWritableConfiguration(cache, pair.getSecond(), Integer.toString(pair.getFirst())));
+    }
+
+    @Override
+    public WriteableConfiguration getConfiguration(Integer version) throws ConfigurationProviderException {
+        if (version == null) {
+            try {
+                return getCachedConfigurations().findFirst().orElseThrow(() -> new ConfigurationProviderException("No configurations found for " + dirPath + "/" + expectedFilename + "[0-9]+"));
+            } catch (IOException e) {
+                throw new ConfigurationProviderException("Unable to get cached configurations.", e);
+            }
+        }
+        try {
+            return new FileSystemWritableConfiguration(cache, cache.resolveChildAndVerifyParent(dirPath, expectedFilename + version), Integer.toString(version));
+        } catch (NumberFormatException e) {
+            throw new InvalidParameterException("Expected numeric version.", e);
+        }
+    }
+}
diff --git a/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/src/main/java/org/apache/nifi/minifi/c2/cache/filesystem/FileSystemConfigurationCache.java b/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/src/main/java/org/apache/nifi/minifi/c2/cache/filesystem/FileSystemConfigurationCache.java
new file mode 100644
index 0000000..ced36a9
--- /dev/null
+++ b/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/src/main/java/org/apache/nifi/minifi/c2/cache/filesystem/FileSystemConfigurationCache.java
@@ -0,0 +1,80 @@
+/*
+ * 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.c2.cache.filesystem;
+
+import org.apache.nifi.minifi.c2.api.InvalidParameterException;
+import org.apache.nifi.minifi.c2.api.cache.ConfigurationCache;
+import org.apache.nifi.minifi.c2.api.cache.ConfigurationCacheFileInfo;
+import org.apache.nifi.minifi.c2.api.util.Pair;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+public class FileSystemConfigurationCache implements ConfigurationCache {
+    private final Path pathRoot;
+    private final String pathPattern;
+
+    public FileSystemConfigurationCache(String pathRoot, String pathPattern) throws IOException {
+        this.pathRoot = Paths.get(System.getenv("C2_SERVER_HOME")).resolve(pathRoot).toAbsolutePath();
+        Files.createDirectories(this.pathRoot);
+        this.pathPattern = pathPattern;
+    }
+
+    protected Path resolveChildAndVerifyParent(Path parent, String s) throws InvalidParameterException {
+        Path child = parent.resolve(s).toAbsolutePath();
+        if (child.toAbsolutePath().getParent().equals(parent)) {
+            return child;
+        } else {
+            throw new InvalidParameterException("Path entry " + s + " not child of " + parent);
+        }
+    }
+
+    @Override
+    public ConfigurationCacheFileInfo getCacheFileInfo(Map<String, List<String>> parameters) throws InvalidParameterException {
+        String pathString = pathPattern;
+        for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
+            if (entry.getValue().size() != 1) {
+                throw new InvalidParameterException("Multiple values for same parameter not supported in this provider.");
+            }
+            pathString = pathString.replaceAll(Pattern.quote("${" + entry.getKey() + "}"), entry.getValue().get(0));
+        }
+        String[] split = pathString.split("/");
+        for (String s1 : split) {
+            int openBrace = s1.indexOf("${");
+            if (openBrace >= 0 && openBrace < s1.length() + 2) {
+                int closeBrace = s1.indexOf("}", openBrace + 2);
+                if (closeBrace >= 0) {
+                    throw new InvalidParameterException("Found unsubstituted variable " + s1.substring(openBrace + 2, closeBrace));
+                }
+            }
+        }
+        String[] splitPath = split;
+        Path path = pathRoot.toAbsolutePath();
+        for (int i = 0; i < splitPath.length - 1; i++) {
+            String s = splitPath[i];
+            path = resolveChildAndVerifyParent(path, s);
+        }
+        Pair<Path, String> dirPathAndFilename = new Pair<>(path, splitPath[splitPath.length - 1]);
+        return new FileSystemCacheFileInfoImpl(this, dirPathAndFilename.getFirst(), dirPathAndFilename.getSecond() + ".v");
+    }
+}
diff --git a/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/src/main/java/org/apache/nifi/minifi/c2/cache/filesystem/FileSystemWritableConfiguration.java b/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/src/main/java/org/apache/nifi/minifi/c2/cache/filesystem/FileSystemWritableConfiguration.java
new file mode 100644
index 0000000..bb48dcc
--- /dev/null
+++ b/minifi-c2/minifi-c2-cache/minifi-c2-cache-filesystem/src/main/java/org/apache/nifi/minifi/c2/cache/filesystem/FileSystemWritableConfiguration.java
@@ -0,0 +1,94 @@
+/*
+ * 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.c2.cache.filesystem;
+
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+import org.apache.nifi.minifi.c2.api.InvalidParameterException;
+import org.apache.nifi.minifi.c2.api.cache.WriteableConfiguration;
+import org.apache.nifi.minifi.c2.api.util.DelegatingOutputStream;
+
+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.UUID;
+
+public class FileSystemWritableConfiguration implements WriteableConfiguration {
+    private final FileSystemConfigurationCache cache;
+    private final Path path;
+    private final String version;
+
+    public FileSystemWritableConfiguration(FileSystemConfigurationCache cache, Path path, String version) {
+        this.cache = cache;
+        this.path = path;
+        this.version = version;
+    }
+
+    @Override
+    public String getVersion() {
+        return version;
+    }
+
+    @Override
+    public boolean exists() {
+        return Files.exists(path);
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws ConfigurationProviderException {
+        try {
+            Path parent = path.getParent();
+            Files.createDirectories(parent);
+            Path tmpPath = cache.resolveChildAndVerifyParent(parent, path.getFileName().toString() + "." + UUID.randomUUID().toString());
+            return new DelegatingOutputStream(Files.newOutputStream(tmpPath)) {
+                @Override
+                public void close() throws IOException {
+                    super.close();
+                    Files.move(tmpPath, path);
+                }
+            };
+        } catch (IOException e) {
+            throw new ConfigurationProviderException("Unable to open " + path + " for writing.", e);
+        }
+    }
+
+    @Override
+    public InputStream getInputStream() throws ConfigurationProviderException {
+        try {
+            return Files.newInputStream(path, StandardOpenOption.READ);
+        } catch (IOException e) {
+            if (Files.exists(path)) {
+                throw new ConfigurationProviderException("Unable to open " + path + " for reading.", e);
+            } else {
+                throw new InvalidParameterException("File not found: " + path, e);
+            }
+        }
+    }
+
+    @Override
+    public String getName() {
+        return path.getFileName().toString();
+    }
+
+    @Override
+    public String toString() {
+        return "FileSystemWritableConfiguration{path=" + path + ", version='" + version + "'}";
+    }
+}
diff --git a/minifi-c2/minifi-c2-cache/pom.xml b/minifi-c2/minifi-c2-cache/pom.xml
new file mode 100644
index 0000000..f452317
--- /dev/null
+++ b/minifi-c2/minifi-c2-cache/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-cache</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>minifi-c2-cache-filesystem</module>
+    </modules>
+</project>
diff --git a/minifi-c2/minifi-c2-docker/README.md b/minifi-c2/minifi-c2-docker/README.md
new file mode 100644
index 0000000..9e0d846
--- /dev/null
+++ b/minifi-c2/minifi-c2-docker/README.md
@@ -0,0 +1,27 @@
+<!--
+  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.
+-->
+
+Apache MiNiFi Command And Control Server
+-------------
+A subproject of Apache NiFi and a complementary data collection approach that supplements the core tenets of NiFi in dataflow management, focusing on the collection of data at the source of its creation.
+
+Specific goals for the initial thrust of the MiNiFi effort comprise:
+
+* small and lightweight footprint,
+* central management of agents,
+* generation of data provenance, and
+* integration with NiFi for follow-on dataflow management and full chain of custody of information.
+
+Perspectives of the role of MiNiFi should be from the perspective of the agent acting immediately at, or directly adjacent to, source sensors, systems, or servers.
diff --git a/minifi-c2/minifi-c2-docker/dockerhub/.dockerignore b/minifi-c2/minifi-c2-docker/dockerhub/.dockerignore
new file mode 100644
index 0000000..30a2650
--- /dev/null
+++ b/minifi-c2/minifi-c2-docker/dockerhub/.dockerignore
@@ -0,0 +1,19 @@
+# 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.
+
+# Place files you want to exclude from the docker build here similar to .gitignore https://docs.docker.com/engine/reference/builder/#dockerignore-file
+DockerBuild.sh
+DockerRun.sh
+DockerImage.txt
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-docker/dockerhub/DockerBuild.sh b/minifi-c2/minifi-c2-docker/dockerhub/DockerBuild.sh
new file mode 100755
index 0000000..0ce7783
--- /dev/null
+++ b/minifi-c2/minifi-c2-docker/dockerhub/DockerBuild.sh
@@ -0,0 +1,31 @@
+# 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.
+
+#!/bin/bash
+
+DOCKER_UID=1000
+if [ -n "$1" ]; then
+  DOCKER_UID="$1"
+fi
+
+DOCKER_GID=50
+if [ -n "$2" ]; then
+  DOCKER_GID="$2"
+fi
+
+DOCKER_IMAGE="$(egrep -v '(^#|^\s*$|^\s*\t*#)' DockerImage.txt)"
+MINIFI_C2_IMAGE_VERSION="$(echo $DOCKER_IMAGE | cut -d : -f 2)"
+echo "Building MiNiFi C2 Server Image: '$DOCKER_IMAGE' Version: $MINIFI_C2_IMAGE_VERSION"
+docker build --build-arg UID="$DOCKER_UID" --build-arg GID="$DOCKER_GID" --build-arg MINIFI_C2_VERSION="$MINIFI_C2_IMAGE_VERSION" -t $DOCKER_IMAGE .
diff --git a/minifi-c2/minifi-c2-docker/dockerhub/DockerImage.txt b/minifi-c2/minifi-c2-docker/dockerhub/DockerImage.txt
new file mode 100644
index 0000000..bc7c4a3
--- /dev/null
+++ b/minifi-c2/minifi-c2-docker/dockerhub/DockerImage.txt
@@ -0,0 +1,16 @@
+# 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.
+
+apacheminific2:0.1.0
diff --git a/minifi-c2/minifi-c2-docker/dockerhub/DockerRun.sh b/minifi-c2/minifi-c2-docker/dockerhub/DockerRun.sh
new file mode 100755
index 0000000..9436848
--- /dev/null
+++ b/minifi-c2/minifi-c2-docker/dockerhub/DockerRun.sh
@@ -0,0 +1,19 @@
+# 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.
+
+#!/bin/bash
+DOCKER_IMAGE="$(egrep -v '(^#|^\s*$|^\s*\t*#)' DockerImage.txt)"
+echo "Running Docker Image: $DOCKER_IMAGE"
+docker run -it -d $DOCKER_IMAGE
diff --git a/minifi-c2/minifi-c2-docker/dockerhub/Dockerfile b/minifi-c2/minifi-c2-docker/dockerhub/Dockerfile
new file mode 100644
index 0000000..f139052
--- /dev/null
+++ b/minifi-c2/minifi-c2-docker/dockerhub/Dockerfile
@@ -0,0 +1,49 @@
+# 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.
+#
+
+FROM openjdk:8
+MAINTAINER Apache MiNiFi <dev@nifi.apache.org>
+
+ARG UID=1000
+ARG GID=50
+ARG MINIFI_C2_VERSION=0.2.0
+
+ENV MINIFI_C2_BASE_DIR /opt/minifi-c2
+ENV MINIFI_C2_HOME $MINIFI_C2_BASE_DIR/minifi-c2-$MINIFI_C2_VERSION
+ENV MINIFI_C2_BINARY_URL https://archive.apache.org/dist/minifi/$MINIFI_C2_VERSION/minifi-c2-$MINIFI_C2_VERSION-bin.tar.gz
+
+# Setup c2 user
+RUN groupadd -g $GID c2 || groupmod -n c2 `getent group $GID | cut -d: -f1`
+RUN useradd --shell /bin/bash -u $UID -g $GID -m c2
+RUN mkdir -p $MINIFI_C2_HOME
+
+# Download, validate, and expand Apache MiNiFi C2 binary.
+RUN curl -fSL $MINIFI_C2_BINARY_URL -o $MINIFI_C2_BASE_DIR/minifi-c2-$MINIFI_C2_VERSION-bin.tar.gz \
+	&& echo "$(curl $MINIFI_C2_BINARY_URL.sha256) *$MINIFI_C2_BASE_DIR/minifi-c2-$MINIFI_C2_VERSION-bin.tar.gz" | sha256sum -c - \
+	&& tar -xvzf $MINIFI_C2_BASE_DIR/minifi-c2-$MINIFI_C2_VERSION-bin.tar.gz -C $MINIFI_C2_BASE_DIR \
+	&& rm $MINIFI_C2_BASE_DIR/minifi-c2-$MINIFI_C2_VERSION-bin.tar.gz
+
+RUN chown -R c2:c2 $MINIFI_C2_HOME
+
+USER c2
+
+#Default http port
+EXPOSE 10080
+
+# Startup MiNiFi C2
+CMD $MINIFI_C2_HOME/bin/c2.sh
diff --git a/minifi-c2/minifi-c2-docker/dockermaven/Dockerfile b/minifi-c2/minifi-c2-docker/dockermaven/Dockerfile
new file mode 100644
index 0000000..329a110
--- /dev/null
+++ b/minifi-c2/minifi-c2-docker/dockermaven/Dockerfile
@@ -0,0 +1,44 @@
+# 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.
+#
+
+FROM openjdk:8
+MAINTAINER Apache MiNiFi <dev@nifi.apache.org>
+
+ARG UID=1000
+ARG GID=50
+ARG MINIFI_C2_VERSION
+ARG MINIFI_C2_BINARY
+
+ENV MINIFI_C2_BASE_DIR /opt/minifi-c2
+ENV MINIFI_C2_HOME $MINIFI_C2_BASE_DIR/minifi-c2-$MINIFI_C2_VERSION
+
+# Setup MiNiFi C2 user
+RUN groupadd -g $GID c2 || groupmod -n c2 `getent group $GID | cut -d: -f1`
+RUN useradd --shell /bin/bash -u $UID -g $GID -m c2
+RUN mkdir -p $MINIFI_C2_HOME 
+
+ADD $MINIFI_C2_BINARY $MINIFI_C2_BASE_DIR
+RUN chown -R c2:c2 $MINIFI_C2_HOME
+
+USER c2
+
+#Default http port
+EXPOSE 10080
+
+# Startup MiNiFi c2
+CMD $MINIFI_C2_HOME/bin/c2.sh run
diff --git a/minifi-c2/minifi-c2-docker/pom.xml b/minifi-c2/minifi-c2-docker/pom.xml
new file mode 100644
index 0000000..edf91d4
--- /dev/null
+++ b/minifi-c2/minifi-c2-docker/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    
+    <parent>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <artifactId>minifi-c2</artifactId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.nifi.minifi</groupId>
+    <artifactId>minifi-c2-docker</artifactId>
+    <version>0.2.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <properties>
+        <minifi.c2.version>${project.version}</minifi.c2.version>
+    </properties>
+
+    <profiles>
+        <!-- Profile for building official Docker images. Not bound to build phases since that would require anyone build to have the Docker engine installed on their machine -->       
+        <profile>                                   
+            <id>docker</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>com.spotify</groupId>
+                        <artifactId>docker-maven-plugin</artifactId>
+                        <version>0.4.13</version>
+                        <executions>
+                            <execution>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>build</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <imageName>apacheminific2</imageName>
+                            <dockerDirectory>${project.basedir}/dockermaven</dockerDirectory>
+                            <imageTags>
+                               <imageTag>${minifi.c2.version}</imageTag>
+                            </imageTags>
+                            <buildArgs>
+                                <UID>1000</UID>
+                                <GID>1000</GID>
+                                <MINIFI_C2_VERSION>${minifi.c2.version}</MINIFI_C2_VERSION>
+                                <MINIFI_C2_BINARY>minifi-c2-${minifi.c2.version}-bin.tar.gz</MINIFI_C2_BINARY>
+                            </buildArgs>
+                            <resources>
+                               <resource>
+                                 <targetPath>/</targetPath>
+                                 <directory>${project.basedir}/../minifi-c2-assembly/target</directory>
+                                 <include>minifi-c2-${minifi.c2.version}-bin.tar.gz</include>
+                               </resource>
+                            </resources>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>      
+</project>
diff --git a/minifi-c2/minifi-c2-integration-tests/pom.xml b/minifi-c2/minifi-c2-integration-tests/pom.xml
new file mode 100644
index 0000000..60af910
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/pom.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-integration-tests</artifactId>
+    <packaging>jar</packaging>
+
+    <properties>
+        <minifi.c2.version>${project.version}</minifi.c2.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-commons-schema</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-api</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-toolkit-tls</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.palantir.docker.compose</groupId>
+            <artifactId>docker-compose-rule-junit4</artifactId>
+            <version>0.31.1</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+                <filtering>true</filtering>
+                <excludes>
+                    <exclude>**/minifi-c2-context.xml</exclude>
+                </excludes>
+            </testResource>
+            <testResource>
+                <directory>src/test/resources</directory>
+                <filtering>false</filtering>
+                <includes>
+                    <include>**/minifi-c2-context.xml</include>
+                </includes>
+            </testResource>
+        </testResources>
+
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <configuration>
+                    <excludes combine.children="append">
+                        <exclude>src/test/resources/mocknifi/www/nifi-api/flow/templates</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>docker</id>
+            <properties>
+                <name>docker</name>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>false</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <repositories>
+        <repository>
+            <id>palantir.bintray</id>
+            <name>Palantir Bintray</name>
+            <url>https://dl.bintray.com/palantir/releases</url>
+        </repository>
+    </repositories>
+</project>
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/AbstractTestSecure.java b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/AbstractTestSecure.java
new file mode 100644
index 0000000..222512f
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/AbstractTestSecure.java
@@ -0,0 +1,199 @@
+/*
+ * 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.c2.integration.test;
+
+import com.palantir.docker.compose.DockerComposeRule;
+import com.palantir.docker.compose.connection.DockerPort;
+import org.apache.commons.io.IOUtils;
+import org.apache.nifi.minifi.commons.schema.ConfigSchema;
+import org.apache.nifi.minifi.commons.schema.serialization.SchemaLoader;
+import org.apache.nifi.security.util.KeyStoreUtils;
+import org.apache.nifi.security.util.SslContextFactory;
+import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandalone;
+import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandaloneCommandLine;
+import org.junit.Test;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public abstract class AbstractTestSecure extends AbstractTestUnsecure {
+    public static final String C2_URL = "https://c2:10443/c2/config";
+
+    private final DockerComposeRule docker;
+    private final Path certificatesDirectory;
+    private final SSLContext trustSslContext;
+
+    protected AbstractTestSecure(DockerComposeRule docker, Path certificatesDirectory, SSLContext trustSslContext) {
+        this.docker = docker;
+        this.certificatesDirectory = certificatesDirectory;
+        this.trustSslContext = trustSslContext;
+    }
+
+    @Override
+    protected String getConfigUrl(DockerComposeRule docker) {
+        return C2_URL;
+    }
+
+    public static SSLContext initCertificates(Path certificatesDirectory, List<String> serverHostnames) throws Exception {
+        List<String> toolkitCommandLine = new ArrayList<>(Arrays.asList("-O", "-o", certificatesDirectory.toFile().getAbsolutePath(),
+                "-C", "CN=user1", "-C", "CN=user2", "-C", "CN=user3", "-C", "CN=user4", "-S", "badKeystorePass", "-K", "badKeyPass", "-P", "badTrustPass"));
+        for (String serverHostname : serverHostnames) {
+            toolkitCommandLine.add("-n");
+            toolkitCommandLine.add(serverHostname);
+        }
+        Files.createDirectories(certificatesDirectory);
+        TlsToolkitStandaloneCommandLine tlsToolkitStandaloneCommandLine = new TlsToolkitStandaloneCommandLine();
+        tlsToolkitStandaloneCommandLine.parse(toolkitCommandLine.toArray(new String[toolkitCommandLine.size()]));
+        new TlsToolkitStandalone().createNifiKeystoresAndTrustStores(tlsToolkitStandaloneCommandLine.createConfig());
+
+        tlsToolkitStandaloneCommandLine = new TlsToolkitStandaloneCommandLine();
+        tlsToolkitStandaloneCommandLine.parse(new String[]{"-O", "-o", certificatesDirectory.getParent().resolve("badCert").toFile().getAbsolutePath(), "-C", "CN=user3"});
+        new TlsToolkitStandalone().createNifiKeystoresAndTrustStores(tlsToolkitStandaloneCommandLine.createConfig());
+
+        final KeyStore trustStore = KeyStoreUtils.getTrustStore("jks");
+        try (final InputStream trustStoreStream = new FileInputStream(certificatesDirectory.resolve("c2").resolve("truststore.jks").toFile().getAbsolutePath())) {
+            trustStore.load(trustStoreStream, "badTrustPass".toCharArray());
+        }
+        final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        trustManagerFactory.init(trustStore);
+
+        return SslContextFactory.createTrustSslContext(certificatesDirectory.resolve("c2").resolve("truststore.jks").toFile().getAbsolutePath(), "badTrustPass".toCharArray(), "jks", "TLS");
+    }
+
+    @Test
+    public void testNoClientCert() throws Exception {
+        assertReturnCode("", trustSslContext, 403);
+        assertReturnCode("?class=raspi2", trustSslContext, 403);
+        assertReturnCode("?class=raspi3", trustSslContext, 403);
+    }
+
+    @Test
+    public void testUser1() throws Exception {
+        SSLContext sslContext = loadSslContext("user1");
+
+        assertReturnCode("", sslContext, 403);
+
+        ConfigSchema configSchema = assertReturnCode("?class=raspi2", sslContext, 200);
+        assertEquals("raspi2.v1", configSchema.getFlowControllerProperties().getName());
+
+        assertReturnCode("?class=raspi3", sslContext, 403);
+    }
+
+    @Test
+    public void testUser2() throws Exception {
+        SSLContext sslContext = loadSslContext("user2");
+
+        assertReturnCode("", sslContext, 403);
+        assertReturnCode("?class=raspi2", sslContext, 403);
+
+        ConfigSchema configSchema = assertReturnCode("?class=raspi3", sslContext, 200);
+        assertEquals("raspi3.v2", configSchema.getFlowControllerProperties().getName());
+    }
+
+    @Test
+    public void testUser3() throws Exception {
+        SSLContext sslContext = loadSslContext("user3");
+
+        assertReturnCode("", sslContext, 400);
+
+        ConfigSchema configSchema = assertReturnCode("?class=raspi2", sslContext, 200);
+        assertEquals("raspi2.v1", configSchema.getFlowControllerProperties().getName());
+
+        configSchema = assertReturnCode("?class=raspi3", sslContext, 200);
+        assertEquals("raspi3.v2", configSchema.getFlowControllerProperties().getName());
+    }
+
+    @Test(expected = IOException.class)
+    public void testUser3WrongCA() throws Exception {
+        assertReturnCode("?class=raspi3", loadSslContext("user3", certificatesDirectory.getParent().resolve("badCert")), 403);
+    }
+
+    @Test
+    public void testUser4() throws Exception {
+        SSLContext sslContext = loadSslContext("user4");
+
+        assertReturnCode("", sslContext, 403);
+        assertReturnCode("?class=raspi2", sslContext, 403);
+        assertReturnCode("?class=raspi3", sslContext, 403);
+    }
+
+    protected SSLContext loadSslContext(String username) throws GeneralSecurityException, IOException {
+        return loadSslContext(username, certificatesDirectory);
+    }
+
+    protected SSLContext loadSslContext(String username, Path directory) throws GeneralSecurityException, IOException {
+        char[] keystorePasswd;
+        try (InputStream inputStream = Files.newInputStream(directory.resolve("CN=" + username + ".password"))) {
+            keystorePasswd = IOUtils.toString(inputStream, StandardCharsets.UTF_8).toCharArray();
+        }
+        return SslContextFactory.createSslContext(
+                directory.resolve("CN=" + username + ".p12").toFile().getAbsolutePath(),
+                keystorePasswd,
+                "PKCS12",
+                certificatesDirectory.resolve("c2").resolve("truststore.jks").toFile().getAbsolutePath(),
+                "badTrustPass".toCharArray(), "jks", SslContextFactory.ClientAuth.NONE, "TLS");
+    }
+
+    protected ConfigSchema assertReturnCode(String query, SSLContext sslContext, int expectedReturnCode) throws Exception {
+        HttpsURLConnection httpsURLConnection = openUrlConnection(C2_URL + query, sslContext);
+        try {
+            assertEquals(expectedReturnCode, httpsURLConnection.getResponseCode());
+            if (expectedReturnCode == 200) {
+                return SchemaLoader.loadConfigSchemaFromYaml(httpsURLConnection.getInputStream());
+            }
+        } finally {
+            httpsURLConnection.disconnect();
+        }
+        return null;
+    }
+
+    protected HttpsURLConnection openUrlConnection(String url, SSLContext sslContext) throws IOException {
+        DockerPort dockerPort = docker.containers().container("squid").port(3128);
+        HttpsURLConnection httpURLConnection = (HttpsURLConnection) new URL(url).openConnection(
+                new Proxy(Proxy.Type.HTTP, new InetSocketAddress(dockerPort.getIp(), dockerPort.getExternalPort())));
+        httpURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
+        return httpURLConnection;
+    }
+
+    @Override
+    protected HttpURLConnection openSuperUserUrlConnection(String url) throws IOException {
+        try {
+            return openUrlConnection(url, loadSslContext("user3"));
+        } catch (GeneralSecurityException e) {
+            throw new IOException(e);
+        }
+    }
+}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/AbstractTestUnsecure.java b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/AbstractTestUnsecure.java
new file mode 100644
index 0000000..27a9683
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/AbstractTestUnsecure.java
@@ -0,0 +1,107 @@
+/*
+ * 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.c2.integration.test;
+
+import com.palantir.docker.compose.DockerComposeRule;
+import com.palantir.docker.compose.connection.Container;
+import com.palantir.docker.compose.connection.DockerPort;
+import org.apache.nifi.minifi.commons.schema.ConfigSchema;
+import org.apache.nifi.minifi.commons.schema.exception.SchemaLoaderException;
+import org.apache.nifi.minifi.commons.schema.serialization.SchemaLoader;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import static org.junit.Assert.assertEquals;
+
+public abstract class AbstractTestUnsecure {
+    protected String c2Url;
+
+    public static String getUnsecureConfigUrl(Container container) {
+        DockerPort dockerPort = container.port(10080);
+        return "http://" + dockerPort.getIp() + ":" + dockerPort.getExternalPort() + "/c2/config";
+    }
+
+    protected void setup(DockerComposeRule docker) {
+        c2Url = getConfigUrl(docker);
+    }
+
+    protected String getConfigUrl(DockerComposeRule docker) {
+        return getUnsecureConfigUrl(docker.containers().container("c2"));
+    }
+
+    @Test
+    public void testCurrentVersion() throws IOException, SchemaLoaderException {
+        ConfigSchema configSchema = getConfigSchema(c2Url + "?class=raspi3");
+        assertEquals(3, configSchema.getVersion());
+        assertEquals("raspi3.v2", configSchema.getFlowControllerProperties().getName());
+    }
+
+    @Test
+    public void testVersion1() throws IOException, SchemaLoaderException {
+        ConfigSchema configSchema = getConfigSchema(c2Url + "?class=raspi3&version=1");
+        assertEquals(3, configSchema.getVersion());
+        assertEquals("raspi3.v1", configSchema.getFlowControllerProperties().getName());
+    }
+
+    @Test
+    public void testVersion2() throws IOException, SchemaLoaderException {
+        ConfigSchema configSchema = getConfigSchema(c2Url + "?class=raspi3&version=2");
+        assertEquals(3, configSchema.getVersion());
+        assertEquals("raspi3.v2", configSchema.getFlowControllerProperties().getName());
+    }
+
+    @Test
+    public void testUnacceptable() throws IOException {
+        HttpURLConnection urlConnection = openSuperUserUrlConnection(c2Url + "?class=raspi3");
+        try {
+            urlConnection.setRequestProperty("Accept", "text/xml");
+            assertEquals(406, urlConnection.getResponseCode());
+        } finally {
+            urlConnection.disconnect();
+        }
+    }
+
+    @Test
+    public void testInvalid() throws IOException {
+        HttpURLConnection urlConnection = openSuperUserUrlConnection(c2Url);
+        try {
+            assertEquals(400, urlConnection.getResponseCode());
+        } finally {
+            urlConnection.disconnect();
+        }
+    }
+
+    public ConfigSchema getConfigSchema(String urlString) throws IOException, SchemaLoaderException {
+        HttpURLConnection urlConnection = openSuperUserUrlConnection(urlString);
+        ConfigSchema configSchema;
+        try (InputStream inputStream = urlConnection.getInputStream()) {
+            configSchema = SchemaLoader.loadConfigSchemaFromYaml(inputStream);
+        } finally {
+            urlConnection.disconnect();
+        }
+        return configSchema;
+    }
+
+    protected HttpURLConnection openSuperUserUrlConnection(String url) throws IOException {
+        return (HttpURLConnection) new URL(url).openConnection();
+    }
+}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/FileSystemCacheProviderSecureTest.java b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/FileSystemCacheProviderSecureTest.java
new file mode 100644
index 0000000..14ad4ae
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/FileSystemCacheProviderSecureTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.c2.integration.test;
+
+import com.palantir.docker.compose.DockerComposeRule;
+import org.apache.nifi.minifi.c2.integration.test.health.HttpsStatusCodeHealthCheck;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class FileSystemCacheProviderSecureTest extends AbstractTestSecure {
+    private static SSLSocketFactory healthCheckSocketFactory;
+
+    // Not annotated as rule because we need to generate certificatesDirectory first
+    public static DockerComposeRule docker = DockerComposeRule.builder()
+            .file("target/test-classes/docker-compose-FileSystemCacheProviderSecureTest.yml")
+            .waitingForServices(Arrays.asList("squid", "c2"),
+                    new HttpsStatusCodeHealthCheck(container -> C2_URL, containers -> containers.get(0), containers -> containers.get(1), () -> healthCheckSocketFactory, 403))
+            .build();
+    private static Path certificatesDirectory;
+    private static SSLContext trustSslContext;
+
+    public FileSystemCacheProviderSecureTest() {
+        super(docker, certificatesDirectory, trustSslContext);
+    }
+
+    /**
+     * Generates certificates with the tls-toolkit and then starts up the docker compose file
+     */
+    @BeforeClass
+    public static void initCertificates() throws Exception {
+        certificatesDirectory = Paths.get(FileSystemCacheProviderSecureTest.class.getClassLoader()
+                .getResource("docker-compose-FileSystemCacheProviderSecureTest.yml").getFile()).getParent().toAbsolutePath().resolve("certificates-FileSystemCacheProviderSecureTest");
+        trustSslContext = initCertificates(certificatesDirectory, Arrays.asList("c2"));
+        healthCheckSocketFactory = trustSslContext.getSocketFactory();
+        docker.before();
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        docker.after();
+    }
+
+    @Before
+    public void setup() {
+        super.setup(docker);
+    }
+}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/FileSystemCacheProviderUnsecureTest.java b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/FileSystemCacheProviderUnsecureTest.java
new file mode 100644
index 0000000..9834d80
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/FileSystemCacheProviderUnsecureTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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.c2.integration.test;
+
+import com.palantir.docker.compose.DockerComposeRule;
+import org.apache.nifi.minifi.c2.integration.test.health.HttpStatusCodeHealthCheck;
+import org.junit.Before;
+import org.junit.ClassRule;
+
+public class FileSystemCacheProviderUnsecureTest extends AbstractTestUnsecure {
+    @ClassRule
+    public static DockerComposeRule docker = DockerComposeRule.builder()
+            .file("target/test-classes/docker-compose-FileSystemProviderUnsecureTest.yml")
+            .waitingForService("c2", new HttpStatusCodeHealthCheck(FileSystemCacheProviderUnsecureTest::getUnsecureConfigUrl, 400))
+            .build();
+
+    @Before
+    public void setup() {
+        super.setup(docker);
+    }
+}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/NiFiRestConfigurationProviderSecureTest.java b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/NiFiRestConfigurationProviderSecureTest.java
new file mode 100644
index 0000000..9ce4a9f
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/NiFiRestConfigurationProviderSecureTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.c2.integration.test;
+
+import com.palantir.docker.compose.DockerComposeRule;
+import org.apache.nifi.minifi.c2.integration.test.health.HttpsStatusCodeHealthCheck;
+import org.apache.nifi.security.util.SslContextFactory;
+import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandalone;
+import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
+import org.bouncycastle.util.io.pem.PemWriter;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+
+public class NiFiRestConfigurationProviderSecureTest extends AbstractTestSecure {
+    private static SSLSocketFactory healthCheckSocketFactory;
+    private static Path certificatesDirectory;
+    private static SSLContext trustSslContext;
+
+    // Not annotated as rule because we need to generate certificatesDirectory first
+    public static DockerComposeRule docker = DockerComposeRule.builder()
+            .file("target/test-classes/docker-compose-NiFiRestConfigurationProviderSecureTest.yml")
+            .waitingForServices(Arrays.asList("squid", "mocknifi"),
+                    new HttpsStatusCodeHealthCheck(container -> "https://mocknifi:8443/", containers -> containers.get(0), containers -> containers.get(1), () -> {
+                        Path c2 = certificatesDirectory.resolve("c2");
+                        try {
+                            return SslContextFactory.createSslContext(c2.resolve("keystore.jks").toFile().getAbsolutePath(), "badKeystorePass".toCharArray(), "badKeyPass".toCharArray(), "JKS",
+                                    c2.resolve("truststore.jks").toFile().getAbsolutePath(), "badTrustPass".toCharArray(), "JKS", SslContextFactory.ClientAuth.NONE, "TLS").getSocketFactory();
+                        } catch (Exception e) {
+                            throw new RuntimeException(e);
+                        }
+                    }, 200))
+            .waitingForServices(Arrays.asList("squid", "c2"),
+                    new HttpsStatusCodeHealthCheck(container -> C2_URL, containers -> containers.get(0), containers -> containers.get(1), () -> healthCheckSocketFactory, 403))
+            .build();
+
+    public NiFiRestConfigurationProviderSecureTest() {
+        super(docker, certificatesDirectory, trustSslContext);
+    }
+
+    /**
+     * Generates certificates with the tls-toolkit and then starts up the docker compose file
+     */
+    @BeforeClass
+    public static void initCertificates() throws Exception {
+        certificatesDirectory = Paths.get(NiFiRestConfigurationProviderSecureTest.class.getClassLoader()
+                .getResource("docker-compose-NiFiRestConfigurationProviderSecureTest.yml").getFile()).getParent().toAbsolutePath().resolve("certificates-NiFiRestConfigurationProviderSecureTest");
+        trustSslContext = initCertificates(certificatesDirectory, Arrays.asList("c2", "mocknifi"));
+        healthCheckSocketFactory = trustSslContext.getSocketFactory();
+
+        KeyStore mockNiFiKeyStore = KeyStore.getInstance("JKS");
+        try (InputStream inputStream = Files.newInputStream(certificatesDirectory.resolve("mocknifi").resolve("keystore.jks"))) {
+            mockNiFiKeyStore.load(inputStream, "badKeystorePass".toCharArray());
+        }
+        try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(Files.newOutputStream(certificatesDirectory.resolve("mocknifi").resolve("cert.pem"))))) {
+            pemWriter.writeObject(new JcaMiscPEMGenerator(mockNiFiKeyStore.getKey(TlsToolkitStandalone.NIFI_KEY, "badKeyPass".toCharArray())));
+            for (Certificate certificate : mockNiFiKeyStore.getCertificateChain(TlsToolkitStandalone.NIFI_KEY)) {
+                pemWriter.writeObject(new JcaMiscPEMGenerator(certificate));
+            }
+        }
+
+        KeyStore mockNiFiTrustStore = KeyStore.getInstance("JKS");
+        try (InputStream inputStream = Files.newInputStream(certificatesDirectory.resolve("mocknifi").resolve("truststore.jks"))) {
+            mockNiFiTrustStore.load(inputStream, "badTrustPass".toCharArray());
+        }
+        try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(Files.newOutputStream(certificatesDirectory.resolve("mocknifi").resolve("ca.pem"))))) {
+            pemWriter.writeObject(new JcaMiscPEMGenerator(mockNiFiTrustStore.getCertificate(TlsToolkitStandalone.NIFI_CERT)));
+        }
+
+        docker.before();
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        docker.after();
+    }
+
+    @Before
+    public void setup() {
+        super.setup(docker);
+    }
+}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/NiFiRestConfigurationProviderUnsecureTest.java b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/NiFiRestConfigurationProviderUnsecureTest.java
new file mode 100644
index 0000000..b444cc1
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/NiFiRestConfigurationProviderUnsecureTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.c2.integration.test;
+
+import com.palantir.docker.compose.DockerComposeRule;
+import com.palantir.docker.compose.connection.waiting.HealthChecks;
+import org.apache.nifi.minifi.c2.integration.test.health.HttpStatusCodeHealthCheck;
+import org.junit.Before;
+import org.junit.ClassRule;
+
+public class NiFiRestConfigurationProviderUnsecureTest extends AbstractTestUnsecure {
+    @ClassRule
+    public static DockerComposeRule docker = DockerComposeRule.builder()
+            .file("target/test-classes/docker-compose-NiFiRestConfigurationProviderUnsecureTest.yml")
+            .waitingForService("mocknifi", HealthChecks.toRespond2xxOverHttp(8080,
+                    dockerPort -> "http://" + dockerPort.getIp() + ":" + dockerPort.getExternalPort() + "/"))
+            .waitingForService("c2", new HttpStatusCodeHealthCheck(FileSystemCacheProviderUnsecureTest::getUnsecureConfigUrl, 400))
+            .build();
+
+    @Before
+    public void setup() {
+        super.setup(docker);
+    }
+}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/health/HttpStatusCodeHealthCheck.java b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/health/HttpStatusCodeHealthCheck.java
new file mode 100644
index 0000000..f639d96
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/health/HttpStatusCodeHealthCheck.java
@@ -0,0 +1,55 @@
+/*
+ * 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.c2.integration.test.health;
+
+import com.palantir.docker.compose.connection.Container;
+import com.palantir.docker.compose.connection.waiting.HealthCheck;
+import com.palantir.docker.compose.connection.waiting.SuccessOrFailure;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.function.Function;
+
+public class HttpStatusCodeHealthCheck implements HealthCheck<Container> {
+    private final Function<Container, String> urlFunction;
+    private final int expected;
+
+    public HttpStatusCodeHealthCheck(Function<Container, String> urlFunction, int expected) {
+        this.urlFunction = urlFunction;
+        this.expected = expected;
+    }
+
+    @Override
+    public SuccessOrFailure isHealthy(Container target) {
+        try {
+            int responseCode = openConnection(urlFunction.apply(target)).getResponseCode();
+            if (responseCode == expected) {
+                return SuccessOrFailure.success();
+            } else {
+                return SuccessOrFailure.failure("Expected Status code " + expected + " got " + responseCode);
+            }
+        } catch (IOException e) {
+            return SuccessOrFailure.failure("Expected Status code " + expected + " got IOException " + e.getMessage());
+        }
+    }
+
+    protected HttpURLConnection openConnection(String url) throws IOException {
+        return ((HttpURLConnection) new URL(url).openConnection());
+    }
+}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/health/HttpsStatusCodeHealthCheck.java b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/health/HttpsStatusCodeHealthCheck.java
new file mode 100644
index 0000000..9842e78
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/health/HttpsStatusCodeHealthCheck.java
@@ -0,0 +1,70 @@
+/*
+ * 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.c2.integration.test.health;
+
+import com.palantir.docker.compose.connection.Container;
+import com.palantir.docker.compose.connection.DockerPort;
+import com.palantir.docker.compose.connection.waiting.HealthCheck;
+import com.palantir.docker.compose.connection.waiting.SuccessOrFailure;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.URL;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class HttpsStatusCodeHealthCheck implements HealthCheck<List<Container>> {
+    private final Function<Container, String> urlFunction;
+    private final Function<List<Container>, Container> proxyExtractor;
+    private final Function<List<Container>, Container> serverExtractor;
+    private final Supplier<SSLSocketFactory> sslSocketFactorySupplier;
+    private final int expected;
+
+    public HttpsStatusCodeHealthCheck(Function<Container, String> urlFunction, Function<List<Container>, Container> proxyExtractor,
+                                      Function<List<Container>, Container> serverExtractor, Supplier<SSLSocketFactory> sslSocketFactorySupplier, int expected) {
+        this.urlFunction = urlFunction;
+        this.proxyExtractor = proxyExtractor;
+        this.serverExtractor = serverExtractor;
+        this.sslSocketFactorySupplier = sslSocketFactorySupplier;
+        this.expected = expected;
+    }
+
+    @Override
+    public SuccessOrFailure isHealthy(List<Container> target) {
+
+        return new HttpStatusCodeHealthCheck(urlFunction, expected) {
+            @Override
+            protected HttpURLConnection openConnection(String url) throws IOException {
+                DockerPort dockerPort = proxyExtractor.apply(target).port(3128);
+                return getHttpURLConnection(url, sslSocketFactorySupplier.get(), dockerPort.getIp(), dockerPort.getExternalPort());
+            }
+        }.isHealthy(serverExtractor.apply(target));
+    }
+
+    public static HttpURLConnection getHttpURLConnection(String url, SSLSocketFactory sslSocketFactory, String proxyHostname, int proxyPort) throws IOException {
+        HttpsURLConnection httpURLConnection = (HttpsURLConnection) new URL(url).openConnection(
+                new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHostname, proxyPort)));
+        httpURLConnection.setSSLSocketFactory(sslSocketFactory);
+        return httpURLConnection;
+    }
+}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure-rest/conf/minifi-c2-context.xml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure-rest/conf/minifi-c2-context.xml
new file mode 100644
index 0000000..d648d88
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure-rest/conf/minifi-c2-context.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<beans default-lazy-init="true"
+       xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
+    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
+    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
+
+    <bean id="configService" class="org.apache.nifi.minifi.c2.service.ConfigService" scope="singleton">
+        <constructor-arg>
+            <list>
+                <bean class="org.apache.nifi.minifi.c2.provider.nifi.rest.NiFiRestConfigurationProvider">
+                    <constructor-arg>
+                        <bean class="org.apache.nifi.minifi.c2.cache.filesystem.FileSystemConfigurationCache">
+                            <constructor-arg>
+                                <value>./cache</value>
+                            </constructor-arg>
+                            <constructor-arg>
+                                <value>${class}/${class}</value>
+                            </constructor-arg>
+                        </bean>
+                    </constructor-arg>
+                    <constructor-arg>
+                        <value>https://mocknifi:8443/nifi-api</value>
+                    </constructor-arg>
+                </bean>
+            </list>
+        </constructor-arg>
+        <constructor-arg>
+            <bean class="org.apache.nifi.minifi.c2.security.authorization.GrantedAuthorityAuthorizer">
+                <constructor-arg value="classpath:authorizations.yaml"/>
+            </bean>
+        </constructor-arg>
+    </bean>
+</beans>
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure/conf/authorities.yaml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure/conf/authorities.yaml
new file mode 100644
index 0000000..c137c00
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure/conf/authorities.yaml
@@ -0,0 +1,21 @@
+# 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.
+
+CN=user1:
+  - CLASS_RASPI_2
+CN=user2:
+  - CLASS_RASPI_3
+CN=user3:
+  - ROLE_SUPERUSER
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure/conf/authorizations.yaml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure/conf/authorizations.yaml
new file mode 100644
index 0000000..fec97d5
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure/conf/authorizations.yaml
@@ -0,0 +1,30 @@
+# 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.
+
+Default Action: deny
+Paths:
+  /c2/config:
+    Default Action: deny
+    Actions:
+    - Authorization: CLASS_RASPI_3
+      Query Parameters:
+        class: raspi3
+      Action: allow
+    - Authorization: CLASS_RASPI_2
+      Query Parameters:
+        class: raspi2
+      Action: allow
+    - Authorization: ROLE_SUPERUSER
+      Action: allow
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure/conf/c2.properties b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure/conf/c2.properties
new file mode 100644
index 0000000..b683350
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure/conf/c2.properties
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+minifi.c2.server.port=10443
+
+minifi.c2.server.secure=true
+minifi.c2.server.keystore=./conf/keystore.jks
+minifi.c2.server.keystoreType=JKS
+minifi.c2.server.keystorePasswd=badKeystorePass
+minifi.c2.server.keyPasswd=badKeyPass
+minifi.c2.server.truststore=./conf/truststore.jks
+minifi.c2.server.truststoreType=JKS
+minifi.c2.server.truststorePasswd=badTrustPass
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-unsecure-rest/conf/minifi-c2-context.xml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-unsecure-rest/conf/minifi-c2-context.xml
new file mode 100644
index 0000000..f2e4eee
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-unsecure-rest/conf/minifi-c2-context.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ 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.
+  -->
+<beans default-lazy-init="true"
+       xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
+    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
+    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
+
+    <bean id="configService" class="org.apache.nifi.minifi.c2.service.ConfigService" scope="singleton">
+        <constructor-arg>
+            <list>
+                <bean class="org.apache.nifi.minifi.c2.provider.nifi.rest.NiFiRestConfigurationProvider">
+                    <constructor-arg>
+                        <bean class="org.apache.nifi.minifi.c2.cache.filesystem.FileSystemConfigurationCache">
+                            <constructor-arg>
+                                <value>./cache</value>
+                            </constructor-arg>
+                            <constructor-arg>
+                                <value>${class}/${class}</value>
+                            </constructor-arg>
+                        </bean>
+                    </constructor-arg>
+                    <constructor-arg>
+                        <value>http://mocknifi:8080/nifi-api</value>
+                    </constructor-arg>
+                </bean>
+            </list>
+        </constructor-arg>
+        <constructor-arg>
+            <bean class="org.apache.nifi.minifi.c2.security.authorization.GrantedAuthorityAuthorizer">
+                <constructor-arg value="classpath:authorizations.yaml"/>
+            </bean>
+        </constructor-arg>
+    </bean>
+</beans>
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi2/config.yml.v1 b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi2/config.yml.v1
new file mode 100644
index 0000000..116426f
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi2/config.yml.v1
@@ -0,0 +1,63 @@
+# 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.
+
+MiNiFi Config Version: 3
+Flow Controller:
+  name: raspi2.v1
+  comment: ''
+Core Properties:
+  flow controller graceful shutdown period: 10 sec
+  flow service write delay interval: 500 ms
+  administrative yield duration: 30 sec
+  bored yield duration: 10 millis
+  max concurrent threads: 1
+FlowFile Repository:
+  partitions: 256
+  checkpoint interval: 2 mins
+  always sync: false
+  Swap:
+    threshold: 20000
+    in period: 5 sec
+    in threads: 1
+    out period: 5 sec
+    out threads: 4
+Content Repository:
+  content claim max appendable size: 10 MB
+  content claim max flow files: 100
+  always sync: false
+Provenance Repository:
+  provenance rollover time: 1 min
+Component Status Repository:
+  buffer size: 1440
+  snapshot frequency: 1 min
+Security Properties:
+  keystore: ''
+  keystore type: ''
+  keystore password: ''
+  key password: ''
+  truststore: ''
+  truststore type: ''
+  truststore password: ''
+  ssl protocol: ''
+  Sensitive Props:
+    key: ''
+    algorithm: PBEWITHMD5AND256BITAES-CBC-OPENSSL
+    provider: BC
+Processors: []
+Process Groups: []
+Funnels: []
+Connections: []
+Remote Process Groups: []
+NiFi Properties Overrides: {}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v1 b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v1
new file mode 100644
index 0000000..690cdaa
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v1
@@ -0,0 +1,63 @@
+# 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.
+
+MiNiFi Config Version: 3
+Flow Controller:
+  name: raspi3.v1
+  comment: ''
+Core Properties:
+  flow controller graceful shutdown period: 10 sec
+  flow service write delay interval: 500 ms
+  administrative yield duration: 30 sec
+  bored yield duration: 10 millis
+  max concurrent threads: 1
+FlowFile Repository:
+  partitions: 256
+  checkpoint interval: 2 mins
+  always sync: false
+  Swap:
+    threshold: 20000
+    in period: 5 sec
+    in threads: 1
+    out period: 5 sec
+    out threads: 4
+Content Repository:
+  content claim max appendable size: 10 MB
+  content claim max flow files: 100
+  always sync: false
+Provenance Repository:
+  provenance rollover time: 1 min
+Component Status Repository:
+  buffer size: 1440
+  snapshot frequency: 1 min
+Security Properties:
+  keystore: ''
+  keystore type: ''
+  keystore password: ''
+  key password: ''
+  truststore: ''
+  truststore type: ''
+  truststore password: ''
+  ssl protocol: ''
+  Sensitive Props:
+    key: ''
+    algorithm: PBEWITHMD5AND256BITAES-CBC-OPENSSL
+    provider: BC
+Processors: []
+Process Groups: []
+Funnels: []
+Connections: []
+Remote Process Groups: []
+NiFi Properties Overrides: {}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v2 b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v2
new file mode 100644
index 0000000..14750b6
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v2
@@ -0,0 +1,63 @@
+# 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.
+
+MiNiFi Config Version: 3
+Flow Controller:
+  name: raspi3.v2
+  comment: ''
+Core Properties:
+  flow controller graceful shutdown period: 10 sec
+  flow service write delay interval: 500 ms
+  administrative yield duration: 30 sec
+  bored yield duration: 10 millis
+  max concurrent threads: 1
+FlowFile Repository:
+  partitions: 256
+  checkpoint interval: 2 mins
+  always sync: false
+  Swap:
+    threshold: 20000
+    in period: 5 sec
+    in threads: 1
+    out period: 5 sec
+    out threads: 4
+Content Repository:
+  content claim max appendable size: 10 MB
+  content claim max flow files: 100
+  always sync: false
+Provenance Repository:
+  provenance rollover time: 1 min
+Component Status Repository:
+  buffer size: 1440
+  snapshot frequency: 1 min
+Security Properties:
+  keystore: ''
+  keystore type: ''
+  keystore password: ''
+  key password: ''
+  truststore: ''
+  truststore type: ''
+  truststore password: ''
+  ssl protocol: ''
+  Sensitive Props:
+    key: ''
+    algorithm: PBEWITHMD5AND256BITAES-CBC-OPENSSL
+    provider: BC
+Processors: []
+Process Groups: []
+Funnels: []
+Connections: []
+Remote Process Groups: []
+NiFi Properties Overrides: {}
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-FileSystemCacheProviderSecureTest.yml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-FileSystemCacheProviderSecureTest.yml
new file mode 100644
index 0000000..27768d4
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-FileSystemCacheProviderSecureTest.yml
@@ -0,0 +1,40 @@
+# 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.
+
+version: "2"
+
+services:
+  c2:
+    image: apacheminific2:${minifi.c2.version}
+    ports:
+      - "10443"
+    hostname: c2
+    volumes:
+      - ./c2/files:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/files
+
+      - ./c2-secure/conf/c2.properties:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/c2.properties
+      - ./c2-secure/conf/authorities.yaml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/authorities.yaml
+      - ./c2-secure/conf/authorizations.yaml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/authorizations.yaml
+
+      - ./certificates-FileSystemCacheProviderSecureTest/c2/keystore.jks:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/keystore.jks
+      - ./certificates-FileSystemCacheProviderSecureTest/c2/truststore.jks:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/truststore.jks
+
+  squid:
+    image: chrisdaish/squid
+    ports:
+      - "3128"
+    hostname: squid
+    volumes:
+      - ./squid/squid.conf:/etc/squid/squid.conf
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-FileSystemProviderUnsecureTest.yml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-FileSystemProviderUnsecureTest.yml
new file mode 100644
index 0000000..fe4f8ec
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-FileSystemProviderUnsecureTest.yml
@@ -0,0 +1,25 @@
+# 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.
+
+version: "2"
+
+services:
+  c2:
+    image: apacheminific2:${minifi.c2.version}
+    ports:
+      - "10080"
+    hostname: c2
+    volumes:
+      - ./c2/files:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/files
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-NiFiRestConfigurationProviderSecureTest.yml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-NiFiRestConfigurationProviderSecureTest.yml
new file mode 100644
index 0000000..8665b7f
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-NiFiRestConfigurationProviderSecureTest.yml
@@ -0,0 +1,55 @@
+# 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.
+
+version: "2"
+
+services:
+  c2:
+    image: apacheminific2:${minifi.c2.version}
+    ports:
+      - "10443"
+    hostname: c2
+    volumes:
+      - ./c2-secure/conf/c2.properties:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/c2.properties
+      - ./c2-secure/conf/authorities.yaml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/authorities.yaml
+      - ./c2-secure/conf/authorizations.yaml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/authorizations.yaml
+      - ./c2-secure-rest/conf/minifi-c2-context.xml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/minifi-c2-context.xml
+
+      - ./certificates-NiFiRestConfigurationProviderSecureTest/c2/keystore.jks:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/keystore.jks
+      - ./certificates-NiFiRestConfigurationProviderSecureTest/c2/truststore.jks:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/truststore.jks
+
+  squid:
+    image: chrisdaish/squid
+    ports:
+      - "3128"
+    hostname: squid
+    volumes:
+      - ./squid/squid.conf:/etc/squid/squid.conf
+
+  mocknifi:
+      image: python:2
+      ports:
+        - "8443"
+      hostname: mocknifi
+      volumes:
+        - ./mocknifi-secure/server.py:/root/server.py
+        - ./mocknifi/www:/var/www
+
+        - ./certificates-NiFiRestConfigurationProviderSecureTest/mocknifi/cert.pem:/root/cert.pem
+        - ./certificates-NiFiRestConfigurationProviderSecureTest/mocknifi/ca.pem:/root/ca.pem
+      working_dir: /var/www
+      entrypoint:
+        - python
+        - /root/server.py
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-NiFiRestConfigurationProviderUnsecureTest.yml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-NiFiRestConfigurationProviderUnsecureTest.yml
new file mode 100644
index 0000000..a80428c
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-NiFiRestConfigurationProviderUnsecureTest.yml
@@ -0,0 +1,37 @@
+# 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.
+
+version: "2"
+
+services:
+  c2:
+    image: apacheminific2:${minifi.c2.version}
+    ports:
+      - "10080"
+    hostname: c2
+    volumes:
+      - ./c2-unsecure-rest/conf/minifi-c2-context.xml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/minifi-c2-context.xml
+  mocknifi:
+    image: python:2
+    ports:
+      - "8080"
+    hostname: mocknifi
+    volumes:
+      - ./mocknifi-unsecure/server.py:/root/server.py
+      - ./mocknifi/www:/var/www
+    working_dir: /var/www
+    entrypoint:
+      - python
+      - /root/server.py
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi-secure/server.py b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi-secure/server.py
new file mode 100644
index 0000000..fe869a8
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi-secure/server.py
@@ -0,0 +1,48 @@
+# 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.
+
+#!/usr/bin/env python
+
+import logging
+
+from argparse import ArgumentParser
+from BaseHTTPServer import HTTPServer
+from os import chdir
+from SimpleHTTPServer import SimpleHTTPRequestHandler
+from SocketServer import ThreadingMixIn
+from ssl import CERT_REQUIRED, wrap_socket
+
+# Needs to be threaded or health check hangs the server
+class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
+  pass
+
+if __name__ == '__main__':
+  logging.basicConfig(level=logging.DEBUG)
+  parser = ArgumentParser(description='Serve up directory with ssl')
+  parser.add_argument('--dir', default='/var/www')
+  parser.add_argument('--port', type=int, default=8443)
+  parser.add_argument('--cert', default='/root/cert.pem')
+  parser.add_argument('--ca', default='/root/ca.pem')
+
+  logging.debug('About to parse arguments')
+  args = parser.parse_args()
+
+  logging.debug('Serving directory ' + args.dir + ' via HTTPS at port ' + str(args.port))
+
+  chdir(args.dir)
+
+  server = ThreadedHTTPServer(('', args.port), SimpleHTTPRequestHandler)
+  server.socket = wrap_socket(server.socket, certfile=args.cert, ca_certs=args.ca, server_side=True, cert_reqs=CERT_REQUIRED)
+  server.serve_forever()
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi-unsecure/server.py b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi-unsecure/server.py
new file mode 100644
index 0000000..82d5f6a
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi-unsecure/server.py
@@ -0,0 +1,44 @@
+# 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.
+
+#!/usr/bin/env python
+
+import logging
+
+from argparse import ArgumentParser
+from BaseHTTPServer import HTTPServer
+from os import chdir
+from SimpleHTTPServer import SimpleHTTPRequestHandler
+from SocketServer import ThreadingMixIn
+
+# Needs to be threaded or health check hangs the server
+class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
+  pass
+
+if __name__ == '__main__':
+  logging.basicConfig(level=logging.DEBUG)
+  parser = ArgumentParser(description='Serve up directory over http')
+  parser.add_argument('--dir', default='/var/www')
+  parser.add_argument('--port', type=int, default=8080)
+
+  logging.debug('About to parse arguments')
+  args = parser.parse_args()
+
+  logging.debug('Serving directory ' + args.dir + ' via HTTP at port ' + str(args.port))
+
+  chdir(args.dir)
+
+  server = ThreadedHTTPServer(('', args.port), SimpleHTTPRequestHandler)
+  server.serve_forever()
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/flow/templates b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/flow/templates
new file mode 100644
index 0000000..496665b
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/flow/templates
@@ -0,0 +1,53 @@
+{
+    "generated": "15:41:45 EDT",
+    "templates": [
+        {
+            "id": "f080ec50-ca32-4b36-8453-5a7145bec4c5",
+            "permissions": {
+                "canRead": true,
+                "canWrite": true
+            },
+            "template": {
+                "description": "",
+                "encoding-version": "1.0",
+                "groupId": "8f8eda5e-015a-1000-a9c1-b7e4fe10ae83",
+                "id": "f080ec50-ca32-4b36-8453-5a7145bec4c5",
+                "name": "raspi3.v1",
+                "timestamp": "03/07/2017 11:13:03 EST",
+                "uri": "http://localhost:8080/nifi-api/templates/f080ec50-ca32-4b36-8453-5a7145bec4c5"
+            }
+        },
+        {
+            "id": "87048385-a6ca-42fe-b2d8-6a563cedd036",
+            "permissions": {
+                "canRead": true,
+                "canWrite": true
+            },
+            "template": {
+                "description": "",
+                "encoding-version": "1.0",
+                "groupId": "8f8eda5e-015a-1000-a9c1-b7e4fe10ae83",
+                "id": "87048385-a6ca-42fe-b2d8-6a563cedd036",
+                "name": "raspi2.v1",
+                "timestamp": "03/17/2017 15:41:33 EDT",
+                "uri": "http://localhost:8080/nifi-api/templates/87048385-a6ca-42fe-b2d8-6a563cedd036"
+            }
+        },
+        {
+            "id": "dd737a3e-333e-40df-a0bc-d7e28c8e6843",
+            "permissions": {
+                "canRead": true,
+                "canWrite": true
+            },
+            "template": {
+                "description": "",
+                "encoding-version": "1.0",
+                "groupId": "8f8eda5e-015a-1000-a9c1-b7e4fe10ae83",
+                "id": "dd737a3e-333e-40df-a0bc-d7e28c8e6843",
+                "name": "raspi3.v2",
+                "timestamp": "03/17/2017 13:22:58 EDT",
+                "uri": "http://localhost:8080/nifi-api/templates/dd737a3e-333e-40df-a0bc-d7e28c8e6843"
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/templates/87048385-a6ca-42fe-b2d8-6a563cedd036/download b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/templates/87048385-a6ca-42fe-b2d8-6a563cedd036/download
new file mode 100644
index 0000000..5d36113
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/templates/87048385-a6ca-42fe-b2d8-6a563cedd036/download
@@ -0,0 +1,203 @@
+<?xml version="1.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.
+-->
+<template encoding-version="1.0">
+  <description></description>
+  <groupId>8f8eda5e-015a-1000-a9c1-b7e4fe10ae83</groupId>
+  <name>raspi2.v1</name>
+  <snippet>
+    <connections>
+      <id>8f96f2a9-015a-1000-0000-000000000000</id>
+      <parentGroupId>8f8eda5e-015a-1000-0000-000000000000</parentGroupId>
+      <backPressureDataSizeThreshold>1 GB</backPressureDataSizeThreshold>
+      <backPressureObjectThreshold>10000</backPressureObjectThreshold>
+      <destination>
+        <groupId>8f8eda5e-015a-1000-0000-000000000000</groupId>
+        <id>8f96bf68-015a-1000-0000-000000000000</id>
+        <type>PROCESSOR</type>
+      </destination>
+      <flowFileExpiration>0 sec</flowFileExpiration>
+      <labelIndex>1</labelIndex>
+      <name></name>
+      <selectedRelationships>success</selectedRelationships>
+      <source>
+        <groupId>8f8eda5e-015a-1000-0000-000000000000</groupId>
+        <id>8f96e313-015a-1000-0000-000000000000</id>
+        <type>PROCESSOR</type>
+      </source>
+      <zIndex>0</zIndex>
+    </connections>
+    <processors>
+      <id>8f96bf68-015a-1000-0000-000000000000</id>
+      <parentGroupId>8f8eda5e-015a-1000-0000-000000000000</parentGroupId>
+      <position>
+        <x>14.0</x>
+        <y>253.0</y>
+      </position>
+      <config>
+        <bulletinLevel>WARN</bulletinLevel>
+        <comments></comments>
+        <concurrentlySchedulableTaskCount>1</concurrentlySchedulableTaskCount>
+        <descriptors>
+          <entry>
+            <key>Log Level</key>
+            <value>
+              <name>Log Level</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Log Payload</key>
+            <value>
+              <name>Log Payload</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Attributes to Log</key>
+            <value>
+              <name>Attributes to Log</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Attributes to Ignore</key>
+            <value>
+              <name>Attributes to Ignore</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Log prefix</key>
+            <value>
+              <name>Log prefix</name>
+            </value>
+          </entry>
+        </descriptors>
+        <executionNode>ALL</executionNode>
+        <lossTolerant>false</lossTolerant>
+        <penaltyDuration>30 sec</penaltyDuration>
+        <properties>
+          <entry>
+            <key>Log Level</key>
+            <value>info</value>
+          </entry>
+          <entry>
+            <key>Log Payload</key>
+            <value>true</value>
+          </entry>
+          <entry>
+            <key>Attributes to Log</key>
+          </entry>
+          <entry>
+            <key>Attributes to Ignore</key>
+          </entry>
+          <entry>
+            <key>Log prefix</key>
+          </entry>
+        </properties>
+        <runDurationMillis>0</runDurationMillis>
+        <schedulingPeriod>0 sec</schedulingPeriod>
+        <schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
+        <yieldDuration>1 sec</yieldDuration>
+      </config>
+      <name>LogAttribute</name>
+      <relationships>
+        <autoTerminate>true</autoTerminate>
+        <name>success</name>
+      </relationships>
+      <style></style>
+      <type>org.apache.nifi.processors.standard.LogAttribute</type>
+    </processors>
+    <processors>
+      <id>8f96e313-015a-1000-0000-000000000000</id>
+      <parentGroupId>8f8eda5e-015a-1000-0000-000000000000</parentGroupId>
+      <position>
+        <x>0.0</x>
+        <y>0.0</y>
+      </position>
+      <config>
+        <bulletinLevel>WARN</bulletinLevel>
+        <comments></comments>
+        <concurrentlySchedulableTaskCount>1</concurrentlySchedulableTaskCount>
+        <descriptors>
+          <entry>
+            <key>File Size</key>
+            <value>
+              <name>File Size</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Batch Size</key>
+            <value>
+              <name>Batch Size</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Data Format</key>
+            <value>
+              <name>Data Format</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Unique FlowFiles</key>
+            <value>
+              <name>Unique FlowFiles</name>
+            </value>
+          </entry>
+          <entry>
+            <key>generate-ff-custom-text</key>
+            <value>
+              <name>generate-ff-custom-text</name>
+            </value>
+          </entry>
+        </descriptors>
+        <executionNode>ALL</executionNode>
+        <lossTolerant>false</lossTolerant>
+        <penaltyDuration>30 sec</penaltyDuration>
+        <properties>
+          <entry>
+            <key>File Size</key>
+            <value>0B</value>
+          </entry>
+          <entry>
+            <key>Batch Size</key>
+            <value>1</value>
+          </entry>
+          <entry>
+            <key>Data Format</key>
+            <value>Text</value>
+          </entry>
+          <entry>
+            <key>Unique FlowFiles</key>
+            <value>false</value>
+          </entry>
+          <entry>
+            <key>generate-ff-custom-text</key>
+            <value>abcdefg</value>
+          </entry>
+        </properties>
+        <runDurationMillis>0</runDurationMillis>
+        <schedulingPeriod>0 sec</schedulingPeriod>
+        <schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
+        <yieldDuration>1 sec</yieldDuration>
+      </config>
+      <name>GenerateFlowFile</name>
+      <relationships>
+        <autoTerminate>false</autoTerminate>
+        <name>success</name>
+      </relationships>
+      <style></style>
+      <type>org.apache.nifi.processors.standard.GenerateFlowFile</type>
+    </processors>
+  </snippet>
+  <timestamp>03/17/2017 15:41:33 EDT</timestamp>
+</template>
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/templates/dd737a3e-333e-40df-a0bc-d7e28c8e6843/download b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/templates/dd737a3e-333e-40df-a0bc-d7e28c8e6843/download
new file mode 100644
index 0000000..6b364d0
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/templates/dd737a3e-333e-40df-a0bc-d7e28c8e6843/download
@@ -0,0 +1,203 @@
+<?xml version="1.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.
+-->
+<template encoding-version="1.0">
+  <description></description>
+  <groupId>8f8eda5e-015a-1000-a9c1-b7e4fe10ae83</groupId>
+  <name>raspi3.v2</name>
+  <snippet>
+    <connections>
+      <id>8f96f2a9-015a-1000-0000-000000000000</id>
+      <parentGroupId>8f8eda5e-015a-1000-0000-000000000000</parentGroupId>
+      <backPressureDataSizeThreshold>1 GB</backPressureDataSizeThreshold>
+      <backPressureObjectThreshold>10000</backPressureObjectThreshold>
+      <destination>
+        <groupId>8f8eda5e-015a-1000-0000-000000000000</groupId>
+        <id>8f96bf68-015a-1000-0000-000000000000</id>
+        <type>PROCESSOR</type>
+      </destination>
+      <flowFileExpiration>0 sec</flowFileExpiration>
+      <labelIndex>1</labelIndex>
+      <name></name>
+      <selectedRelationships>success</selectedRelationships>
+      <source>
+        <groupId>8f8eda5e-015a-1000-0000-000000000000</groupId>
+        <id>8f96e313-015a-1000-0000-000000000000</id>
+        <type>PROCESSOR</type>
+      </source>
+      <zIndex>0</zIndex>
+    </connections>
+    <processors>
+      <id>8f96bf68-015a-1000-0000-000000000000</id>
+      <parentGroupId>8f8eda5e-015a-1000-0000-000000000000</parentGroupId>
+      <position>
+        <x>14.0</x>
+        <y>253.0</y>
+      </position>
+      <config>
+        <bulletinLevel>WARN</bulletinLevel>
+        <comments></comments>
+        <concurrentlySchedulableTaskCount>1</concurrentlySchedulableTaskCount>
+        <descriptors>
+          <entry>
+            <key>Log Level</key>
+            <value>
+              <name>Log Level</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Log Payload</key>
+            <value>
+              <name>Log Payload</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Attributes to Log</key>
+            <value>
+              <name>Attributes to Log</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Attributes to Ignore</key>
+            <value>
+              <name>Attributes to Ignore</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Log prefix</key>
+            <value>
+              <name>Log prefix</name>
+            </value>
+          </entry>
+        </descriptors>
+        <executionNode>ALL</executionNode>
+        <lossTolerant>false</lossTolerant>
+        <penaltyDuration>30 sec</penaltyDuration>
+        <properties>
+          <entry>
+            <key>Log Level</key>
+            <value>info</value>
+          </entry>
+          <entry>
+            <key>Log Payload</key>
+            <value>true</value>
+          </entry>
+          <entry>
+            <key>Attributes to Log</key>
+          </entry>
+          <entry>
+            <key>Attributes to Ignore</key>
+          </entry>
+          <entry>
+            <key>Log prefix</key>
+          </entry>
+        </properties>
+        <runDurationMillis>0</runDurationMillis>
+        <schedulingPeriod>0 sec</schedulingPeriod>
+        <schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
+        <yieldDuration>1 sec</yieldDuration>
+      </config>
+      <name>LogAttribute</name>
+      <relationships>
+        <autoTerminate>true</autoTerminate>
+        <name>success</name>
+      </relationships>
+      <style></style>
+      <type>org.apache.nifi.processors.standard.LogAttribute</type>
+    </processors>
+    <processors>
+      <id>8f96e313-015a-1000-0000-000000000000</id>
+      <parentGroupId>8f8eda5e-015a-1000-0000-000000000000</parentGroupId>
+      <position>
+        <x>0.0</x>
+        <y>0.0</y>
+      </position>
+      <config>
+        <bulletinLevel>WARN</bulletinLevel>
+        <comments></comments>
+        <concurrentlySchedulableTaskCount>1</concurrentlySchedulableTaskCount>
+        <descriptors>
+          <entry>
+            <key>File Size</key>
+            <value>
+              <name>File Size</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Batch Size</key>
+            <value>
+              <name>Batch Size</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Data Format</key>
+            <value>
+              <name>Data Format</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Unique FlowFiles</key>
+            <value>
+              <name>Unique FlowFiles</name>
+            </value>
+          </entry>
+          <entry>
+            <key>generate-ff-custom-text</key>
+            <value>
+              <name>generate-ff-custom-text</name>
+            </value>
+          </entry>
+        </descriptors>
+        <executionNode>ALL</executionNode>
+        <lossTolerant>false</lossTolerant>
+        <penaltyDuration>30 sec</penaltyDuration>
+        <properties>
+          <entry>
+            <key>File Size</key>
+            <value>0B</value>
+          </entry>
+          <entry>
+            <key>Batch Size</key>
+            <value>1</value>
+          </entry>
+          <entry>
+            <key>Data Format</key>
+            <value>Text</value>
+          </entry>
+          <entry>
+            <key>Unique FlowFiles</key>
+            <value>false</value>
+          </entry>
+          <entry>
+            <key>generate-ff-custom-text</key>
+            <value>abcdefg</value>
+          </entry>
+        </properties>
+        <runDurationMillis>0</runDurationMillis>
+        <schedulingPeriod>0 sec</schedulingPeriod>
+        <schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
+        <yieldDuration>1 sec</yieldDuration>
+      </config>
+      <name>GenerateFlowFile</name>
+      <relationships>
+        <autoTerminate>false</autoTerminate>
+        <name>success</name>
+      </relationships>
+      <style></style>
+      <type>org.apache.nifi.processors.standard.GenerateFlowFile</type>
+    </processors>
+  </snippet>
+  <timestamp>03/17/2017 13:22:58 EDT</timestamp>
+</template>
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/templates/f080ec50-ca32-4b36-8453-5a7145bec4c5/download b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/templates/f080ec50-ca32-4b36-8453-5a7145bec4c5/download
new file mode 100644
index 0000000..ad65498
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/mocknifi/www/nifi-api/templates/f080ec50-ca32-4b36-8453-5a7145bec4c5/download
@@ -0,0 +1,202 @@
+<?xml version="1.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.
+-->
+<template encoding-version="1.0">
+  <description></description>
+  <groupId>8f8eda5e-015a-1000-a9c1-b7e4fe10ae83</groupId>
+  <name>raspi3.v1</name>
+  <snippet>
+    <connections>
+      <id>8f96f2a9-015a-1000-0000-000000000000</id>
+      <parentGroupId>8f8eda5e-015a-1000-0000-000000000000</parentGroupId>
+      <backPressureDataSizeThreshold>1 GB</backPressureDataSizeThreshold>
+      <backPressureObjectThreshold>10000</backPressureObjectThreshold>
+      <destination>
+        <groupId>8f8eda5e-015a-1000-0000-000000000000</groupId>
+        <id>8f96bf68-015a-1000-0000-000000000000</id>
+        <type>PROCESSOR</type>
+      </destination>
+      <flowFileExpiration>0 sec</flowFileExpiration>
+      <labelIndex>1</labelIndex>
+      <name></name>
+      <selectedRelationships>success</selectedRelationships>
+      <source>
+        <groupId>8f8eda5e-015a-1000-0000-000000000000</groupId>
+        <id>8f96e313-015a-1000-0000-000000000000</id>
+        <type>PROCESSOR</type>
+      </source>
+      <zIndex>0</zIndex>
+    </connections>
+    <processors>
+      <id>8f96bf68-015a-1000-0000-000000000000</id>
+      <parentGroupId>8f8eda5e-015a-1000-0000-000000000000</parentGroupId>
+      <position>
+        <x>14.0</x>
+        <y>253.0</y>
+      </position>
+      <config>
+        <bulletinLevel>WARN</bulletinLevel>
+        <comments></comments>
+        <concurrentlySchedulableTaskCount>1</concurrentlySchedulableTaskCount>
+        <descriptors>
+          <entry>
+            <key>Log prefix</key>
+            <value>
+              <name>Log prefix</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Log Level</key>
+            <value>
+              <name>Log Level</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Attributes to Ignore</key>
+            <value>
+              <name>Attributes to Ignore</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Attributes to Log</key>
+            <value>
+              <name>Attributes to Log</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Log Payload</key>
+            <value>
+              <name>Log Payload</name>
+            </value>
+          </entry>
+        </descriptors>
+        <executionNode>ALL</executionNode>
+        <lossTolerant>false</lossTolerant>
+        <penaltyDuration>30 sec</penaltyDuration>
+        <properties>
+          <entry>
+            <key>Log prefix</key>
+          </entry>
+          <entry>
+            <key>Log Level</key>
+            <value>info</value>
+          </entry>
+          <entry>
+            <key>Attributes to Ignore</key>
+          </entry>
+          <entry>
+            <key>Attributes to Log</key>
+          </entry>
+          <entry>
+            <key>Log Payload</key>
+            <value>true</value>
+          </entry>
+        </properties>
+        <runDurationMillis>0</runDurationMillis>
+        <schedulingPeriod>0 sec</schedulingPeriod>
+        <schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
+        <yieldDuration>1 sec</yieldDuration>
+      </config>
+      <name>LogAttribute</name>
+      <relationships>
+        <autoTerminate>true</autoTerminate>
+        <name>success</name>
+      </relationships>
+      <style></style>
+      <type>org.apache.nifi.processors.standard.LogAttribute</type>
+    </processors>
+    <processors>
+      <id>8f96e313-015a-1000-0000-000000000000</id>
+      <parentGroupId>8f8eda5e-015a-1000-0000-000000000000</parentGroupId>
+      <position>
+        <x>0.0</x>
+        <y>0.0</y>
+      </position>
+      <config>
+        <bulletinLevel>WARN</bulletinLevel>
+        <comments></comments>
+        <concurrentlySchedulableTaskCount>1</concurrentlySchedulableTaskCount>
+        <descriptors>
+          <entry>
+            <key>File Size</key>
+            <value>
+              <name>File Size</name>
+            </value>
+          </entry>
+          <entry>
+            <key>generate-ff-custom-text</key>
+            <value>
+              <name>generate-ff-custom-text</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Batch Size</key>
+            <value>
+              <name>Batch Size</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Unique FlowFiles</key>
+            <value>
+              <name>Unique FlowFiles</name>
+            </value>
+          </entry>
+          <entry>
+            <key>Data Format</key>
+            <value>
+              <name>Data Format</name>
+            </value>
+          </entry>
+        </descriptors>
+        <executionNode>ALL</executionNode>
+        <lossTolerant>false</lossTolerant>
+        <penaltyDuration>30 sec</penaltyDuration>
+        <properties>
+          <entry>
+            <key>File Size</key>
+            <value>0B</value>
+          </entry>
+          <entry>
+            <key>generate-ff-custom-text</key>
+          </entry>
+          <entry>
+            <key>Batch Size</key>
+            <value>1</value>
+          </entry>
+          <entry>
+            <key>Unique FlowFiles</key>
+            <value>false</value>
+          </entry>
+          <entry>
+            <key>Data Format</key>
+            <value>Text</value>
+          </entry>
+        </properties>
+        <runDurationMillis>0</runDurationMillis>
+        <schedulingPeriod>0 sec</schedulingPeriod>
+        <schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
+        <yieldDuration>1 sec</yieldDuration>
+      </config>
+      <name>GenerateFlowFile</name>
+      <relationships>
+        <autoTerminate>false</autoTerminate>
+        <name>success</name>
+      </relationships>
+      <style></style>
+      <type>org.apache.nifi.processors.standard.GenerateFlowFile</type>
+    </processors>
+  </snippet>
+  <timestamp>03/07/2017 11:13:03 EST</timestamp>
+</template>
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/overlay.properties b/minifi-c2/minifi-c2-integration-tests/src/test/resources/overlay.properties
new file mode 100644
index 0000000..b1312a0
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/overlay.properties
@@ -0,0 +1,16 @@
+#
+# 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.
+#
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/squid/squid.conf b/minifi-c2/minifi-c2-integration-tests/src/test/resources/squid/squid.conf
new file mode 100644
index 0000000..90a5248
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/squid/squid.conf
@@ -0,0 +1,19 @@
+# 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.
+
+http_access allow all
+
+# Choose the port you want. Below we set it to default 3128.
+http_port 3128
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-jetty/pom.xml b/minifi-c2/minifi-c2-jetty/pom.xml
new file mode 100644
index 0000000..df24c62
--- /dev/null
+++ b/minifi-c2/minifi-c2-jetty/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-jetty</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/minifi-c2/minifi-c2-jetty/src/main/java/org/apache/nifi/minifi/c2/jetty/JettyServer.java b/minifi-c2/minifi-c2-jetty/src/main/java/org/apache/nifi/minifi/c2/jetty/JettyServer.java
new file mode 100644
index 0000000..5c8b1f8
--- /dev/null
+++ b/minifi-c2/minifi-c2-jetty/src/main/java/org/apache/nifi/minifi/c2/jetty/JettyServer.java
@@ -0,0 +1,142 @@
+/*
+ * 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.c2.jetty;
+
+import org.apache.nifi.minifi.c2.api.properties.C2Properties;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.webapp.WebAppClassLoader;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class JettyServer {
+    private static final Logger logger = LoggerFactory.getLogger(JettyServer.class);
+    private static String C2_SERVER_HOME = System.getenv("C2_SERVER_HOME");
+    private static final String WEB_DEFAULTS_XML = "webdefault.xml";
+
+    public static void main(String[] args) throws Exception {
+        C2Properties properties = C2Properties.getInstance();
+
+        final HandlerCollection handlers = new HandlerCollection();
+        for (Path path : Files.list(Paths.get(C2_SERVER_HOME, "webapps")).collect(Collectors.toList())) {
+             handlers.addHandler(loadWar(path.toFile(), "/c2", JettyServer.class.getClassLoader()));
+        }
+
+        Server server;
+        int port = Integer.parseInt(properties.getProperty("minifi.c2.server.port", "10080"));
+        SslContextFactory sslContextFactory = properties.getSslContextFactory();
+        if (sslContextFactory == null) {
+            server = new Server(port);
+        } else {
+            HttpConfiguration config = new HttpConfiguration();
+            config.setSecureScheme("https");
+            config.setSecurePort(port);
+            config.addCustomizer(new SecureRequestCustomizer());
+
+            server = new Server();
+
+            ServerConnector serverConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(config));
+            serverConnector.setPort(port);
+
+            server.addConnector(serverConnector);
+        }
+
+        server.setHandler(handlers);
+        server.start();
+
+        // ensure everything started successfully
+        for (Handler handler : server.getChildHandlers()) {
+            // see if the handler is a web app
+            if (handler instanceof WebAppContext) {
+                WebAppContext context = (WebAppContext) handler;
+
+                // see if this webapp had any exceptions that would
+                // cause it to be unavailable
+                if (context.getUnavailableException() != null) {
+
+                    System.err.println("Failed to start web server: " + context.getUnavailableException().getMessage());
+                    System.err.println("Shutting down...");
+                    logger.warn("Failed to start web server... shutting down.", context.getUnavailableException());
+                    server.stop();
+                    System.exit(1);
+                }
+            }
+        }
+
+        server.dumpStdErr();
+        server.join();
+    }
+
+    private static WebAppContext loadWar(final File warFile, final String contextPath, final ClassLoader parentClassLoader) throws IOException {
+        final WebAppContext webappContext = new WebAppContext(warFile.getPath(), contextPath);
+        webappContext.setContextPath(contextPath);
+        webappContext.setDisplayName(contextPath);
+
+        // instruction jetty to examine these jars for tlds, web-fragments, etc
+        webappContext.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\\\.jar$|.*/[^/]*taglibs.*\\.jar$" );
+
+        // remove slf4j server class to allow WAR files to have slf4j dependencies in WEB-INF/lib
+        List<String> serverClasses = new ArrayList<>(Arrays.asList(webappContext.getServerClasses()));
+        serverClasses.remove("org.slf4j.");
+        webappContext.setServerClasses(serverClasses.toArray(new String[0]));
+        webappContext.setDefaultsDescriptor(WEB_DEFAULTS_XML);
+
+        // get the temp directory for this webapp
+        File tempDir = Paths.get(C2_SERVER_HOME, "tmp", warFile.getName()).toFile();
+        if (tempDir.exists() && !tempDir.isDirectory()) {
+            throw new RuntimeException(tempDir.getAbsolutePath() + " is not a directory");
+        } else if (!tempDir.exists()) {
+            final boolean made = tempDir.mkdirs();
+            if (!made) {
+                throw new RuntimeException(tempDir.getAbsolutePath() + " could not be created");
+            }
+        }
+        if (!(tempDir.canRead() && tempDir.canWrite())) {
+            throw new RuntimeException(tempDir.getAbsolutePath() + " directory does not have read/write privilege");
+        }
+
+        // configure the temp dir
+        webappContext.setTempDirectory(tempDir);
+
+        // configure the max form size (3x the default)
+        webappContext.setMaxFormContentSize(600000);
+
+        webappContext.setClassLoader(new WebAppClassLoader(parentClassLoader, webappContext));
+
+        logger.info("Loading WAR: " + warFile.getAbsolutePath() + " with context path set to " + contextPath);
+        return webappContext;
+    }
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/pom.xml b/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/pom.xml
new file mode 100644
index 0000000..76e082e
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2-provider</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-provider-cache</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/src/main/java/org/apache/nifi/minifi/c2/provider/cache/CacheConfigurationProvider.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/src/main/java/org/apache/nifi/minifi/c2/provider/cache/CacheConfigurationProvider.java
new file mode 100644
index 0000000..b0864ac
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/src/main/java/org/apache/nifi/minifi/c2/provider/cache/CacheConfigurationProvider.java
@@ -0,0 +1,46 @@
+/*
+ * 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.c2.provider.cache;
+
+import org.apache.nifi.minifi.c2.api.Configuration;
+import org.apache.nifi.minifi.c2.api.ConfigurationProvider;
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+import org.apache.nifi.minifi.c2.api.cache.ConfigurationCache;
+
+import java.util.List;
+import java.util.Map;
+
+public class CacheConfigurationProvider implements ConfigurationProvider {
+    private final String contentType;
+    private final ConfigurationCache configurationCache;
+
+    public CacheConfigurationProvider(String contentType, ConfigurationCache configurationCache) {
+        this.contentType = contentType;
+        this.configurationCache = configurationCache;
+    }
+
+    @Override
+    public String getContentType() {
+        return contentType;
+    }
+
+    @Override
+    public Configuration getConfiguration(Integer version, Map<String, List<String>> parameters) throws ConfigurationProviderException {
+        return configurationCache.getCacheFileInfo(parameters).getConfiguration(version);
+    }
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/src/test/java/org/apache/nifi/minifi/c2/provider/cache/CacheConfigurationProviderTest.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/src/test/java/org/apache/nifi/minifi/c2/provider/cache/CacheConfigurationProviderTest.java
new file mode 100644
index 0000000..d038194
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/src/test/java/org/apache/nifi/minifi/c2/provider/cache/CacheConfigurationProviderTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.c2.provider.cache;
+
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+import org.apache.nifi.minifi.c2.api.cache.ConfigurationCache;
+import org.apache.nifi.minifi.c2.api.cache.ConfigurationCacheFileInfo;
+import org.apache.nifi.minifi.c2.api.cache.WriteableConfiguration;
+import org.apache.nifi.minifi.c2.provider.cache.CacheConfigurationProvider;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class CacheConfigurationProviderTest {
+    public static final String TEST_CONTENT_TYPE = "test/contenttype";
+
+    private CacheConfigurationProvider cacheConfigurationProvider;
+    private ConfigurationCache configConfigurationCache;
+
+    @Before
+    public void setup() {
+        configConfigurationCache = mock(ConfigurationCache.class);
+        cacheConfigurationProvider = new CacheConfigurationProvider(TEST_CONTENT_TYPE, configConfigurationCache);
+    }
+
+    @Test
+    public void testContentType() {
+        assertEquals(TEST_CONTENT_TYPE, cacheConfigurationProvider.getContentType());
+    }
+
+    @Test
+    public void testGetConfiguration() throws ConfigurationProviderException {
+        int version = 99;
+
+        Map<String, List<String>> parameters = mock(Map.class);
+        ConfigurationCacheFileInfo configurationCacheFileInfo = mock(ConfigurationCacheFileInfo.class);
+        WriteableConfiguration configuration = mock(WriteableConfiguration.class);
+
+        when(configConfigurationCache.getCacheFileInfo(parameters)).thenReturn(configurationCacheFileInfo);
+        when(configurationCacheFileInfo.getConfiguration(version)).thenReturn(configuration);
+
+        assertEquals(configuration, cacheConfigurationProvider.getConfiguration(version, parameters));
+    }
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/pom.xml b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/pom.xml
new file mode 100644
index 0000000..8595246
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2-provider</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-provider-nifi-rest</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>2.8.7</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-toolkit-configuration</artifactId>
+            <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <configuration>
+                    <excludes combine.children="append">
+                        <exclude>src/test/resources/noTemplates.json</exclude>
+                        <exclude>src/test/resources/oneTemplate.json</exclude>
+                        <exclude>src/test/resources/twoTemplates.json</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConfigurationProvider.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConfigurationProvider.java
new file mode 100644
index 0000000..77175d7
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConfigurationProvider.java
@@ -0,0 +1,149 @@
+/*
+ * 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.c2.provider.nifi.rest;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import org.apache.nifi.minifi.c2.api.Configuration;
+import org.apache.nifi.minifi.c2.api.ConfigurationProvider;
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+import org.apache.nifi.minifi.c2.api.InvalidParameterException;
+import org.apache.nifi.minifi.c2.api.cache.ConfigurationCache;
+import org.apache.nifi.minifi.c2.api.cache.ConfigurationCacheFileInfo;
+import org.apache.nifi.minifi.c2.api.cache.WriteableConfiguration;
+import org.apache.nifi.minifi.c2.api.util.Pair;
+import org.apache.nifi.minifi.commons.schema.ConfigSchema;
+import org.apache.nifi.minifi.commons.schema.serialization.SchemaSaver;
+import org.apache.nifi.minifi.toolkit.configuration.ConfigMain;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.JAXBException;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.security.GeneralSecurityException;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+public class NiFiRestConfigurationProvider implements ConfigurationProvider {
+    public static final String CONTENT_TYPE = "text/yml";
+    private static final Logger logger = LoggerFactory.getLogger(NiFiRestConfigurationProvider.class);
+    private final JsonFactory jsonFactory = new JsonFactory();
+    private final ConfigurationCache configurationCache;
+    private final NiFiRestConnector niFiRestConnector;
+
+    public NiFiRestConfigurationProvider(ConfigurationCache configurationCache, String nifiUrl) throws InvalidParameterException, GeneralSecurityException, IOException {
+        this(configurationCache, new NiFiRestConnector(nifiUrl));
+    }
+
+    public NiFiRestConfigurationProvider(ConfigurationCache configurationCache, NiFiRestConnector niFiRestConnector) {
+        this.configurationCache = configurationCache;
+        this.niFiRestConnector = niFiRestConnector;
+    }
+
+    @Override
+    public String getContentType() {
+        return CONTENT_TYPE;
+    }
+
+    @Override
+    public Configuration getConfiguration(Integer version, Map<String, List<String>> parameters) throws ConfigurationProviderException {
+        ConfigurationCacheFileInfo configurationCacheFileInfo = configurationCache.getCacheFileInfo(parameters);
+        String id = null;
+        if (version == null) {
+            Pair<String, Integer> maxIdAndVersion = getMaxIdAndVersion(configurationCacheFileInfo);
+            id = maxIdAndVersion.getFirst();
+            version = maxIdAndVersion.getSecond();
+        }
+        WriteableConfiguration configuration = configurationCacheFileInfo.getConfiguration(version);
+        if (configuration.exists()) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Configuration " + configuration + " exists and can be served from configurationCache.");
+            }
+        } else {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Configuration " + configuration + " doesn't exist, will need to download and convert template.");
+            }
+            if (id == null) {
+                try {
+                    String filename = configuration.getName();
+                    Pair<Stream<Pair<String, String>>, Closeable> streamCloseablePair = getIdAndFilenameStream();
+                    try {
+                        id = streamCloseablePair.getFirst().filter(p -> filename.equals(p.getSecond())).map(Pair::getFirst).findFirst()
+                                .orElseThrow(() -> new InvalidParameterException("Unable to find template named " + filename));
+                    } finally {
+                        streamCloseablePair.getSecond().close();
+                    }
+                } catch (IOException|TemplatesIteratorException e) {
+                    throw new ConfigurationProviderException("Unable to retrieve template list", e);
+                }
+            }
+
+            HttpURLConnection urlConnection = niFiRestConnector.get("/templates/" + id + "/download");
+
+            try (InputStream inputStream = urlConnection.getInputStream()){
+                ConfigSchema configSchema = ConfigMain.transformTemplateToSchema(inputStream);
+                SchemaSaver.saveConfigSchema(configSchema, configuration.getOutputStream());
+            } catch (IOException e) {
+                throw new ConfigurationProviderException("Unable to download template from url " + urlConnection.getURL(), e);
+            } catch (JAXBException e) {
+                throw new ConfigurationProviderException("Unable to convert template to yaml", e);
+            } finally {
+                urlConnection.disconnect();
+            }
+        }
+        return configuration;
+    }
+
+    private Pair<Stream<Pair<String, String>>, Closeable> getIdAndFilenameStream() throws ConfigurationProviderException, IOException {
+        TemplatesIterator templatesIterator = new TemplatesIterator(niFiRestConnector, jsonFactory);
+        return new Pair<>(StreamSupport.stream(Spliterators.spliteratorUnknownSize(templatesIterator, Spliterator.ORDERED), false), templatesIterator);
+    }
+
+    private Pair<Stream<Pair<String, Integer>>, Closeable> getIdAndVersionStream(ConfigurationCacheFileInfo configurationCacheFileInfo) throws ConfigurationProviderException, IOException {
+        Pair<Stream<Pair<String, String>>, Closeable> streamCloseablePair = getIdAndFilenameStream();
+        return new Pair<>(streamCloseablePair.getFirst().map(p -> {
+            Integer version = configurationCacheFileInfo.getVersionIfMatch(p.getSecond());
+            if (version == null) {
+                return null;
+            }
+            return new Pair<>(p.getFirst(), version);
+        }).filter(Objects::nonNull), streamCloseablePair.getSecond());
+    }
+
+    private Pair<String, Integer> getMaxIdAndVersion(ConfigurationCacheFileInfo configurationCacheFileInfo) throws ConfigurationProviderException {
+        try {
+            Pair<Stream<Pair<String, Integer>>, Closeable> streamCloseablePair = getIdAndVersionStream(configurationCacheFileInfo);
+            try {
+                return streamCloseablePair.getFirst().sorted(Comparator.comparing(p -> ((Pair<String, Integer>) p).getSecond()).reversed()).findFirst()
+                        .orElseThrow(() -> new ConfigurationProviderException("Didn't find any templates that matched " + configurationCacheFileInfo + ".v[0-9]+"));
+            } finally {
+                streamCloseablePair.getSecond().close();
+            }
+        } catch (IOException|TemplatesIteratorException e) {
+            throw new ConfigurationProviderException("Unable to retrieve template list", e);
+        }
+    }
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConnector.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConnector.java
new file mode 100644
index 0000000..9a0befc
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConnector.java
@@ -0,0 +1,80 @@
+/*
+ * 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.c2.provider.nifi.rest;
+
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+import org.apache.nifi.minifi.c2.api.InvalidParameterException;
+import org.apache.nifi.minifi.c2.api.properties.C2Properties;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+
+public class NiFiRestConnector {
+    private static final Logger logger = LoggerFactory.getLogger(NiFiRestConnector.class);
+
+    private final String nifiApiUrl;
+    private final SslContextFactory sslContextFactory;
+
+    public NiFiRestConnector(String nifiApiUrl) throws InvalidParameterException, GeneralSecurityException, IOException {
+        if (nifiApiUrl.startsWith("https:")) {
+            sslContextFactory = C2Properties.getInstance().getSslContextFactory();
+            if (sslContextFactory == null) {
+                throw new InvalidParameterException("Need sslContextFactory to connect to https NiFi endpoint (" + nifiApiUrl + ")");
+            }
+        } else {
+            sslContextFactory = null;
+        }
+        this.nifiApiUrl = nifiApiUrl;
+    }
+
+    protected HttpURLConnection get(String endpointPath) throws ConfigurationProviderException {
+        String endpointUrl = nifiApiUrl + endpointPath;
+        if (logger.isDebugEnabled()) {
+            logger.debug("Connecting to NiFi endpoint: " + endpointUrl);
+        }
+        URL url;
+        try {
+            url = new URL(endpointUrl);
+        } catch (MalformedURLException e) {
+            throw new ConfigurationProviderException("Malformed url " + endpointUrl, e);
+        }
+
+        try {
+            if (sslContextFactory == null) {
+                return (HttpURLConnection) url.openConnection();
+            } else {
+                HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
+                SSLContext sslContext = sslContextFactory.getSslContext();
+                SSLSocketFactory socketFactory = sslContext.getSocketFactory();
+                httpsURLConnection.setSSLSocketFactory(socketFactory);
+                return httpsURLConnection;
+            }
+        } catch (IOException e) {
+            throw new ConfigurationProviderException("Unable to connect to " + url, e);
+        }
+    }
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIterator.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIterator.java
new file mode 100644
index 0000000..afe6c6e
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIterator.java
@@ -0,0 +1,115 @@
+/*
+ * 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.c2.provider.nifi.rest;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+import org.apache.nifi.minifi.c2.api.util.Pair;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class TemplatesIterator implements Iterator<Pair<String, String>>, Closeable {
+    public static final String FLOW_TEMPLATES = "/flow/templates";
+
+    private final HttpURLConnection urlConnection;
+    private final InputStream inputStream;
+    private final JsonParser parser;
+    private Pair<String, String> next;
+
+    public TemplatesIterator(NiFiRestConnector niFiRestConnector, JsonFactory jsonFactory) throws ConfigurationProviderException, IOException {
+        urlConnection = niFiRestConnector.get(FLOW_TEMPLATES);
+        inputStream = urlConnection.getInputStream();
+        parser = jsonFactory.createParser(inputStream);
+        while (parser.nextToken() != JsonToken.END_OBJECT) {
+            if ("templates".equals(parser.getCurrentName())) {
+                break;
+            }
+        }
+        next = getNext();
+    }
+
+    private Pair<String, String> getNext() throws IOException {
+        while (parser.nextToken() != JsonToken.END_ARRAY) {
+            if ("template".equals(parser.getCurrentName())) {
+                String id = null;
+                String name = null;
+                while (parser.nextToken() != JsonToken.END_OBJECT) {
+                    String currentName = parser.getCurrentName();
+                    if ("id".equals(currentName)) {
+                        parser.nextToken();
+                        id = parser.getText();
+                    } else if ("name".equals(currentName)) {
+                        parser.nextToken();
+                        name = parser.getText();
+                    }
+                }
+                return new Pair<>(id, name);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return next != null;
+    }
+
+    @Override
+    public Pair<String, String> next() {
+        if (next == null) {
+            throw new NoSuchElementException();
+        }
+        try {
+            return next;
+        } finally {
+            try {
+                next = getNext();
+            } catch (IOException e) {
+                throw new TemplatesIteratorException(e);
+            }
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (parser != null) {
+            try {
+                parser.close();
+            } catch (IOException e) {
+                //Ignore
+            }
+        }
+        if (inputStream != null) {
+            try {
+                inputStream.close();
+            } catch (IOException e) {
+                //Ignore
+            }
+        }
+        if (urlConnection != null) {
+            urlConnection.disconnect();
+        }
+    }
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIteratorException.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIteratorException.java
new file mode 100644
index 0000000..af22474
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIteratorException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.c2.provider.nifi.rest;
+
+import java.io.IOException;
+
+public class TemplatesIteratorException extends RuntimeException {
+    public TemplatesIteratorException(IOException cause) {
+        super(cause);
+    }
+
+    @Override
+    public synchronized IOException getCause() {
+        return (IOException) super.getCause();
+    }
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConfigurationProviderTest.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConfigurationProviderTest.java
new file mode 100644
index 0000000..2cb3a8d
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConfigurationProviderTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.c2.provider.nifi.rest;
+
+import org.apache.nifi.minifi.c2.api.cache.ConfigurationCache;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Comparator;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class NiFiRestConfigurationProviderTest {
+    private ConfigurationCache configConfigurationCache;
+    private NiFiRestConnector niFiRestConnector;
+    private NiFiRestConfigurationProvider niFiRestConfigurationProvider;
+    private Path cachePath;
+
+    @Before
+    public void setup() throws IOException {
+        configConfigurationCache = mock(ConfigurationCache.class);
+        niFiRestConnector = mock(NiFiRestConnector.class);
+        niFiRestConfigurationProvider = new NiFiRestConfigurationProvider(configConfigurationCache, niFiRestConnector);
+        cachePath = Files.createTempDirectory(NiFiRestConfigurationProviderTest.class.getCanonicalName());
+    }
+
+    @After
+    public void teardown() throws IOException {
+        Files.walk(cachePath)
+                .sorted(Comparator.reverseOrder())
+                .forEach(p -> {
+                    try {
+                        Files.deleteIfExists(p);
+                    } catch (IOException e) {
+                        p.toFile().deleteOnExit();
+                    }
+                });
+    }
+
+    @Test
+    public void testContentType() {
+        assertEquals(NiFiRestConfigurationProvider.CONTENT_TYPE, niFiRestConfigurationProvider.getContentType());
+    }
+
+
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIteratorExceptionTest.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIteratorExceptionTest.java
new file mode 100644
index 0000000..3be3445
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIteratorExceptionTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.c2.provider.nifi.rest;
+
+import org.apache.nifi.minifi.c2.provider.nifi.rest.TemplatesIteratorException;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class TemplatesIteratorExceptionTest {
+    @Test
+    public void testCauseConstructor() {
+        IOException ioException = mock(IOException.class);
+        assertEquals(ioException, new TemplatesIteratorException(ioException).getCause());
+    }
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIteratorTest.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIteratorTest.java
new file mode 100644
index 0000000..ca20e4a
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIteratorTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.c2.provider.nifi.rest;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.google.common.collect.Lists;
+import org.apache.nifi.minifi.c2.api.ConfigurationProviderException;
+import org.apache.nifi.minifi.c2.api.util.Pair;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class TemplatesIteratorTest {
+    private JsonFactory jsonFactory;
+    private HttpURLConnection httpURLConnection;
+    private NiFiRestConnector niFiRestConnector;
+
+    @Before
+    public void setup() throws ConfigurationProviderException {
+        jsonFactory = new JsonFactory();
+        httpURLConnection = mock(HttpURLConnection.class);
+        niFiRestConnector = mock(NiFiRestConnector.class);
+        when(niFiRestConnector.get(TemplatesIterator.FLOW_TEMPLATES)).thenReturn(httpURLConnection);
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void testIteratorNoSuchElementException() throws ConfigurationProviderException, IOException {
+        when(httpURLConnection.getInputStream()).thenReturn(TemplatesIteratorTest.class.getClassLoader().getResourceAsStream("noTemplates.json"));
+
+        try (TemplatesIterator templatesIterator = new TemplatesIterator(niFiRestConnector, jsonFactory)) {
+            assertFalse(templatesIterator.hasNext());
+            templatesIterator.next();
+        } finally {
+            verify(httpURLConnection).disconnect();
+        }
+    }
+
+    @Test
+    public void testIteratorNoTemplates() throws ConfigurationProviderException, IOException {
+        when(httpURLConnection.getInputStream()).thenReturn(TemplatesIteratorTest.class.getClassLoader().getResourceAsStream("noTemplates.json"));
+        List<Pair<String, String>> idToNameList;
+        try (TemplatesIterator templatesIterator = new TemplatesIterator(niFiRestConnector, jsonFactory)) {
+            idToNameList = Lists.newArrayList(templatesIterator);
+        }
+        assertEquals(0, idToNameList.size());
+
+        verify(httpURLConnection).disconnect();
+    }
+
+    @Test
+    public void testIteratorSingleTemplate() throws ConfigurationProviderException, IOException {
+        when(httpURLConnection.getInputStream()).thenReturn(TemplatesIteratorTest.class.getClassLoader().getResourceAsStream("oneTemplate.json"));
+        List<Pair<String, String>> idToNameList;
+        try (TemplatesIterator templatesIterator = new TemplatesIterator(niFiRestConnector, jsonFactory)) {
+            idToNameList = Lists.newArrayList(templatesIterator);
+        }
+        assertEquals(1, idToNameList.size());
+        Pair<String, String> idNamePair = idToNameList.get(0);
+        assertEquals("d05845ae-ceda-4c50-b7c2-037e42ddf1d3", idNamePair.getFirst());
+        assertEquals("raspi3.v1", idNamePair.getSecond());
+
+        verify(httpURLConnection).disconnect();
+    }
+
+    @Test
+    public void testIteratorTwoTemplates() throws ConfigurationProviderException, IOException {
+        when(httpURLConnection.getInputStream()).thenReturn(TemplatesIteratorTest.class.getClassLoader().getResourceAsStream("twoTemplates.json"));
+        List<Pair<String, String>> idToNameList;
+        try (TemplatesIterator templatesIterator = new TemplatesIterator(niFiRestConnector, jsonFactory)) {
+            idToNameList = Lists.newArrayList(templatesIterator);
+        }
+        assertEquals(2, idToNameList.size());
+        Pair<String, String> idNamePair = idToNameList.get(0);
+        assertEquals("d05845ae-ceda-4c50-b7c2-037e42ddf1d3", idNamePair.getFirst());
+        assertEquals("raspi3.v1", idNamePair.getSecond());
+
+        idNamePair = idToNameList.get(1);
+        assertEquals("9384b48d-85b4-478a-bf3e-64d113f8fbc5", idNamePair.getFirst());
+        assertEquals("raspi3.v2", idNamePair.getSecond());
+
+        verify(httpURLConnection).disconnect();
+    }
+}
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/resources/noTemplates.json b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/resources/noTemplates.json
new file mode 100644
index 0000000..ff41507
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/resources/noTemplates.json
@@ -0,0 +1 @@
+{"templates":[],"generated":"14:55:13 EST"}
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/resources/oneTemplate.json b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/resources/oneTemplate.json
new file mode 100644
index 0000000..0d981f1
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/resources/oneTemplate.json
@@ -0,0 +1,21 @@
+{
+    "generated": "10:26:04 EST",
+    "templates": [
+        {
+            "id": "d05845ae-ceda-4c50-b7c2-037e42ddf1d3",
+            "permissions": {
+                "canRead": true,
+                "canWrite": true
+            },
+            "template": {
+                "description": "",
+                "encoding-version": "1.0",
+                "groupId": "8f8eda5e-015a-1000-a9c1-b7e4fe10ae83",
+                "id": "d05845ae-ceda-4c50-b7c2-037e42ddf1d3",
+                "name": "raspi3.v1",
+                "timestamp": "03/02/2017 10:26:01 EST",
+                "uri": "http://localhost:8081/nifi-api/templates/d05845ae-ceda-4c50-b7c2-037e42ddf1d3"
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/resources/twoTemplates.json b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/resources/twoTemplates.json
new file mode 100644
index 0000000..9b4245a
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/resources/twoTemplates.json
@@ -0,0 +1,37 @@
+{
+  "generated": "14:53:15 EST",
+  "templates": [
+    {
+      "id": "d05845ae-ceda-4c50-b7c2-037e42ddf1d3",
+      "permissions": {
+        "canRead": true,
+        "canWrite": true
+      },
+      "template": {
+        "description": "",
+        "encoding-version": "1.0",
+        "groupId": "8f8eda5e-015a-1000-a9c1-b7e4fe10ae83",
+        "id": "d05845ae-ceda-4c50-b7c2-037e42ddf1d3",
+        "name": "raspi3.v1",
+        "timestamp": "03/02/2017 10:26:01 EST",
+        "uri": "http://localhost:8081/nifi-api/templates/d05845ae-ceda-4c50-b7c2-037e42ddf1d3"
+      }
+    },
+    {
+      "id": "9384b48d-85b4-478a-bf3e-64d113f8fbc5",
+      "permissions": {
+        "canRead": true,
+        "canWrite": true
+      },
+      "template": {
+        "description": "",
+        "encoding-version": "1.0",
+        "groupId": "8f8eda5e-015a-1000-a9c1-b7e4fe10ae83",
+        "id": "9384b48d-85b4-478a-bf3e-64d113f8fbc5",
+        "name": "raspi3.v2",
+        "timestamp": "03/02/2017 13:08:14 EST",
+        "uri": "http://localhost:8081/nifi-api/templates/9384b48d-85b4-478a-bf3e-64d113f8fbc5"
+      }
+    }
+  ]
+}
diff --git a/minifi-c2/minifi-c2-provider/pom.xml b/minifi-c2/minifi-c2-provider/pom.xml
new file mode 100644
index 0000000..fc0fbae
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/pom.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-provider</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>minifi-c2-provider-cache</module>
+        <module>minifi-c2-provider-nifi-rest</module>
+    </modules>
+</project>
diff --git a/minifi-c2/minifi-c2-service/pom.xml b/minifi-c2/minifi-c2-service/pom.xml
new file mode 100644
index 0000000..eb52d7d
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi-c2</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2-service</artifactId>
+    <packaging>war</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-web</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-config</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.yaml</groupId>
+            <artifactId>snakeyaml</artifactId>
+            <version>1.17</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>jsr311-api</artifactId>
+            <version>1.1.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.wordnik</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/configuration/Configuration.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/configuration/Configuration.java
new file mode 100644
index 0000000..19b647f
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/configuration/Configuration.java
@@ -0,0 +1,28 @@
+/*
+ * 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.c2.configuration;
+
+import org.apache.nifi.minifi.c2.security.SecurityConfiguration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.ImportResource;
+
+@org.springframework.context.annotation.Configuration
+@Import({SecurityConfiguration.class})
+@ImportResource({"classpath:minifi-c2-context.xml"})
+public class Configuration {
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/SecurityConfiguration.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/SecurityConfiguration.java
new file mode 100644
index 0000000..2c4630c
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/SecurityConfiguration.java
@@ -0,0 +1,91 @@
+/*
+ * 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.c2.security;
+
+import org.apache.nifi.minifi.c2.security.authentication.C2AnonymousAuthenticationFilter;
+import org.apache.nifi.minifi.c2.security.authentication.X509AuthenticationFilter;
+import org.apache.nifi.minifi.c2.security.authentication.X509AuthenticationProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.ImportResource;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+@ImportResource({"classpath:minifi-c2-web-security-context.xml"})
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+    private AuthenticationProvider authenticationProvider;
+    private X509AuthenticationFilter x509AuthenticationFilter;
+    private C2AnonymousAuthenticationFilter c2AnonymousAuthenticationFilter;
+
+    public SecurityConfiguration() {
+        super(true);
+    }
+
+    @Bean
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        // override xxxBean method so the authentication manager is available in app context (necessary for the method level security)
+        return super.authenticationManagerBean();
+    }
+
+    @Override
+    public void configure(WebSecurity web) throws Exception {
+        web.ignoring().antMatchers("/access", "/access/config", "/access/token", "/access/kerberos");
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                .rememberMe().disable().authorizeRequests().anyRequest().fullyAuthenticated().and()
+                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+        http.addFilterBefore(x509AuthenticationFilter, AnonymousAuthenticationFilter.class);
+        http.anonymous().authenticationFilter(c2AnonymousAuthenticationFilter);
+    }
+
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth.authenticationProvider(authenticationProvider);
+    }
+
+    @Autowired
+    public void setX509AuthenticationProvider(X509AuthenticationProvider x509AuthenticationProvider) {
+        this.authenticationProvider = x509AuthenticationProvider;
+    }
+
+    @Autowired
+    public void setX509AuthenticationFilter(X509AuthenticationFilter x509AuthenticationFilter) {
+        this.x509AuthenticationFilter = x509AuthenticationFilter;
+    }
+
+    @Autowired
+    public void setC2AnonymousAuthenticationFilter(C2AnonymousAuthenticationFilter c2AnonymousAuthenticationFilter) {
+        this.c2AnonymousAuthenticationFilter = c2AnonymousAuthenticationFilter;
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/C2AnonymousAuthenticationFilter.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/C2AnonymousAuthenticationFilter.java
new file mode 100644
index 0000000..e73d2ee
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/C2AnonymousAuthenticationFilter.java
@@ -0,0 +1,38 @@
+/*
+ * 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.c2.security.authentication;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+
+public class C2AnonymousAuthenticationFilter extends AnonymousAuthenticationFilter {
+    public static final String ANONYMOUS = "anonymous";
+
+    public C2AnonymousAuthenticationFilter() {
+        super(ANONYMOUS);
+    }
+
+    @Override
+    protected Authentication createAuthentication(HttpServletRequest request) {
+        return new C2AuthenticationToken(ANONYMOUS, null, Arrays.asList(new SimpleGrantedAuthority("ROLE_ANONYMOUS")));
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/C2AuthenticationToken.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/C2AuthenticationToken.java
new file mode 100644
index 0000000..d6f85ec
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/C2AuthenticationToken.java
@@ -0,0 +1,51 @@
+/*
+ * 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.c2.security.authentication;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Collection;
+
+public class C2AuthenticationToken extends AbstractAuthenticationToken {
+    private final String principal;
+    private Object credentials;
+
+    public C2AuthenticationToken(String principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
+        super(authorities);
+        setAuthenticated(true);
+        this.principal = principal;
+        this.credentials = credentials;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return credentials;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return principal;
+    }
+
+    @Override
+    public void eraseCredentials() {
+        super.eraseCredentials();
+        credentials = null;
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/X509AuthenticationFilter.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/X509AuthenticationFilter.java
new file mode 100644
index 0000000..63c743b
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/X509AuthenticationFilter.java
@@ -0,0 +1,70 @@
+/*
+ * 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.c2.security.authentication;
+
+import org.apache.nifi.minifi.c2.util.HttpRequestUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.filter.GenericFilterBean;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+public class X509AuthenticationFilter extends GenericFilterBean {
+    private static final Logger logger = LoggerFactory.getLogger(X509AuthenticationFilter.class);
+    private AuthenticationManager authenticationManager;
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        authenticateIfPossible(request);
+        chain.doFilter(request, response);
+    }
+
+    private void authenticateIfPossible(ServletRequest request) {
+        if (!request.isSecure()) {
+            return;
+        }
+
+        X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
+
+        if (certs == null || certs.length == 0) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Unable to get certificates in request from " + HttpRequestUtil.getClientString(request));
+            }
+            return;
+        }
+
+        Authentication authentication = authenticationManager.authenticate(new X509AuthenticationToken(certs));
+        if (authentication.isAuthenticated()) {
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+        }
+    }
+
+    @Autowired
+    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
+        this.authenticationManager = authenticationManager;
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/X509AuthenticationProvider.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/X509AuthenticationProvider.java
new file mode 100644
index 0000000..c297947
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/X509AuthenticationProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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.c2.security.authentication;
+
+import org.apache.nifi.minifi.c2.api.security.authorization.AuthorityGranter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+
+public class X509AuthenticationProvider implements AuthenticationProvider {
+    private static final Logger logger = LoggerFactory.getLogger(X509AuthenticationProvider.class);
+    private final AuthorityGranter authorityGranter;
+
+    public X509AuthenticationProvider(AuthorityGranter authorityGranter) {
+        this.authorityGranter = authorityGranter;
+    }
+
+    @Override
+    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+        X509AuthenticationToken x509AuthenticationToken = (X509AuthenticationToken) authentication;
+        if (logger.isDebugEnabled()) {
+            logger.debug("Authenticating " + X509AuthenticationToken.class.getSimpleName() + " with principal " +  x509AuthenticationToken.getPrincipal());
+        }
+        return new C2AuthenticationToken(x509AuthenticationToken.getPrincipal(), x509AuthenticationToken.getCredentials(),
+                authorityGranter.grantAuthorities(authentication));
+    }
+
+    @Override
+    public boolean supports(Class<?> authentication) {
+        return X509AuthenticationToken.class.isAssignableFrom(authentication);
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/X509AuthenticationToken.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/X509AuthenticationToken.java
new file mode 100644
index 0000000..1faa0b3
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authentication/X509AuthenticationToken.java
@@ -0,0 +1,52 @@
+/*
+ * 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.c2.security.authentication;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+
+public class X509AuthenticationToken extends AbstractAuthenticationToken {
+    private final X509Certificate[] x509Certificates;
+    private final String subjectDn;
+
+    public X509AuthenticationToken(X509Certificate[] x509Certificates) {
+        this(x509Certificates, null);
+        setAuthenticated(false);
+    }
+
+    protected X509AuthenticationToken(X509Certificate[] x509Certificates, Collection<GrantedAuthority> grantedAuthorities) {
+        super(grantedAuthorities);
+        this.x509Certificates = Arrays.copyOf(x509Certificates, x509Certificates.length, X509Certificate[].class);
+        X509Certificate x509Certificate = x509Certificates[0];
+        this.subjectDn = x509Certificate.getSubjectDN().getName().trim();
+    }
+
+    @Override
+    public X509Certificate[] getCredentials() {
+        return x509Certificates;
+    }
+
+    @Override
+    public String getPrincipal() {
+        return subjectDn;
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authorization/GrantedAuthorityAuthorizer.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authorization/GrantedAuthorityAuthorizer.java
new file mode 100644
index 0000000..71f3fa4
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authorization/GrantedAuthorityAuthorizer.java
@@ -0,0 +1,138 @@
+/*
+ * 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.c2.security.authorization;
+
+import org.apache.nifi.minifi.c2.api.security.authorization.AuthorizationException;
+import org.apache.nifi.minifi.c2.api.security.authorization.Authorizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.Resource;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.yaml.snakeyaml.Yaml;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class GrantedAuthorityAuthorizer implements Authorizer {
+    private static final Logger logger = LoggerFactory.getLogger(GrantedAuthorityAuthorizer.class);
+
+    public static final String DENY = "deny";
+    public static final String ALLOW = "allow";
+    public static final String DEFAULT_ACTION = "Default Action";
+    private final Map<String, Object> grantedAuthorityMap;
+
+    public GrantedAuthorityAuthorizer(Resource configYaml) throws IOException {
+        try (InputStream inputStream = configYaml.getInputStream()) {
+            grantedAuthorityMap = as(Map.class, new Yaml().load(inputStream), o -> new IllegalArgumentException("Expected yaml map for root of configuration but was " + o));
+        }
+    }
+
+    @Override
+    public void authorize(Authentication authentication, UriInfo uriInfo) throws AuthorizationException {
+        if (authentication == null) {
+            throw new AuthorizationException("null authentication object provided.");
+        }
+
+        if (!authentication.isAuthenticated()) {
+            throw new AuthorizationException(authentication + " not authenticated.");
+        }
+
+        Set<String> authorities = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
+
+        String defaultAction = as(String.class, grantedAuthorityMap.getOrDefault(DEFAULT_ACTION, DENY));
+        String path = uriInfo.getAbsolutePath().getPath();
+        Map<String, Object> pathAuthorizations = as(Map.class, grantedAuthorityMap.get("Paths"));
+        if (pathAuthorizations == null && !ALLOW.equalsIgnoreCase(defaultAction)) {
+            throw new AuthorizationException("Didn't find authorizations for " + path + " and default policy is " + defaultAction + " instead of allow");
+        }
+
+        Map<String, Object> pathAuthorization = as(Map.class, pathAuthorizations.get(path));
+        if (pathAuthorization == null && !ALLOW.equalsIgnoreCase(defaultAction)) {
+            throw new AuthorizationException("Didn't find authorizations for " + path + " and default policy is " + defaultAction + " instead of allow");
+        }
+        defaultAction = as(String.class, pathAuthorization.getOrDefault(DEFAULT_ACTION, defaultAction));
+        List<Map<String, Object>> actions = as(List.class, pathAuthorization.get("Actions"));
+        MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters();
+        for (Map<String, Object> action : actions) {
+            String ruleAction = as(String.class, action.get("Action"));
+            if (ruleAction == null || !(ALLOW.equalsIgnoreCase(ruleAction) || DENY.equalsIgnoreCase(ruleAction))) {
+                throw new AuthorizationException("Expected Action key of allow or deny for " + action);
+            }
+            String authorization = as(String.class, action.get("Authorization"));
+            if (authorization != null && !authorities.contains(authorization)) {
+                continue;
+            }
+            Map<String, Object> parameters = as(Map.class, action.get("Query Parameters"));
+            if (parameters != null) {
+                boolean foundParameterMismatch = false;
+                for (Map.Entry<String, Object> parameter : parameters.entrySet()) {
+                    Object value = parameter.getValue();
+                    if (value instanceof String) {
+                        value = Arrays.asList((String)value);
+                    }
+                    if (!Objects.equals(queryParameters.get(parameter.getKey()), value)) {
+                        foundParameterMismatch = true;
+                        break;
+                    }
+                }
+                if (foundParameterMismatch) {
+                    continue;
+                }
+            }
+            if (ALLOW.equalsIgnoreCase(ruleAction)) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Action " + action + "matched which resulted in " + ruleAction);
+                }
+                return;
+            } else {
+                throw new AuthorizationException("Action " + action + " matched which resulted in " + ruleAction);
+            }
+        }
+        if (ALLOW.equalsIgnoreCase(defaultAction)) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Found no matching actions so falling back to default action " + defaultAction);
+            }
+        } else {
+            throw new AuthorizationException("Didn't find authorizations for " + path + " and default policy is " + defaultAction + " instead of allow");
+        }
+    }
+
+    private static <T> T as(Class<T> clazz, Object object) throws AuthorizationException {
+        return as(clazz, object, o -> new AuthorizationException("Expected " + clazz + " but was " + o));
+    }
+
+    private static <T, E extends Throwable> T as(Class<T> clazz, Object object, Function<Object, E> exceptionSupplier) throws E {
+        if (object == null) {
+            return null;
+        }
+        if (!clazz.isInstance(object)) {
+            throw exceptionSupplier.apply(object);
+        }
+        return clazz.cast(object);
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authorization/PrincipalStringAuthorityGranter.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authorization/PrincipalStringAuthorityGranter.java
new file mode 100644
index 0000000..24e1ffa
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/security/authorization/PrincipalStringAuthorityGranter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.c2.security.authorization;
+
+import org.apache.nifi.minifi.c2.api.security.authorization.AuthorityGranter;
+import org.springframework.core.io.Resource;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class PrincipalStringAuthorityGranter implements AuthorityGranter {
+    private final Map<String, List<String>> grantedAuthorityMap;
+
+    public PrincipalStringAuthorityGranter(Resource configYaml) throws IOException {
+        try (InputStream inputStream = configYaml.getInputStream()) {
+            Object yaml = new Yaml().load(inputStream);
+            if (!(yaml instanceof Map)) {
+                throw new IllegalArgumentException("Expected authority map of Principal -> Authority list");
+            }
+            grantedAuthorityMap = (Map<String, List<String>>) yaml;
+        }
+    }
+    @Override
+    public Collection<GrantedAuthority> grantAuthorities(Authentication authentication) {
+        List<String> authorities = grantedAuthorityMap.get(authentication.getPrincipal().toString());
+        if (authorities == null) {
+            return null;
+        }
+        return authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/service/ConfigService.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/service/ConfigService.java
new file mode 100644
index 0000000..e57089e
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/service/ConfigService.java
@@ -0,0 +1,167 @@
+/*
+ * 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.c2.service;
+
+import com.wordnik.swagger.annotations.Api;
+import org.apache.nifi.minifi.c2.api.Configuration;
+import org.apache.nifi.minifi.c2.api.ConfigurationProvider;
+import org.apache.nifi.minifi.c2.api.InvalidParameterException;
+import org.apache.nifi.minifi.c2.api.security.authorization.AuthorizationException;
+import org.apache.nifi.minifi.c2.api.security.authorization.Authorizer;
+import org.apache.nifi.minifi.c2.api.util.Pair;
+import org.apache.nifi.minifi.c2.util.HttpRequestUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Path("/config")
+@Api(
+        value = "/config",
+        description = "Provides configuration for MiNiFi instances"
+)
+public class ConfigService {
+    private static final Logger logger = LoggerFactory.getLogger(ConfigService.class);
+    private final List<Pair<MediaType, ConfigurationProvider>> configurationProviders;
+    private final Authorizer authorizer;
+
+    public ConfigService(List<ConfigurationProvider> configurationProviders, Authorizer authorizer) {
+        this.authorizer = authorizer;
+        if (configurationProviders == null || configurationProviders.size() == 0) {
+            throw new IllegalArgumentException("Expected at least one configuration provider");
+        }
+        this.configurationProviders = configurationProviders.stream().map(c -> new Pair<>(MediaType.valueOf(c.getContentType()), c)).collect(Collectors.toList());
+    }
+
+    @GET
+    public Response getConfig(@Context HttpServletRequest request, @Context HttpHeaders httpHeaders, @Context UriInfo uriInfo) {
+        try {
+            authorizer.authorize(SecurityContextHolder.getContext().getAuthentication(), uriInfo);
+        } catch (AuthorizationException e) {
+            logger.warn(HttpRequestUtil.getClientString(request) + " not authorized to access " + uriInfo, e);
+            return Response.status(403).build();
+        }
+        Map<String, List<String>> parameters = new HashMap<>();
+        for (Map.Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            parameters.put(entry.getKey(), entry.getValue());
+        }
+        List<MediaType> acceptValues = httpHeaders.getAcceptableMediaTypes();
+        boolean defaultAccept = false;
+        if (acceptValues.size() == 0) {
+            acceptValues = Arrays.asList(MediaType.WILDCARD_TYPE);
+            defaultAccept = true;
+        }
+        if (logger.isDebugEnabled()) {
+            StringBuilder builder = new StringBuilder("Handling request from ")
+                    .append(HttpRequestUtil.getClientString(request))
+                    .append(" with parameters ")
+                    .append(parameters)
+                    .append(" and Accept");
+            if (defaultAccept) {
+                builder = builder.append(" default value");
+            }
+            builder = builder.append(": ")
+                    .append(acceptValues.stream().map(Object::toString).collect(Collectors.joining(", ")));
+            logger.debug(builder.toString());
+        }
+        Pair<MediaType, ConfigurationProvider> providerPair = getProvider(acceptValues);
+
+        try {
+            Integer version = null;
+            List<String> versionList = parameters.get("version");
+            if (versionList != null && versionList.size() > 0) {
+                try {
+                    version = Integer.parseInt(versionList.get(0));
+                } catch (NumberFormatException e) {
+                    throw new InvalidParameterException("Unable to parse " + version + " as integer.", e);
+                }
+            }
+            Response.ResponseBuilder ok = Response.ok();
+            Configuration configuration = providerPair.getSecond().getConfiguration(version, parameters);
+            ok = ok.header("X-Content-Version", configuration.getVersion());
+            ok = ok.type(providerPair.getFirst());
+            byte[] buffer = new byte[1024];
+            int read;
+            try (InputStream inputStream = configuration.getInputStream();
+                 ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+                MessageDigest md5 = MessageDigest.getInstance("MD5");
+                MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+                while((read = inputStream.read(buffer)) >= 0) {
+                    outputStream.write(buffer, 0, read);
+                    md5.update(buffer, 0, read);
+                    sha256.update(buffer, 0, read);
+                }
+                ok = ok.header("Content-MD5", bytesToHex(md5.digest()));
+                ok = ok.header("X-Content-SHA-256", bytesToHex(sha256.digest()));
+                ok = ok.entity(outputStream.toByteArray());
+            } catch (IOException|NoSuchAlgorithmException e) {
+                logger.error("Error reading or checksumming configuration file", e);
+                throw new WebApplicationException(500);
+            }
+            return ok.build();
+        } catch (InvalidParameterException e) {
+            logger.info(HttpRequestUtil.getClientString(request) + " made invalid request with " + HttpRequestUtil.getQueryString(request), e);
+            return Response.status(400).entity("Invalid request.").build();
+        } catch (Throwable t) {
+            logger.error(HttpRequestUtil.getClientString(request) + " made request with " + HttpRequestUtil.getQueryString(request) + " that caused error in " + providerPair.getSecond(), t);
+            return Response.status(500).entity("Internal error").build();
+        }
+    }
+
+    // see: http://stackoverflow.com/questions/15429257/how-to-convert-byte-array-to-hexstring-in-java#answer-15429408
+    protected static String bytesToHex(byte[] in) {
+        final StringBuilder builder = new StringBuilder();
+        for(byte b : in) {
+            builder.append(String.format("%02x", b));
+        }
+        return builder.toString();
+    }
+
+    private Pair<MediaType, ConfigurationProvider> getProvider(List<MediaType> acceptValues) {
+        for (MediaType accept : acceptValues) {
+            for (Pair<MediaType, ConfigurationProvider> configurationProviderPair : configurationProviders) {
+                if (accept.isCompatible(configurationProviderPair.getFirst())) {
+                    return configurationProviderPair;
+                }
+            }
+        }
+
+        throw new WebApplicationException(Response.status(406).entity("Unable to find configuration provider for " +
+                "\"Accept: " + acceptValues.stream().map(Object::toString).collect(Collectors.joining(", ")) + "\" supported media types are " +
+                configurationProviders.stream().map(Pair::getFirst).map(Object::toString).collect(Collectors.joining(", "))).build());
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/util/HttpRequestUtil.java b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/util/HttpRequestUtil.java
new file mode 100644
index 0000000..395eb73
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/util/HttpRequestUtil.java
@@ -0,0 +1,41 @@
+/*
+ * 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.c2.util;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+public class HttpRequestUtil {
+    public static String getQueryString(HttpServletRequest request) {
+        String queryString = request.getQueryString();
+        if (queryString == null) {
+            return "no query string";
+        }
+        return "query string \"" + queryString + "\"";
+    }
+
+    public static String getClientString(ServletRequest request) {
+        String remoteHost = request.getRemoteHost();
+        String remoteAddr = request.getRemoteAddr();
+        String result =  "Client " + remoteHost;
+        if (!remoteAddr.equals(remoteHost)) {
+            result = result + " (" + remoteAddr + ")";
+        }
+        return result;
+    }
+}
diff --git a/minifi-c2/minifi-c2-service/src/main/webapp/WEB-INF/web.xml b/minifi-c2/minifi-c2-service/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..7be7364
--- /dev/null
+++ b/minifi-c2/minifi-c2-service/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
+    <display-name>minifi-c2</display-name>
+    <context-param>
+        <param-name>contextClass</param-name>
+        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
+    </context-param>
+    <context-param>
+        <param-name>contextConfigLocation</param-name>
+        <param-value>org.apache.nifi.minifi.c2.configuration</param-value>
+    </context-param>
+    <listener>
+        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+    </listener>
+    <servlet>
+        <servlet-name>jerseySpring</servlet-name>
+        <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
+        <init-param>
+            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
+            <param-value>true</param-value>
+        </init-param>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jerseySpring</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+    <filter>
+        <filter-name>springSecurityFilterChain</filter-name>
+        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+    </filter>
+    <filter-mapping>
+        <filter-name>springSecurityFilterChain</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/minifi-c2/pom.xml b/minifi-c2/pom.xml
new file mode 100644
index 0000000..f4d5add
--- /dev/null
+++ b/minifi-c2/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>minifi</artifactId>
+        <groupId>org.apache.nifi.minifi</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>minifi-c2</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>minifi-c2-api</module>
+        <module>minifi-c2-cache</module>
+        <module>minifi-c2-provider</module>
+        <module>minifi-c2-service</module>
+        <module>minifi-c2-jetty</module>
+        <module>minifi-c2-assembly</module>
+        <module>minifi-c2-docker</module>
+        <module>minifi-c2-integration-tests</module>
+    </modules>
+</project>
diff --git a/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-resources/src/main/resources/conf/bootstrap.conf b/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-resources/src/main/resources/conf/bootstrap.conf
index 537536e..85204cc 100644
--- a/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-resources/src/main/resources/conf/bootstrap.conf
+++ b/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-resources/src/main/resources/conf/bootstrap.conf
@@ -54,6 +54,10 @@
 #nifi.minifi.notifier.ingestors.pull.http.hostname=localhost
 # Port on which to pull configurations from
 #nifi.minifi.notifier.ingestors.pull.http.port=4567
+# Path to pull configurations from
+#nifi.minifi.notifier.ingestors.pull.http.path=/c2/config
+# Query string to pull configurations with
+#nifi.minifi.notifier.ingestors.pull.http.query=class=raspi3
 # Period on which to pull configurations from, defaults to 5 minutes if commented out
 #nifi.minifi.notifier.ingestors.pull.http.period.ms=300000
 
diff --git a/pom.xml b/pom.xml
index 3727ce4..3fa1f2a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
         <module>minifi-assembly</module>
         <module>minifi-toolkit</module>
         <module>minifi-docker</module>
+        <module>minifi-c2</module>
     </modules>
 
     <url>http://nifi.apache.org/minifi</url>
@@ -402,6 +403,11 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-toolkit-tls</artifactId>
+                <version>${org.apache.nifi.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
                 <artifactId>nifi-resources</artifactId>
                 <version>${org.apache.nifi.version}</version>
                 <classifier>resources</classifier>