You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by al...@apache.org on 2017/05/15 13:44:47 UTC

[3/4] nifi-minifi git commit: MINIFI-272 - Delegating C2 Provider, Caching ConfigService, Tests Proxy auth, caching fixes Added C2 readme

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/DelegatingConfigurationProviderUnsecureTest.java
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/DelegatingConfigurationProviderUnsecureTest.java b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/DelegatingConfigurationProviderUnsecureTest.java
new file mode 100644
index 0000000..8394427
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/java/org/apache/nifi/minifi/c2/integration/test/DelegatingConfigurationProviderUnsecureTest.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.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 DelegatingConfigurationProviderUnsecureTest extends AbstractTestUnsecure {
+    @ClassRule
+    public static DockerComposeRule docker = DockerComposeRule.builder()
+            .file("target/test-classes/docker-compose-DelegatingProviderUnsecureTest.yml")
+            .waitingForService("c2-upstream", new HttpStatusCodeHealthCheck(DelegatingConfigurationProviderUnsecureTest::getUnsecureConfigUrl, 400))
+            .waitingForService("c2", new HttpStatusCodeHealthCheck(DelegatingConfigurationProviderUnsecureTest::getUnsecureConfigUrl, 400))
+            .build();
+
+    @Before
+    public void setup() {
+        super.setup(docker);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure-delegating/conf/minifi-c2-context.xml
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure-delegating/conf/minifi-c2-context.xml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure-delegating/conf/minifi-c2-context.xml
new file mode 100644
index 0000000..e3e4976
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure-delegating/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.delegating.DelegatingConfigurationProvider">
+                    <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://c2-upstream:10443</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>

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-secure-rest/conf/minifi-c2-context.xml
----------------------------------------------------------------------
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
index d648d88..42c98ea 100644
--- 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
@@ -45,6 +45,9 @@
                     <constructor-arg>
                         <value>https://mocknifi:8443/nifi-api</value>
                     </constructor-arg>
+                    <constructor-arg>
+                        <value>${class}.v${version}</value>
+                    </constructor-arg>
                 </bean>
             </list>
         </constructor-arg>

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-unsecure-delegating/conf/minifi-c2-context.xml
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-unsecure-delegating/conf/minifi-c2-context.xml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-unsecure-delegating/conf/minifi-c2-context.xml
new file mode 100644
index 0000000..a246219
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-unsecure-delegating/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.delegating.DelegatingConfigurationProvider">
+                    <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://c2-upstream:10080</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>

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-unsecure-rest/conf/minifi-c2-context.xml
----------------------------------------------------------------------
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
index f2e4eee..3834cc8 100644
--- 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
@@ -44,6 +44,9 @@
                     <constructor-arg>
                         <value>http://mocknifi:8080/nifi-api</value>
                     </constructor-arg>
+                    <constructor-arg>
+                        <value>${class}.v${version}</value>
+                    </constructor-arg>
                 </bean>
             </list>
         </constructor-arg>

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-upstream-secure/conf/authorities.yaml
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-upstream-secure/conf/authorities.yaml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-upstream-secure/conf/authorities.yaml
new file mode 100644
index 0000000..22be134
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-upstream-secure/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=c2, OU=NIFI:
+  - C2

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-upstream-secure/conf/authorizations.yaml
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-upstream-secure/conf/authorizations.yaml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-upstream-secure/conf/authorizations.yaml
new file mode 100644
index 0000000..6d626f7
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2-upstream-secure/conf/authorizations.yaml
@@ -0,0 +1,32 @@
+# 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: C2
+      Query Parameters:
+        class: raspi4
+      Action: deny
+    - Authorization: C2
+      Action: allow
+
+  /c2/config/contentTypes:
+    Default Action: deny
+    Actions:
+    - Authorization: C2
+      Action: allow
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi2/config.text.yml.v1
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi2/config.text.yml.v1 b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi2/config.text.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.text.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: {}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi2/config.yml.v1
----------------------------------------------------------------------
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
deleted file mode 100644
index 116426f..0000000
--- a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi2/config.yml.v1
+++ /dev/null
@@ -1,63 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the \"License\"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an \"AS IS\" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-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: {}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.text.yml.v1
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.text.yml.v1 b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.text.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.text.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: {}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.text.yml.v2
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.text.yml.v2 b/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.text.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.text.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: {}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v1
----------------------------------------------------------------------
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
deleted file mode 100644
index 690cdaa..0000000
--- a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v1
+++ /dev/null
@@ -1,63 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the \"License\"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an \"AS IS\" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-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: {}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v2
----------------------------------------------------------------------
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
deleted file mode 100644
index 14750b6..0000000
--- a/minifi-c2/minifi-c2-integration-tests/src/test/resources/c2/files/raspi3/config.yml.v2
+++ /dev/null
@@ -1,63 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the \"License\"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an \"AS IS\" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-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: {}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-DelegatingProviderSecureTest.yml
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-DelegatingProviderSecureTest.yml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-DelegatingProviderSecureTest.yml
new file mode 100644
index 0000000..2c19308
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-DelegatingProviderSecureTest.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-upstream:
+    image: apacheminific2:${minifi.c2.version}
+    ports:
+      - "10443"
+    hostname: c2-upstream
+    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-upstream-secure/conf/authorities.yaml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/authorities.yaml
+      - ./c2-upstream-secure/conf/authorizations.yaml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/authorizations.yaml
+
+      - ./certificates-DelegatingConfigurationProviderSecureTest/c2-upstream/keystore.jks:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/keystore.jks
+      - ./certificates-DelegatingConfigurationProviderSecureTest/c2-upstream/truststore.jks:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/truststore.jks
+
+  c2:
+    image: apacheminific2:${minifi.c2.version}
+    ports:
+      - "10443"
+    hostname: c2
+    volumes:
+      - ./c2-secure-delegating/conf/minifi-c2-context.xml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/minifi-c2-context.xml
+
+      - ./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-DelegatingConfigurationProviderSecureTest/c2/keystore.jks:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/keystore.jks
+      - ./certificates-DelegatingConfigurationProviderSecureTest/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

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-DelegatingProviderUnsecureTest.yml
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-DelegatingProviderUnsecureTest.yml b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-DelegatingProviderUnsecureTest.yml
new file mode 100644
index 0000000..a5d0da4
--- /dev/null
+++ b/minifi-c2/minifi-c2-integration-tests/src/test/resources/docker-compose-DelegatingProviderUnsecureTest.yml
@@ -0,0 +1,32 @@
+# 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-upstream:
+    image: apacheminific2:${minifi.c2.version}
+    ports:
+      - "10080"
+    hostname: c2-upstream
+    volumes:
+      - ./c2/files:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/files
+  c2:
+    image: apacheminific2:${minifi.c2.version}
+    ports:
+      - "10080"
+    hostname: c2
+    volumes:
+      - ./c2-unsecure-delegating/conf/minifi-c2-context.xml:/opt/minifi-c2/minifi-c2-${minifi.c2.version}/conf/minifi-c2-context.xml

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-jetty/src/main/java/org/apache/nifi/minifi/c2/jetty/JettyServer.java
----------------------------------------------------------------------
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
index 5c8b1f8..abace06 100644
--- 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
@@ -57,10 +57,8 @@ public class JettyServer {
 
         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 {
+        if (properties.isSecure()) {
+            SslContextFactory sslContextFactory = properties.getSslContextFactory();
             HttpConfiguration config = new HttpConfiguration();
             config.setSecureScheme("https");
             config.setSecurePort(port);
@@ -72,6 +70,8 @@ public class JettyServer {
             serverConnector.setPort(port);
 
             server.addConnector(serverConnector);
+        } else {
+            server = new Server(port);
         }
 
         server.setHandler(handlers);

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/src/main/java/org/apache/nifi/minifi/c2/provider/cache/CacheConfigurationProvider.java
----------------------------------------------------------------------
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
index b0864ac..f65a86b 100644
--- 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
@@ -26,21 +26,21 @@ import java.util.List;
 import java.util.Map;
 
 public class CacheConfigurationProvider implements ConfigurationProvider {
-    private final String contentType;
+    private final List<String> contentTypes;
     private final ConfigurationCache configurationCache;
 
-    public CacheConfigurationProvider(String contentType, ConfigurationCache configurationCache) {
-        this.contentType = contentType;
+    public CacheConfigurationProvider(List<String> contentTypes, ConfigurationCache configurationCache) {
+        this.contentTypes = contentTypes;
         this.configurationCache = configurationCache;
     }
 
     @Override
-    public String getContentType() {
-        return contentType;
+    public List<String> getContentTypes() {
+        return contentTypes;
     }
 
     @Override
-    public Configuration getConfiguration(Integer version, Map<String, List<String>> parameters) throws ConfigurationProviderException {
-        return configurationCache.getCacheFileInfo(parameters).getConfiguration(version);
+    public Configuration getConfiguration(String contentType, Integer version, Map<String, List<String>> parameters) throws ConfigurationProviderException {
+        return configurationCache.getCacheFileInfo(contentType, parameters).getConfiguration(version);
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-cache/src/test/java/org/apache/nifi/minifi/c2/provider/cache/CacheConfigurationProviderTest.java
----------------------------------------------------------------------
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
index d038194..bcc37e9 100644
--- 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
@@ -21,10 +21,10 @@ 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.Arrays;
 import java.util.List;
 import java.util.Map;
 
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.when;
 
 public class CacheConfigurationProviderTest {
     public static final String TEST_CONTENT_TYPE = "test/contenttype";
+    public static final String TEST_CONTENT_TYPE_2 = "test/contenttype2";
 
     private CacheConfigurationProvider cacheConfigurationProvider;
     private ConfigurationCache configConfigurationCache;
@@ -41,25 +42,30 @@ public class CacheConfigurationProviderTest {
     @Before
     public void setup() {
         configConfigurationCache = mock(ConfigurationCache.class);
-        cacheConfigurationProvider = new CacheConfigurationProvider(TEST_CONTENT_TYPE, configConfigurationCache);
+        cacheConfigurationProvider = new CacheConfigurationProvider(Arrays.asList(TEST_CONTENT_TYPE, TEST_CONTENT_TYPE_2), configConfigurationCache);
     }
 
     @Test
     public void testContentType() {
-        assertEquals(TEST_CONTENT_TYPE, cacheConfigurationProvider.getContentType());
+        assertEquals(Arrays.asList(TEST_CONTENT_TYPE, TEST_CONTENT_TYPE_2), cacheConfigurationProvider.getContentTypes());
     }
 
     @Test
     public void testGetConfiguration() throws ConfigurationProviderException {
         int version = 99;
 
-        Map<String, List<String>> parameters = mock(Map.class);
         ConfigurationCacheFileInfo configurationCacheFileInfo = mock(ConfigurationCacheFileInfo.class);
+        ConfigurationCacheFileInfo configurationCacheFileInfo2 = mock(ConfigurationCacheFileInfo.class);
         WriteableConfiguration configuration = mock(WriteableConfiguration.class);
+        WriteableConfiguration configuration2 = mock(WriteableConfiguration.class);
 
-        when(configConfigurationCache.getCacheFileInfo(parameters)).thenReturn(configurationCacheFileInfo);
+        Map<String, List<String>> parameters = mock(Map.class);
+        when(configConfigurationCache.getCacheFileInfo(TEST_CONTENT_TYPE, parameters)).thenReturn(configurationCacheFileInfo);
+        when(configConfigurationCache.getCacheFileInfo(TEST_CONTENT_TYPE_2, parameters)).thenReturn(configurationCacheFileInfo2);
         when(configurationCacheFileInfo.getConfiguration(version)).thenReturn(configuration);
+        when(configurationCacheFileInfo2.getConfiguration(version)).thenReturn(configuration2);
 
-        assertEquals(configuration, cacheConfigurationProvider.getConfiguration(version, parameters));
+        assertEquals(configuration, cacheConfigurationProvider.getConfiguration(TEST_CONTENT_TYPE, version, parameters));
+        assertEquals(configuration2, cacheConfigurationProvider.getConfiguration(TEST_CONTENT_TYPE_2, version, parameters));
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/pom.xml
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/pom.xml b/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/pom.xml
new file mode 100644
index 0000000..4b7e7ab
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/pom.xml
@@ -0,0 +1,53 @@
+<?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-delegating</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.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-provider-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/src/main/java/org/apache/nifi/minifi/c2/provider/delegating/DelegatingConfigurationProvider.java
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/src/main/java/org/apache/nifi/minifi/c2/provider/delegating/DelegatingConfigurationProvider.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/src/main/java/org/apache/nifi/minifi/c2/provider/delegating/DelegatingConfigurationProvider.java
new file mode 100644
index 0000000..04f6017
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/src/main/java/org/apache/nifi/minifi/c2/provider/delegating/DelegatingConfigurationProvider.java
@@ -0,0 +1,171 @@
+/*
+ * 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.delegating;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.io.IOUtils;
+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.security.authorization.AuthorizationException;
+import org.apache.nifi.minifi.c2.provider.util.HttpConnector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DelegatingConfigurationProvider implements ConfigurationProvider {
+    public static final Pattern errorPattern = Pattern.compile("^Server returned HTTP response code: ([0-9]+) for URL:.*");
+    private static final Logger logger = LoggerFactory.getLogger(DelegatingConfigurationProvider.class);
+    private final ConfigurationCache configurationCache;
+    private final HttpConnector httpConnector;
+    private final ObjectMapper objectMapper;
+
+    public DelegatingConfigurationProvider(ConfigurationCache configurationCache, String delegateUrl) throws InvalidParameterException, GeneralSecurityException, IOException {
+        this(configurationCache, new HttpConnector(delegateUrl));
+    }
+
+    public DelegatingConfigurationProvider(ConfigurationCache configurationCache, HttpConnector httpConnector) {
+        this.configurationCache = configurationCache;
+        this.httpConnector = httpConnector;
+        this.objectMapper = new ObjectMapper();
+    }
+
+    @Override
+    public List<String> getContentTypes() throws ConfigurationProviderException {
+        try {
+            HttpURLConnection httpURLConnection = httpConnector.get("/c2/config/contentTypes");
+            try {
+                List<String> contentTypes = objectMapper.readValue(httpURLConnection.getInputStream(), List.class);
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Got content types: " + contentTypes);
+                }
+                return contentTypes;
+            } finally {
+                httpURLConnection.disconnect();
+            }
+        } catch (IOException e) {
+            throw new ConfigurationProviderException("Unable to get content types from delegate.", e);
+        }
+    }
+
+    @Override
+    public Configuration getConfiguration(String contentType, Integer version, Map<String, List<String>> parameters) throws ConfigurationProviderException {
+        HttpURLConnection remoteC2ServerConnection = null;
+        try {
+            if (version == null) {
+                remoteC2ServerConnection = getDelegateConnection(contentType, parameters);
+                version = Integer.parseInt(remoteC2ServerConnection.getHeaderField("X-Content-Version"));
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Got current version " + version + " from upstream.");
+                }
+            }
+            ConfigurationCacheFileInfo cacheFileInfo = configurationCache.getCacheFileInfo(contentType, parameters);
+            WriteableConfiguration configuration = cacheFileInfo.getConfiguration(version);
+            if (!configuration.exists()) {
+                if (remoteC2ServerConnection == null) {
+                    remoteC2ServerConnection = getDelegateConnection(contentType, parameters);
+                }
+                try (InputStream inputStream = remoteC2ServerConnection.getInputStream();
+                     OutputStream outputStream = configuration.getOutputStream()) {
+                    IOUtils.copy(inputStream, outputStream);
+                } catch (IOException e) {
+                    throw new ConfigurationProviderException("Unable to copy remote configuration to cache.", e);
+                }
+            }
+            return configuration;
+        } finally {
+            if (remoteC2ServerConnection != null) {
+                remoteC2ServerConnection.disconnect();
+            }
+        }
+    }
+
+    protected HttpURLConnection getDelegateConnection(String contentType, Map<String, List<String>> parameters) throws ConfigurationProviderException {
+        StringBuilder queryStringBuilder = new StringBuilder();
+        try {
+            parameters.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEachOrdered(e -> e.getValue().stream().sorted().forEachOrdered(v -> {
+                try {
+                    queryStringBuilder.append(URLEncoder.encode(e.getKey(), "UTF-8")).append("=").append(URLEncoder.encode(v, "UTF-8"));
+                } catch (UnsupportedEncodingException ex) {
+                    throw new ConfigurationProviderException("Unsupported encoding.", ex).wrap();
+                }
+                queryStringBuilder.append("&");
+            }));
+        } catch (ConfigurationProviderException.Wrapper e) {
+            throw e.unwrap();
+        }
+        String url = "/c2/config";
+        if (queryStringBuilder.length() > 0) {
+            queryStringBuilder.setLength(queryStringBuilder.length() - 1);
+            url = url + "?" + queryStringBuilder.toString();
+        }
+        HttpURLConnection httpURLConnection = httpConnector.get(url);
+        httpURLConnection.setRequestProperty("Accepts", contentType);
+        try {
+            int responseCode;
+            try {
+                responseCode = httpURLConnection.getResponseCode();
+            } catch (IOException e) {
+                Matcher matcher = errorPattern.matcher(e.getMessage());
+                if (matcher.matches()) {
+                    responseCode = Integer.parseInt(matcher.group(1));
+                } else {
+                    throw e;
+                }
+            }
+            if (responseCode >= 400) {
+                String message = "";
+                InputStream inputStream = httpURLConnection.getErrorStream();
+                if (inputStream != null) {
+                    try {
+                        message = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
+                    } finally {
+                        inputStream.close();
+                    }
+                }
+                if (responseCode == 400) {
+                    throw new InvalidParameterException(message);
+                } else if (responseCode == 403) {
+                    throw new AuthorizationException("Got authorization exception from upstream server " + message);
+                } else {
+                    throw new ConfigurationProviderException(message);
+                }
+            }
+        } catch (IOException e) {
+            throw new ConfigurationProviderException("Unable to get response code from upstream server.", e);
+        }
+        return httpURLConnection;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/src/test/java/org/apache/nifi/minifi/c2/provider/delegating/DelegatingConfigurationProviderTest.java
----------------------------------------------------------------------
diff --git a/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/src/test/java/org/apache/nifi/minifi/c2/provider/delegating/DelegatingConfigurationProviderTest.java b/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/src/test/java/org/apache/nifi/minifi/c2/provider/delegating/DelegatingConfigurationProviderTest.java
new file mode 100644
index 0000000..3127ca7
--- /dev/null
+++ b/minifi-c2/minifi-c2-provider/minifi-c2-provider-delegating/src/test/java/org/apache/nifi/minifi/c2/provider/delegating/DelegatingConfigurationProviderTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.delegating;
+
+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.security.authorization.AuthorizationException;
+import org.apache.nifi.minifi.c2.provider.util.HttpConnector;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DelegatingConfigurationProviderTest {
+    private ConfigurationCache configurationCache;
+    private HttpConnector httpConnector;
+    private HttpURLConnection httpURLConnection;
+    private DelegatingConfigurationProvider delegatingConfigurationProvider;
+    private Map<String, List<String>> parameters;
+    private Integer version;
+    private String endpointPath;
+    private String contentType;
+
+    @Before
+    public void setup() throws ConfigurationProviderException {
+        contentType = "text/yml";
+        version = 2;
+        parameters = new HashMap<>();
+        parameters.put("net", Collections.singletonList("edge"));
+        parameters.put("class", Collections.singletonList("raspi3"));
+        parameters.put("version", Collections.singletonList(Integer.toString(version)));
+        endpointPath = "/c2/config?class=raspi3&net=edge&version=2";
+        initMocks();
+    }
+
+    public void initMocks() throws ConfigurationProviderException {
+        configurationCache = mock(ConfigurationCache.class);
+        httpConnector = mock(HttpConnector.class);
+        httpURLConnection = mock(HttpURLConnection.class);
+        delegatingConfigurationProvider = new DelegatingConfigurationProvider(configurationCache, httpConnector);
+        when(httpConnector.get(endpointPath)).thenReturn(httpURLConnection);
+    }
+
+    @Test
+    public void testGetDelegateConnectionNoParameters() throws ConfigurationProviderException {
+        endpointPath = "/c2/config";
+        initMocks();
+        assertEquals(httpURLConnection, delegatingConfigurationProvider.getDelegateConnection(contentType, Collections.emptyMap()));
+        verify(httpURLConnection).setRequestProperty("Accepts", contentType);
+    }
+
+    @Test
+    public void testGetDelegateConnectionParameters() throws ConfigurationProviderException {
+        assertEquals(httpURLConnection, delegatingConfigurationProvider.getDelegateConnection(contentType, parameters));
+        verify(httpURLConnection).setRequestProperty("Accepts", contentType);
+    }
+
+    @Test(expected = AuthorizationException.class)
+    public void testGetDelegateConnection403() throws ConfigurationProviderException, IOException {
+        when(httpURLConnection.getResponseCode()).thenReturn(403);
+        delegatingConfigurationProvider.getDelegateConnection(contentType, parameters);
+    }
+
+    @Test(expected = InvalidParameterException.class)
+    public void testGetDelegateConnection400() throws IOException, ConfigurationProviderException {
+        when(httpURLConnection.getResponseCode()).thenThrow(new IOException("Server returned HTTP response code: 400 for URL: " + endpointPath));
+        delegatingConfigurationProvider.getDelegateConnection(contentType, parameters);
+    }
+
+    @Test(expected = ConfigurationProviderException.class)
+    public void testGetDelegateConnection401() throws IOException, ConfigurationProviderException {
+        when(httpURLConnection.getResponseCode()).thenReturn(401);
+        delegatingConfigurationProvider.getDelegateConnection(contentType, parameters);
+    }
+
+    @Test(expected = ConfigurationProviderException.class)
+    public void testGetContentTypesMalformed() throws ConfigurationProviderException, IOException {
+        endpointPath = "/c2/config/contentTypes";
+        initMocks();
+        when(httpURLConnection.getInputStream()).thenReturn(new ByteArrayInputStream("[malformed".getBytes(StandardCharsets.UTF_8)));
+        delegatingConfigurationProvider.getContentTypes();
+    }
+
+    @Test(expected = ConfigurationProviderException.class)
+    public void testGetContentTypesIOE() throws ConfigurationProviderException, IOException {
+        endpointPath = "/c2/config/contentTypes";
+        initMocks();
+        when(httpURLConnection.getInputStream()).thenThrow(new IOException());
+        delegatingConfigurationProvider.getContentTypes();
+    }
+
+    @Test
+    public void testGetContentTypes() throws ConfigurationProviderException, IOException {
+        endpointPath = "/c2/config/contentTypes";
+        initMocks();
+        List<String> contentTypes = Arrays.asList(contentType, "application/json");
+        when(httpURLConnection.getInputStream()).thenReturn(new ByteArrayInputStream(("[\"" + String.join("\", \"", contentTypes) + "\"]").getBytes(StandardCharsets.UTF_8)));
+        assertEquals(contentTypes, delegatingConfigurationProvider.getContentTypes());
+    }
+
+    @Test
+    public void testGetConfigurationExistsWithVersion() throws ConfigurationProviderException {
+        ConfigurationCacheFileInfo configurationCacheFileInfo = mock(ConfigurationCacheFileInfo.class);
+        WriteableConfiguration configuration = mock(WriteableConfiguration.class);
+        when(configurationCache.getCacheFileInfo(contentType, parameters)).thenReturn(configurationCacheFileInfo);
+        when(configurationCacheFileInfo.getConfiguration(version)).thenReturn(configuration);
+        when(configuration.exists()).thenReturn(true);
+
+        assertEquals(configuration, delegatingConfigurationProvider.getConfiguration(contentType, version, parameters));
+    }
+
+    @Test
+    public void testGetConfigurationDoesntExistWithVersion() throws ConfigurationProviderException, IOException {
+        ConfigurationCacheFileInfo configurationCacheFileInfo = mock(ConfigurationCacheFileInfo.class);
+        WriteableConfiguration configuration = mock(WriteableConfiguration.class);
+        byte[] payload = "payload".getBytes(StandardCharsets.UTF_8);
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        when(httpURLConnection.getInputStream()).thenReturn(new ByteArrayInputStream(payload));
+        when(configuration.getOutputStream()).thenReturn(output);
+        when(configurationCache.getCacheFileInfo(contentType, parameters)).thenReturn(configurationCacheFileInfo);
+        when(configurationCacheFileInfo.getConfiguration(version)).thenReturn(configuration);
+        when(configuration.exists()).thenReturn(false);
+
+        assertEquals(configuration, delegatingConfigurationProvider.getConfiguration(contentType, version, parameters));
+        assertArrayEquals(payload, output.toByteArray());
+    }
+
+    @Test
+    public void testGetConfigurationExistsWithNoVersion() throws ConfigurationProviderException {
+        parameters.remove("version");
+        endpointPath = "/c2/config?class=raspi3&net=edge";
+        initMocks();
+        ConfigurationCacheFileInfo configurationCacheFileInfo = mock(ConfigurationCacheFileInfo.class);
+        WriteableConfiguration configuration = mock(WriteableConfiguration.class);
+        when(httpURLConnection.getHeaderField("X-Content-Version")).thenReturn("2");
+        when(configurationCache.getCacheFileInfo(contentType, parameters)).thenReturn(configurationCacheFileInfo);
+        when(configurationCacheFileInfo.getConfiguration(version)).thenReturn(configuration);
+        when(configuration.exists()).thenReturn(true);
+
+        assertEquals(configuration, delegatingConfigurationProvider.getConfiguration(contentType, null, parameters));
+    }
+
+    @Test
+    public void testGetConfigurationDoesntExistWithNoVersion() throws ConfigurationProviderException, IOException {
+        parameters.remove("version");
+        endpointPath = "/c2/config?class=raspi3&net=edge";
+        initMocks();
+        ConfigurationCacheFileInfo configurationCacheFileInfo = mock(ConfigurationCacheFileInfo.class);
+        WriteableConfiguration configuration = mock(WriteableConfiguration.class);
+        byte[] payload = "payload".getBytes(StandardCharsets.UTF_8);
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        when(httpURLConnection.getInputStream()).thenReturn(new ByteArrayInputStream(payload));
+        when(configuration.getOutputStream()).thenReturn(output);
+        when(httpURLConnection.getHeaderField("X-Content-Version")).thenReturn("2");
+        when(configurationCache.getCacheFileInfo(contentType, parameters)).thenReturn(configurationCacheFileInfo);
+        when(configurationCacheFileInfo.getConfiguration(version)).thenReturn(configuration);
+        when(configuration.exists()).thenReturn(false);
+
+        assertEquals(configuration, delegatingConfigurationProvider.getConfiguration(contentType, null, parameters));
+        assertArrayEquals(payload, output.toByteArray());
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/pom.xml
----------------------------------------------------------------------
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
index 8595246..43c5225 100644
--- 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
@@ -32,6 +32,11 @@ limitations under the License.
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.nifi.minifi</groupId>
+            <artifactId>minifi-c2-provider-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-core</artifactId>
             <version>2.8.7</version>

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConfigurationProvider.java
----------------------------------------------------------------------
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
index 77175d7..8f0bf2c 100644
--- 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
@@ -23,9 +23,9 @@ 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.c2.provider.util.HttpConnector;
 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;
@@ -38,12 +38,17 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.Collections;
 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.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
@@ -52,32 +57,58 @@ public class NiFiRestConfigurationProvider implements ConfigurationProvider {
     private static final Logger logger = LoggerFactory.getLogger(NiFiRestConfigurationProvider.class);
     private final JsonFactory jsonFactory = new JsonFactory();
     private final ConfigurationCache configurationCache;
-    private final NiFiRestConnector niFiRestConnector;
+    private final HttpConnector httpConnector;
+    private final String templateNamePattern;
 
-    public NiFiRestConfigurationProvider(ConfigurationCache configurationCache, String nifiUrl) throws InvalidParameterException, GeneralSecurityException, IOException {
-        this(configurationCache, new NiFiRestConnector(nifiUrl));
+    public NiFiRestConfigurationProvider(ConfigurationCache configurationCache, String nifiUrl, String templateNamePattern) throws InvalidParameterException, GeneralSecurityException, IOException {
+        this(configurationCache, new HttpConnector(nifiUrl), templateNamePattern);
     }
 
-    public NiFiRestConfigurationProvider(ConfigurationCache configurationCache, NiFiRestConnector niFiRestConnector) {
+    public NiFiRestConfigurationProvider(ConfigurationCache configurationCache, HttpConnector httpConnector, String templateNamePattern) {
         this.configurationCache = configurationCache;
-        this.niFiRestConnector = niFiRestConnector;
+        this.httpConnector = httpConnector;
+        this.templateNamePattern = templateNamePattern;
     }
 
     @Override
-    public String getContentType() {
-        return CONTENT_TYPE;
+    public List<String> getContentTypes() {
+        return Collections.singletonList(CONTENT_TYPE);
     }
 
     @Override
-    public Configuration getConfiguration(Integer version, Map<String, List<String>> parameters) throws ConfigurationProviderException {
-        ConfigurationCacheFileInfo configurationCacheFileInfo = configurationCache.getCacheFileInfo(parameters);
+    public Configuration getConfiguration(String contentType, Integer version, Map<String, List<String>> parameters) throws ConfigurationProviderException {
+        if (!CONTENT_TYPE.equals(contentType)) {
+            throw new ConfigurationProviderException("Unsupported content type: " + contentType + " supported value is " + CONTENT_TYPE);
+        }
+        String filename = templateNamePattern;
+        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.");
+            }
+            filename = filename.replaceAll(Pattern.quote("${" + entry.getKey() + "}"), entry.getValue().get(0));
+        }
+        int index = filename.indexOf("${");
+        while (index != -1) {
+            int endIndex = filename.indexOf("}", index);
+            if (endIndex == -1) {
+                break;
+            }
+            String variable = filename.substring(index + 2, endIndex);
+            if (!"version".equals(variable)) {
+                throw new InvalidParameterException("Found unsubstituted parameter " + variable);
+            }
+            index = endIndex + 1;
+        }
+
         String id = null;
         if (version == null) {
-            Pair<String, Integer> maxIdAndVersion = getMaxIdAndVersion(configurationCacheFileInfo);
+            String filenamePattern = Arrays.stream(filename.split(Pattern.quote("${version}"), -1)).map(Pattern::quote).collect(Collectors.joining("([0-9+])"));
+            Pair<String, Integer> maxIdAndVersion = getMaxIdAndVersion(filenamePattern);
             id = maxIdAndVersion.getFirst();
             version = maxIdAndVersion.getSecond();
         }
-        WriteableConfiguration configuration = configurationCacheFileInfo.getConfiguration(version);
+        filename = filename.replaceAll(Pattern.quote("${version}"), Integer.toString(version));
+        WriteableConfiguration configuration = configurationCache.getCacheFileInfo(contentType, parameters).getConfiguration(version);
         if (configuration.exists()) {
             if (logger.isDebugEnabled()) {
                 logger.debug("Configuration " + configuration + " exists and can be served from configurationCache.");
@@ -88,11 +119,18 @@ public class NiFiRestConfigurationProvider implements ConfigurationProvider {
             }
             if (id == null) {
                 try {
-                    String filename = configuration.getName();
+                    String tmpFilename = templateNamePattern;
+                    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.");
+                        }
+                        tmpFilename = tmpFilename.replaceAll(Pattern.quote("${" + entry.getKey() + "}"), entry.getValue().get(0));
+                    }
                     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));
+                        String finalFilename = filename;
+                        id = streamCloseablePair.getFirst().filter(p -> finalFilename.equals(p.getSecond())).map(Pair::getFirst).findFirst()
+                                .orElseThrow(() -> new InvalidParameterException("Unable to find template named " + finalFilename));
                     } finally {
                         streamCloseablePair.getSecond().close();
                     }
@@ -101,7 +139,7 @@ public class NiFiRestConfigurationProvider implements ConfigurationProvider {
                 }
             }
 
-            HttpURLConnection urlConnection = niFiRestConnector.get("/templates/" + id + "/download");
+            HttpURLConnection urlConnection = httpConnector.get("/templates/" + id + "/download");
 
             try (InputStream inputStream = urlConnection.getInputStream()){
                 ConfigSchema configSchema = ConfigMain.transformTemplateToSchema(inputStream);
@@ -118,27 +156,28 @@ public class NiFiRestConfigurationProvider implements ConfigurationProvider {
     }
 
     private Pair<Stream<Pair<String, String>>, Closeable> getIdAndFilenameStream() throws ConfigurationProviderException, IOException {
-        TemplatesIterator templatesIterator = new TemplatesIterator(niFiRestConnector, jsonFactory);
+        TemplatesIterator templatesIterator = new TemplatesIterator(httpConnector, 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 {
+    private Pair<Stream<Pair<String, Integer>>, Closeable> getIdAndVersionStream(String filenamePattern) throws ConfigurationProviderException, IOException {
+        Pattern filename = Pattern.compile(filenamePattern);
         Pair<Stream<Pair<String, String>>, Closeable> streamCloseablePair = getIdAndFilenameStream();
         return new Pair<>(streamCloseablePair.getFirst().map(p -> {
-            Integer version = configurationCacheFileInfo.getVersionIfMatch(p.getSecond());
-            if (version == null) {
+            Matcher matcher = filename.matcher(p.getSecond());
+            if (!matcher.matches()) {
                 return null;
             }
-            return new Pair<>(p.getFirst(), version);
+            return new Pair<>(p.getFirst(), Integer.parseInt(matcher.group(1)));
         }).filter(Objects::nonNull), streamCloseablePair.getSecond());
     }
 
-    private Pair<String, Integer> getMaxIdAndVersion(ConfigurationCacheFileInfo configurationCacheFileInfo) throws ConfigurationProviderException {
+    private Pair<String, Integer> getMaxIdAndVersion(String filenamePattern) throws ConfigurationProviderException {
         try {
-            Pair<Stream<Pair<String, Integer>>, Closeable> streamCloseablePair = getIdAndVersionStream(configurationCacheFileInfo);
+            Pair<Stream<Pair<String, Integer>>, Closeable> streamCloseablePair = getIdAndVersionStream(filenamePattern);
             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]+"));
+                        .orElseThrow(() -> new ConfigurationProviderException("Didn't find any templates that matched " + filenamePattern));
             } finally {
                 streamCloseablePair.getSecond().close();
             }

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConnector.java
----------------------------------------------------------------------
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
deleted file mode 100644
index 9a0befc..0000000
--- a/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConnector.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.nifi.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);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/main/java/org/apache/nifi/minifi/c2/provider/nifi/rest/TemplatesIterator.java
----------------------------------------------------------------------
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
index afe6c6e..947a853 100644
--- 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
@@ -22,6 +22,7 @@ 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 org.apache.nifi.minifi.c2.provider.util.HttpConnector;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -38,8 +39,8 @@ public class TemplatesIterator implements Iterator<Pair<String, String>>, Closea
     private final JsonParser parser;
     private Pair<String, String> next;
 
-    public TemplatesIterator(NiFiRestConnector niFiRestConnector, JsonFactory jsonFactory) throws ConfigurationProviderException, IOException {
-        urlConnection = niFiRestConnector.get(FLOW_TEMPLATES);
+    public TemplatesIterator(HttpConnector httpConnector, JsonFactory jsonFactory) throws ConfigurationProviderException, IOException {
+        urlConnection = httpConnector.get(FLOW_TEMPLATES);
         inputStream = urlConnection.getInputStream();
         parser = jsonFactory.createParser(inputStream);
         while (parser.nextToken() != JsonToken.END_OBJECT) {

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/4dc0ce30/minifi-c2/minifi-c2-provider/minifi-c2-provider-nifi-rest/src/test/java/org/apache/nifi/minifi/c2/provider/nifi/rest/NiFiRestConfigurationProviderTest.java
----------------------------------------------------------------------
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
index 2cb3a8d..efc7f6c 100644
--- 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
@@ -18,6 +18,7 @@
 package org.apache.nifi.minifi.c2.provider.nifi.rest;
 
 import org.apache.nifi.minifi.c2.api.cache.ConfigurationCache;
+import org.apache.nifi.minifi.c2.provider.util.HttpConnector;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -25,6 +26,7 @@ import org.junit.Test;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Collections;
 import java.util.Comparator;
 
 import static org.junit.Assert.assertEquals;
@@ -32,15 +34,15 @@ import static org.mockito.Mockito.mock;
 
 public class NiFiRestConfigurationProviderTest {
     private ConfigurationCache configConfigurationCache;
-    private NiFiRestConnector niFiRestConnector;
+    private HttpConnector httpConnector;
     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);
+        httpConnector = mock(HttpConnector.class);
+        niFiRestConfigurationProvider = new NiFiRestConfigurationProvider(configConfigurationCache, httpConnector, "${class}.v${version}");
         cachePath = Files.createTempDirectory(NiFiRestConfigurationProviderTest.class.getCanonicalName());
     }
 
@@ -59,8 +61,6 @@ public class NiFiRestConfigurationProviderTest {
 
     @Test
     public void testContentType() {
-        assertEquals(NiFiRestConfigurationProvider.CONTENT_TYPE, niFiRestConfigurationProvider.getContentType());
+        assertEquals(Collections.singletonList(NiFiRestConfigurationProvider.CONTENT_TYPE), niFiRestConfigurationProvider.getContentTypes());
     }
-
-
 }