You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by jp...@apache.org on 2016/04/19 23:00:16 UTC

nifi-minifi git commit: MINIFI-12 initial commit of http config change notifier

Repository: nifi-minifi
Updated Branches:
  refs/heads/master 11f220d8d -> a1d2fd3fe


MINIFI-12 initial commit of http config change notifier

This closes #13


Project: http://git-wip-us.apache.org/repos/asf/nifi-minifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi-minifi/commit/a1d2fd3f
Tree: http://git-wip-us.apache.org/repos/asf/nifi-minifi/tree/a1d2fd3f
Diff: http://git-wip-us.apache.org/repos/asf/nifi-minifi/diff/a1d2fd3f

Branch: refs/heads/master
Commit: a1d2fd3fe6b0b2cdd50394e2451aa10d5c9c81e9
Parents: 11f220d
Author: Joseph Percivall <jo...@yahoo.com>
Authored: Thu Apr 14 19:01:54 2016 -0400
Committer: Joseph Percivall <jo...@yahoo.com>
Committed: Tue Apr 19 16:59:43 2016 -0400

----------------------------------------------------------------------
 .travis.yml                                     |   2 -
 minifi-assembly/NOTICE                          |   7 +-
 minifi-bootstrap/pom.xml                        |  20 ++
 .../configuration/RestChangeNotifier.java       | 259 +++++++++++++++++++
 .../configuration/TestRestChangeNotifier.java   |  51 ++++
 .../TestRestChangeNotifierSSL.java              |  96 +++++++
 .../configuration/util/MockChangeListener.java  |  46 ++++
 .../util/TestRestChangeNotifierCommon.java      |  89 +++++++
 .../src/test/resources/localhost-ks.jks         | Bin 0 -> 3512 bytes
 .../src/test/resources/localhost-ts.jks         | Bin 0 -> 1816 bytes
 .../src/test/resources/testUploadFile.txt       |  18 ++
 .../src/main/resources/conf/bootstrap.conf      |   6 +
 pom.xml                                         |  28 +-
 13 files changed, 616 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index b5c02d0..811a4c2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,8 +5,6 @@ os:
 
 jdk:
   - oraclejdk8
-  - oraclejdk7
-  - openjdk7
 
 # before_install aids in a couple workarounds for issues within the Travis-CI environment
 #   1. Workaround for buffer overflow issues with OpenJDK versions of java as per https://github.com/travis-ci/travis-ci/issues/5227#issuecomment-165135711

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-assembly/NOTICE
----------------------------------------------------------------------
diff --git a/minifi-assembly/NOTICE b/minifi-assembly/NOTICE
index 90163f2..e7f6169 100644
--- a/minifi-assembly/NOTICE
+++ b/minifi-assembly/NOTICE
@@ -13,4 +13,9 @@ The following binary components are provided under the Apache Software License v
   (ASLv2) Apache NiFi
     The following NOTICE information applies:
       Apache NiFi
-      Copyright 2014-2016 The Apache Software Foundation
\ No newline at end of file
+      Copyright 2014-2016 The Apache Software Foundation
+
+  (ASLv2) Jetty
+    The following NOTICE information applies:
+       Jetty Web Container
+       Copyright 1995-2015 Mort Bay Consulting Pty Ltd.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-bootstrap/pom.xml
----------------------------------------------------------------------
diff --git a/minifi-bootstrap/pom.xml b/minifi-bootstrap/pom.xml
index 68bc60a..2616190 100644
--- a/minifi-bootstrap/pom.xml
+++ b/minifi-bootstrap/pom.xml
@@ -56,6 +56,26 @@ limitations under the License.
         </dependency>
 
         <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${jetty.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlet</artifactId>
+            <version>${jetty.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.squareup.okhttp</groupId>
+            <artifactId>okhttp</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-framework-core</artifactId>
             <version>0.6.0</version>

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/RestChangeNotifier.java
----------------------------------------------------------------------
diff --git a/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/RestChangeNotifier.java b/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/RestChangeNotifier.java
new file mode 100644
index 0000000..5807f89
--- /dev/null
+++ b/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/RestChangeNotifier.java
@@ -0,0 +1,259 @@
+/*
+ * 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.bootstrap.configuration;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import static org.apache.nifi.minifi.bootstrap.RunMiNiFi.NOTIFIER_PROPERTY_PREFIX;
+
+
+public class RestChangeNotifier implements ConfigurationChangeNotifier {
+
+    private final Set<ConfigurationChangeListener> configurationChangeListeners = new HashSet<>();
+    private final static Logger logger = LoggerFactory.getLogger(RestChangeNotifier.class);
+    private String configFile = null;
+    private final Server jetty;
+    public static final String GET_TEXT = "This is a config change listener for an Apache NiFi - MiNiFi instance.\n" +
+            "Use this rest server to upload a conf.yml to configure the MiNiFi instance.\n" +
+            "Send a POST http request to '/' to upload the file.";
+    public static final String POST_TEXT ="Configuration received, notifying listeners.\n";
+    public static final String OTHER_TEXT ="This is not a support HTTP operation. Please use GET to get more information or POST to upload a new config.yml file.\n";
+
+
+    public static final String POST = "POST";
+    public static final String GET = "GET";
+
+    public static final String PORT_KEY = NOTIFIER_PROPERTY_PREFIX + ".http.port";
+    public static final String HOST_KEY = NOTIFIER_PROPERTY_PREFIX + ".http.host";
+    public static final String TRUSTSTORE_LOCATION_KEY = NOTIFIER_PROPERTY_PREFIX + ".http.truststore.location";
+    public static final String TRUSTSTORE_PASSWORD_KEY = NOTIFIER_PROPERTY_PREFIX + ".http.truststore.password";
+    public static final String TRUSTSTORE_TYPE_KEY = NOTIFIER_PROPERTY_PREFIX + ".http.truststore.type";
+    public static final String KEYSTORE_LOCATION_KEY = NOTIFIER_PROPERTY_PREFIX + ".http.keystore.location";
+    public static final String KEYSTORE_PASSWORD_KEY = NOTIFIER_PROPERTY_PREFIX + ".http.keystore.password";
+    public static final String KEYSTORE_TYPE_KEY = NOTIFIER_PROPERTY_PREFIX + ".http.keystore.type";
+    public static final String NEED_CLIENT_AUTH_KEY = NOTIFIER_PROPERTY_PREFIX + ".http.need.client.auth";
+
+    public RestChangeNotifier(){
+        QueuedThreadPool queuedThreadPool = new QueuedThreadPool();
+        queuedThreadPool.setDaemon(true);
+        jetty = new Server(queuedThreadPool);
+    }
+
+    @Override
+    public void initialize(Properties properties) {
+        logger.info("Initializing");
+
+        // create the secure connector if keystore location is specified
+        if (properties.getProperty(KEYSTORE_LOCATION_KEY) != null) {
+            createSecureConnector(properties);
+        } else {
+            // create the unsecure connector otherwise
+            createConnector(properties);
+        }
+
+        HandlerCollection handlerCollection = new HandlerCollection(true);
+        handlerCollection.addHandler(new JettyHandler());
+        jetty.setHandler(handlerCollection);
+    }
+
+
+    @Override
+    public Set<ConfigurationChangeListener> getChangeListeners() {
+        return configurationChangeListeners;
+    }
+
+    @Override
+    public boolean registerListener(ConfigurationChangeListener listener) {
+        return configurationChangeListeners.add(listener);
+    }
+
+    @Override
+    public void notifyListeners() {
+        if (configFile == null){
+            throw new IllegalStateException("Attempting to notify listeners when there is no new config file.");
+        }
+
+        for (final ConfigurationChangeListener listener : getChangeListeners()) {
+            try (final ByteArrayInputStream fis = new ByteArrayInputStream(configFile.getBytes());) {
+                listener.handleChange(fis);
+            } catch (IOException ex) {
+                throw new IllegalStateException("Unable to read the changed file " + configFile, ex);
+            }
+        }
+
+        configFile = null;
+    }
+
+    @Override
+    public void start(){
+        try {
+            jetty.start();
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+
+    @Override
+    public void close() throws IOException {
+        logger.warn("Shutting down the jetty server");
+        try {
+            jetty.stop();
+            jetty.destroy();
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+        logger.warn("Done shutting down the jetty server");
+    }
+
+    public URI getURI(){
+        return jetty.getURI();
+    }
+
+    public int getPort(){
+        if (!jetty.isStarted()) {
+            throw new IllegalStateException("Jetty server not started");
+        }
+        return ((ServerConnector) jetty.getConnectors()[0]).getLocalPort();
+    }
+
+    public String getConfigString(){
+        return configFile;
+    }
+
+    private void setConfigFile(String configFile){
+        this.configFile = configFile;
+    }
+
+    private void createConnector(Properties properties) {
+        final ServerConnector http = new ServerConnector(jetty);
+
+        http.setPort(Integer.parseInt(properties.getProperty(PORT_KEY, "0")));
+        http.setHost(properties.getProperty(HOST_KEY, "localhost"));
+
+        // Severely taxed or distant environments may have significant delays when executing.
+        http.setIdleTimeout(30000L);
+        jetty.addConnector(http);
+
+        logger.info("Added an http connector on the host '{}' and port '{}'", new Object[]{http.getHost(), http.getPort()});
+    }
+
+    private void createSecureConnector(Properties properties) {
+        SslContextFactory ssl = new SslContextFactory();
+
+        if (properties.getProperty(KEYSTORE_LOCATION_KEY) != null) {
+            ssl.setKeyStorePath(properties.getProperty(KEYSTORE_LOCATION_KEY));
+            ssl.setKeyStorePassword(properties.getProperty(KEYSTORE_PASSWORD_KEY));
+            ssl.setKeyStoreType(properties.getProperty(KEYSTORE_TYPE_KEY));
+        }
+
+        if (properties.getProperty(TRUSTSTORE_LOCATION_KEY) != null) {
+            ssl.setTrustStorePath(properties.getProperty(TRUSTSTORE_LOCATION_KEY));
+            ssl.setTrustStorePassword(properties.getProperty(TRUSTSTORE_PASSWORD_KEY));
+            ssl.setTrustStoreType(properties.getProperty(TRUSTSTORE_TYPE_KEY));
+            ssl.setNeedClientAuth(Boolean.parseBoolean(properties.getProperty(NEED_CLIENT_AUTH_KEY, "true")));
+        }
+
+        // build the connector
+        final ServerConnector https = new ServerConnector(jetty, ssl);
+
+        // set host and port
+        https.setPort(Integer.parseInt(properties.getProperty(PORT_KEY,"0")));
+        https.setHost(properties.getProperty(HOST_KEY, "localhost"));
+
+        // Severely taxed environments may have significant delays when executing.
+        https.setIdleTimeout(30000L);
+
+        // add the connector
+        jetty.addConnector(https);
+
+        logger.info("Added an https connector on the host '{}' and port '{}'", new Object[]{https.getHost(), https.getPort()});
+    }
+
+
+    public class JettyHandler extends AbstractHandler {
+
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+                throws IOException, ServletException {
+
+            logRequest(request);
+
+            baseRequest.setHandled(true);
+
+            if(POST.equals(request.getMethod())) {
+                final StringBuilder configBuilder = new StringBuilder();
+                BufferedReader reader = request.getReader();
+                if(reader != null && reader.ready()){
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        configBuilder.append(line);
+                        configBuilder.append(System.getProperty("line.separator"));
+                    }
+                }
+                setConfigFile(configBuilder.substring(0,configBuilder.length()-1));
+                notifyListeners();
+                writeOutput(response, POST_TEXT, 200);
+            } else if(GET.equals(request.getMethod())) {
+                writeOutput(response, GET_TEXT, 200);
+            } else {
+                writeOutput(response, OTHER_TEXT, 404);
+            }
+        }
+
+        private void writeOutput(HttpServletResponse response, String responseText, int responseCode) throws IOException {
+            response.setStatus(responseCode);
+            response.setContentType("text/plain");
+            response.setContentLength(responseText.length());
+            try (PrintWriter writer = response.getWriter()) {
+                writer.print(responseText);
+                writer.flush();
+            }
+        }
+
+        private void logRequest(HttpServletRequest request){
+            logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+            logger.info("request method = " + request.getMethod());
+            logger.info("request url = " + request.getRequestURL());
+            logger.info("context path = " + request.getContextPath());
+            logger.info("request content type = " + request.getContentType());
+            logger.info("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/TestRestChangeNotifier.java
----------------------------------------------------------------------
diff --git a/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/TestRestChangeNotifier.java b/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/TestRestChangeNotifier.java
new file mode 100644
index 0000000..75b44e3
--- /dev/null
+++ b/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/TestRestChangeNotifier.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.bootstrap.configuration;
+
+
+import com.squareup.okhttp.OkHttpClient;
+import org.apache.nifi.minifi.bootstrap.configuration.util.TestRestChangeNotifierCommon;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.net.MalformedURLException;
+import java.util.Properties;
+
+
+public class TestRestChangeNotifier extends TestRestChangeNotifierCommon {
+
+    @BeforeClass
+    public static void setUp() throws InterruptedException, MalformedURLException {
+        Properties properties = new Properties();
+        restChangeNotifier = new RestChangeNotifier();
+        restChangeNotifier.initialize(properties);
+        restChangeNotifier.registerListener(mockChangeListener);
+        restChangeNotifier.start();
+
+        client = new OkHttpClient();
+
+        url = restChangeNotifier.getURI().toURL().toString();
+        Thread.sleep(1000);
+    }
+
+    @AfterClass
+    public static void stop() throws Exception {
+        restChangeNotifier.close();
+        client = null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/TestRestChangeNotifierSSL.java
----------------------------------------------------------------------
diff --git a/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/TestRestChangeNotifierSSL.java b/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/TestRestChangeNotifierSSL.java
new file mode 100644
index 0000000..908e693
--- /dev/null
+++ b/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/TestRestChangeNotifierSSL.java
@@ -0,0 +1,96 @@
+/*
+ * 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.bootstrap.configuration;
+
+
+import com.squareup.okhttp.OkHttpClient;
+import org.apache.nifi.minifi.bootstrap.configuration.util.TestRestChangeNotifierCommon;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Properties;
+
+
+public class TestRestChangeNotifierSSL extends TestRestChangeNotifierCommon {
+
+
+    @BeforeClass
+    public static void setUpHttps() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, UnrecoverableKeyException, KeyManagementException, InterruptedException {
+        Properties properties = new Properties();
+        properties.setProperty(RestChangeNotifier.TRUSTSTORE_LOCATION_KEY, "./src/test/resources/localhost-ts.jks");
+        properties.setProperty(RestChangeNotifier.TRUSTSTORE_PASSWORD_KEY, "localtest");
+        properties.setProperty(RestChangeNotifier.TRUSTSTORE_TYPE_KEY, "JKS");
+        properties.setProperty(RestChangeNotifier.KEYSTORE_LOCATION_KEY, "./src/test/resources/localhost-ks.jks");
+        properties.setProperty(RestChangeNotifier.KEYSTORE_PASSWORD_KEY, "localtest");
+        properties.setProperty(RestChangeNotifier.KEYSTORE_TYPE_KEY, "JKS");
+        properties.setProperty(RestChangeNotifier.NEED_CLIENT_AUTH_KEY, "true");
+        restChangeNotifier = new RestChangeNotifier();
+        restChangeNotifier.initialize(properties);
+        restChangeNotifier.registerListener(mockChangeListener);
+        restChangeNotifier.start();
+
+        client = new OkHttpClient();
+
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        trustManagerFactory.init(readKeyStore("./src/test/resources/localhost-ts.jks"));
+
+        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        keyManagerFactory.init(readKeyStore("./src/test/resources/localhost-ks.jks"), "localtest".toCharArray());
+
+        sslContext.init(keyManagerFactory.getKeyManagers(),trustManagerFactory.getTrustManagers(), new SecureRandom());
+        client.setSslSocketFactory(sslContext.getSocketFactory());
+
+        url = restChangeNotifier.getURI().toURL().toString();
+        Thread.sleep(1000);
+    }
+
+    @AfterClass
+    public static void stop() throws Exception {
+        restChangeNotifier.close();
+        client = null;
+    }
+
+    private static KeyStore readKeyStore(String path) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
+        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+
+        char[] password = "localtest".toCharArray();
+
+        java.io.FileInputStream fis = null;
+        try {
+            fis = new java.io.FileInputStream(path);
+            ks.load(fis, password);
+        } finally {
+            if (fis != null) {
+                fis.close();
+            }
+        }
+        return ks;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/util/MockChangeListener.java
----------------------------------------------------------------------
diff --git a/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/util/MockChangeListener.java b/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/util/MockChangeListener.java
new file mode 100644
index 0000000..6843889
--- /dev/null
+++ b/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/util/MockChangeListener.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.bootstrap.configuration.util;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeListener;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class MockChangeListener implements ConfigurationChangeListener {
+    String confFile;
+
+    @Override
+    public void handleChange(InputStream inputStream) {
+        try {
+            confFile = IOUtils.toString(inputStream, "UTF-8");
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public String getConfFile() {
+        return confFile;
+    }
+
+    public void setConfFile(String confFile) {
+        this.confFile = confFile;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/util/TestRestChangeNotifierCommon.java
----------------------------------------------------------------------
diff --git a/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/util/TestRestChangeNotifierCommon.java b/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/util/TestRestChangeNotifierCommon.java
new file mode 100644
index 0000000..b3c4f54
--- /dev/null
+++ b/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/util/TestRestChangeNotifierCommon.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.bootstrap.configuration.util;
+
+import com.squareup.okhttp.Headers;
+import com.squareup.okhttp.MediaType;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.RequestBody;
+import com.squareup.okhttp.Response;
+import org.apache.nifi.minifi.bootstrap.configuration.RestChangeNotifier;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public abstract class TestRestChangeNotifierCommon {
+
+    public static OkHttpClient client;
+    public static RestChangeNotifier restChangeNotifier;
+    public static final MediaType MEDIA_TYPE_MARKDOWN  = MediaType.parse("text/x-markdown; charset=utf-8");
+    public static String url;
+    public static MockChangeListener mockChangeListener = new MockChangeListener();
+
+    @Test
+    public void testGet() throws Exception {
+        assertEquals(1, restChangeNotifier.getChangeListeners().size());
+
+        Request request = new Request.Builder()
+                .url(url)
+                .build();
+
+        Response response = client.newCall(request).execute();
+        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
+
+        Headers responseHeaders = response.headers();
+        for (int i = 0; i < responseHeaders.size(); i++) {
+            System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
+        }
+
+        assertEquals(RestChangeNotifier.GET_TEXT, response.body().string());
+    }
+
+    @Test
+    public void testFileUpload() throws Exception {
+        assertEquals(1, restChangeNotifier.getChangeListeners().size());
+
+        File file = new File("src/test/resources/testUploadFile.txt");
+        assertTrue(file.exists());
+        assertTrue(file.canRead());
+
+        Request request = new Request.Builder()
+                .url(url)
+                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
+                .addHeader("charset","UTF-8")
+                .build();
+
+        Response response = client.newCall(request).execute();
+        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
+
+        Headers responseHeaders = response.headers();
+        for (int i = 0; i < responseHeaders.size(); i++) {
+            System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
+        }
+
+        assertEquals(RestChangeNotifier.POST_TEXT, response.body().string());
+
+        assertEquals(new String(Files.readAllBytes(file.toPath())), mockChangeListener.getConfFile());
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-bootstrap/src/test/resources/localhost-ks.jks
----------------------------------------------------------------------
diff --git a/minifi-bootstrap/src/test/resources/localhost-ks.jks b/minifi-bootstrap/src/test/resources/localhost-ks.jks
new file mode 100755
index 0000000..df36197
Binary files /dev/null and b/minifi-bootstrap/src/test/resources/localhost-ks.jks differ

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-bootstrap/src/test/resources/localhost-ts.jks
----------------------------------------------------------------------
diff --git a/minifi-bootstrap/src/test/resources/localhost-ts.jks b/minifi-bootstrap/src/test/resources/localhost-ts.jks
new file mode 100755
index 0000000..7824378
Binary files /dev/null and b/minifi-bootstrap/src/test/resources/localhost-ts.jks differ

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-bootstrap/src/test/resources/testUploadFile.txt
----------------------------------------------------------------------
diff --git a/minifi-bootstrap/src/test/resources/testUploadFile.txt b/minifi-bootstrap/src/test/resources/testUploadFile.txt
new file mode 100644
index 0000000..cbd1af8
--- /dev/null
+++ b/minifi-bootstrap/src/test/resources/testUploadFile.txt
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+ this is a test upload file created to test the RestChangeListener
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-resources/src/main/resources/conf/bootstrap.conf
----------------------------------------------------------------------
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 c9626da..107d9cc 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
@@ -33,6 +33,7 @@ nifi.minifi.config=./conf/config.yml
 
 # Notifiers to use for the associated agent, comma separated list of class names
 #nifi.minifi.notifier.components=org.apache.nifi.minifi.bootstrap.configuration.FileChangeNotifier
+#nifi.minifi.notifier.components=org.apache.nifi.minifi.bootstrap.configuration.RestChangeNotifier
 
 # File change notifier configuration
 
@@ -41,6 +42,11 @@ nifi.minifi.config=./conf/config.yml
 # How frequently the file specified by 'nifi.minifi.notifier.file.config.path' should be evaluated for changes.
 #nifi.minifi.notifier.file.polling.period.seconds=5
 
+# Rest change notifier configuration
+
+# Port on which the Jetty server will bind to, keep commented for a random open port
+#nifi.minifi.notifier.http.port=8338
+
 # Disable JSR 199 so that we can use JSP's without running a JDK
 java.arg.1=-Dorg.apache.jasper.compiler.disablejsr199=true
 

http://git-wip-us.apache.org/repos/asf/nifi-minifi/blob/a1d2fd3f/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index cdd8357..b765c7f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,14 +86,15 @@ limitations under the License.
         <url>https://issues.apache.org/jira/browse/MINIFI</url>
     </issueManagement>
     <properties>
-        <maven.compiler.source>1.7</maven.compiler.source>
-        <maven.compiler.target>1.7</maven.compiler.target>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
         <maven.min-version>3.1.0</maven.min-version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <inceptionYear>2016</inceptionYear>
         <org.slf4j.version>1.7.12</org.slf4j.version>
         <org.apache.nifi.version>0.6.0</org.apache.nifi.version>
+        <jetty.version>9.3.8.v20160314</jetty.version>
     </properties>
 
     <dependencies>
@@ -177,6 +178,16 @@ limitations under the License.
                 <version>${org.slf4j.version}</version>
                 <scope>provided</scope>
             </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-server</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-servlet</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
 
             <!-- NiFi Modules -->
             <dependency>
@@ -368,7 +379,18 @@ limitations under the License.
                 <artifactId>slf4j-simple</artifactId>
                 <version>${org.slf4j.version}</version>
             </dependency>
-
+            <dependency>
+                <groupId>com.squareup.okhttp</groupId>
+                <artifactId>okhttp</artifactId>
+                <version>2.7.5</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>commons-io</groupId>
+                <artifactId>commons-io</artifactId>
+                <version>2.4</version>
+                <scope>test</scope>
+            </dependency>
         </dependencies>
     </dependencyManagement>