nifi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mcgil...@apache.org
Subject [4/4] incubator-nifi git commit: Squashed commit of the following:
Date Mon, 23 Mar 2015 03:14:50 GMT
Squashed commit of the following:

commit 7e96bb903c4051613b5192e81aeaeef7997a9c1d
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 22 23:09:18 2015 -0400

    Conflicts:
    	nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EvaluateRegularExpression.java
    	nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ExtractText.java

commit 7fc79a34b7bee4b92988a36c64f8585b7fec8d33
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 22 22:40:51 2015 -0400

    NIFI-353:
    - Only showing up to 1.5kb of the content in the hex view.

commit 36f11c3d9d8466fa3f207b5fc859375b33370b53
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 22 16:27:19 2015 -0400

    NIFI-353:
    - Disabling the content viewer by default.

commit f78f74dc8dcc21e8f01ae65cb17a6db80c2f3a6c
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 22 16:24:45 2015 -0400

    NIFI-353:
    - Adding error handling around the call into the content viewer extension.
    - Using forward instead of include when showing errors.

commit b43fe6f935ce5697f551e5a3b30f7703b49f64a7
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 22 00:30:34 2015 -0400

    NIFI-353:
    - Clean up.
    - Fixing authority check for DFM with check for Provenance.

commit 61fc0467437a6fb0b9db4f1331699c23155aa3a6
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sat Mar 21 22:19:19 2015 -0400

    NIFI-353:
    - Fixing artifact versions.

commit 2bd1a18f3ca5258f745ddf4681f79ea2741aaa78
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Fri Mar 20 20:59:44 2015 -0400

    NIFI-353:
    - Fixing artifact versions.

commit c2eaa192eae7e977ffd50033a007693f636a6322
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Fri Mar 20 20:15:40 2015 -0400

    NIFI-353:
    - Javadocs.
    - Updating the styles of the content labels.

commit 3410197b7d88444de5c6f74622d67a1b0cc39e6a
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Fri Mar 20 19:40:02 2015 -0400

    Squashed commit of the following:

    commit e88ed13d8d7221f5a91588f553d039d3917494be
    Merge: 93b361e 3f36236
    Author: Mark Payne <markap14@hotmail.com>
    Date:   Fri Mar 20 09:11:39 2015 -0400

        Merge branch 'inputstream-callback-protection' of https://github.com/rowolabi/incubator-nifi into develop

    commit 93b361e69bd046d6b2e0f561d8b2231a4505b6a9
    Merge: c9eb237 a6740a6
    Author: Mark Payne <markap14@hotmail.com>
    Date:   Thu Mar 19 11:49:11 2015 -0400

        Merge branch 'develop' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into develop

    commit c9eb237895a94a27fc6f760b82b16ac3e2cdab95
    Author: Mark Payne <markap14@hotmail.com>
    Date:   Thu Mar 19 11:40:01 2015 -0400

        NIFI-443: Removed stopFunnel from ProcessGroup because we don't want to allow it

    commit 8b911c5aab2a4b8283510a3423e3c8962a533b96
    Author: Mark Payne <markap14@hotmail.com>
    Date:   Thu Mar 19 11:04:46 2015 -0400

        NIFI-443: Always start funnels when added to process group, even when autoResumeState is false

    commit a6740a6e2c87f4c994d305db55c0777dc4f99976
    Author: joewitt <joewitt@apache.org>
    Date:   Thu Mar 19 01:21:32 2015 -0400

        NIFI-399 addressed items in the ticket

    commit ad18853b589d80331e2f4574bce35d79bce09c28
    Author: joewitt <joewitt@apache.org>
    Date:   Wed Mar 18 10:59:13 2015 -0400

        NIFI-399 initial port

    commit 3f3623647367421cd8eb318668144aca1afb9bf4
    Author: Bobby Owolabi <bobbyowolabi@gmail.com>
    Date:   Thu Mar 19 01:43:17 2015 -0400

        NIFI-396 reverting accidentially modified whitespace in TestStandardProcessSession

    commit cd183be4410bdc88de7d3a0026452ab62eb10621
    Author: Bobby Owolabi <bobbyowolabi@gmail.com>
    Date:   Thu Mar 19 01:18:22 2015 -0400

        NIFI-396 updated the javadocs of ProcessSession to reflect that a FlowFileAccessExcpetion will be thrown if an Input/Output Stream is attempted to be accessed after the callback is executed.

    commit e2760f8c980583d285137134e05c435c930fb4d2
    Author: Bobby Owolabi <bobbyowolabi@gmail.com>
    Date:   Thu Mar 19 00:54:24 2015 -0400

        NIFI-396 added a DisableOnCloseInputStream class; modified StandardProcessSession to prevent access of the Input/OutputStreams after callbacks have been executed; updated tests

    commit 7272d0df58c23d099809bf96993b55d73c617476
    Author: Bobby Owolabi <bobbyowolabi@gmail.com>
    Date:   Wed Mar 18 23:30:57 2015 -0400

        NIFI-396 created tests to demonstrate the situations where the ProcessSession throws an Exception and where it doesn't after it returns from the callback

    commit eb5ec703ba0d5c188822a37f6d7eed14af56a594
    Author: Oscar de la Pena <odelapena@exist.com>
    Date:   Thu Mar 19 10:10:09 2015 +0800

        Fixes incorrect messages count in Provenance reporter. Adds Unit test to verify fix

commit b1873d86649d22fcf39956c93371be124a2e161b
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Wed Mar 18 23:08:57 2015 -0400

    NIFI-353:
    - Adjusting the layout of the file name and content type.

commit 0ebb54a501825cb68134f009c4810fb79e49c39f
Merge: ecbccae dea9e22
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Wed Mar 18 21:23:37 2015 -0400

    Merge branch 'develop' into NIFI-353

commit ecbccae7343561e25acc9383b64b8c155f2c5700
Merge: 4c44843 1cca300
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Tue Mar 17 23:20:39 2015 -0400

    Merge branch 'develop' into NIFI-353

commit 4c448436c5f84ac5bd4b3afad1b75f40c45c6d54
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Tue Mar 17 23:20:24 2015 -0400

    NIFI-353:
    - Modifying the hexview plugin to address html escape issues and no longer rendering the base64 padding characters.

commit 1a05c9db63cbbe21e08b40a87da14526653c41e1
Author: joewitt <joewitt@apache.org>
Date:   Mon Mar 16 16:58:52 2015 -0400

    NIFI-353 merged to latest dev post 002 release and fixed pom references.  Viewer looks great

commit 2b07b0bc1da9f4da6f40c31258c66afed30ffa4e
Merge: f920902 eb757a4
Author: joewitt <joewitt@apache.org>
Date:   Mon Mar 16 16:38:08 2015 -0400

    Merge branch 'develop' into NIFI-353

commit f92090233fb3ad804cb1881d183592dfd30ffc99
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 15 23:51:31 2015 -0400

    NIFI-353:
    - Addressing issues when running clustered.
    - Javadocs.

commit 73a54eeb859fe9c8822141b59ee79eba8d1e6dff
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 15 09:16:24 2015 -0400

    NIFI-353:
    - Adding support for text/plain.

commit c117a5c6f16c173ba971097fb9a14e9ed495f25b
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 15 08:41:34 2015 -0400

    NIFI-353:
    - Fixing dependency issue.
    - Setting the default content viewer path.
    - Restoring correct content type in the standard viewer META-INF.

commit 12c867daea51d45b80767f82b1f8cf0ec249bb55
Merge: e7d77fe cc890e2
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sat Mar 14 10:11:30 2015 -0400

    Merge branch 'develop' into NIFI-353

commit e7d77fedbdac106803c553626050e380c8b51287
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Thu Mar 12 23:01:36 2015 -0400

    NIFI-353
    - Javadocs.
    - Cleaning up dependencies.

commit a81e1ecbf85b34d5af2054b43118830f0d4fbfa3
Merge: 173177c 7198912
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Thu Mar 12 21:40:45 2015 -0400

    Merge branch 'develop' into NIFI-353

commit 173177c918e6c07c1c98d807ecc7f8be6d0fa637
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Wed Mar 11 23:21:43 2015 -0400

    NIFI-353:
    - Cleaning up error handling.
    - Showing the file name and content type.

commit 098f9709dc0cac7a475d21c08d72de839260952c
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Tue Mar 10 23:21:01 2015 -0400

    NIFI-353:
    - Better error handling when unable to interpret the request, unable to find the content, and no viewer is registered for the detected content type.

commit ee28e9de729dad477cc97b46d9a5e9cd9ef84609
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 8 22:57:15 2015 -0400

    NIFI-353:
    - Replacing dependency on tika parsers with icu4j.

commit a50a6b6e8bf393bb4d47672f69c13e1fa3bc202c
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 8 22:21:49 2015 -0400

    NIFI-353:
    - Allowing the user to toggle between viewing the original content, the formatted content, and the raw bytes in a hex dump.

commit d100a2839bb2aef5af0de00f5e78ecc9c7f1ad0a
Merge: 347e4e0 342ca17
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Mar 8 13:54:24 2015 -0400

    Merge branch 'develop' into NIFI-353

commit 347e4e024d4e990e6eb17a2101c31db79200f7a7
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Thu Mar 5 23:20:39 2015 -0500

    NIFI-353:
    - Updating markup generation flow.
    - Adding a combo box to view the content in original, formatted, or hex form (still not functional).

commit 37b5ca48f43cd3bb80b080e14c7cc2478da859aa
Merge: 4819228 5e0026c
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Wed Mar 4 21:44:10 2015 -0500

    Merge branch 'develop' into NIFI-353

commit 48192289e492ed98bcc433925a70203570223c2a
Merge: cc0b6fe 50744bf
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Wed Feb 25 22:53:29 2015 -0500

    Merge branch 'develop' into NIFI-353

commit cc0b6fe2f2b852457824357ff493b5c4e9d44ccd
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Wed Feb 25 22:50:23 2015 -0500

    NIFI-353:
    - Starting to moving the hex viewer into the main content viewer web application that comes bundled in framework. Previously it was only in the standard content viewer extension but we want to be able to render the content in hex for all types of data.

commit 60c411de0dab6e0dc099e9b1fb04adfb1c2507f1
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Tue Feb 24 23:15:21 2015 -0500

    NIFI-353:
    - Starting to add support for the hex viewer.

commit efe8e06827488b1156edd5ea65e712d2eb675ef2
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Feb 22 21:48:09 2015 -0500

    NIFI-353:
    - Adding support for viewing xml documents.
    - Adding supporting to fold the json and xml documents.

commit 1955926a857daede8091a761e984d22273ada235
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sat Feb 21 21:49:48 2015 -0500

    NIFI-353:
    - Allowing the content to be obtained by either stream or as a string (with the char encoding detected using tika).
    - Set the json viewer size during window resize events.

commit 43f6e3c0585436176527344febc860586ded3b60
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sat Feb 21 16:36:43 2015 -0500

    NIFI-353:
    - Starting to add support for viewing JSON.
    - Updating codemirror to add support for JSON and XML.

commit 8f54adf1c1e6cc581ee501d6f1182dac2ff63512
Merge: 605a05b 57b5d58
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sat Feb 21 08:02:47 2015 -0500

    Merge branch 'develop' into NIFI-353

commit 605a05b89f09bd16311d6321d6a1eeee881880b5
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Thu Feb 19 23:15:54 2015 -0500

    NIFI-353:
    - Adding mime type detection using tika.

commit bd9ef8431bc66f567d8e526e06f0635de2417254
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Wed Feb 18 23:35:28 2015 -0500

    NIFI-353:
    - Continuing to wire up the content viewer controller to NiFi (standalone/clustered) and the content type specific renderer.

commit 2334e4888e48764b93671b8ac2a00d26a07dc3bc
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Tue Feb 17 23:00:07 2015 -0500

    NIFI-353:
    - Updating the mark up on the content page.

commit 78bab591d1c717077c7ee975c5ab4cf17c02d139
Merge: b1b2eaf 0047fa4
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Tue Feb 17 22:48:33 2015 -0500

    Merge branch 'develop' into NIFI-353

commit b1b2eafe807c8f2e6cccb1a51736db8badd5d03b
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Tue Feb 17 22:47:26 2015 -0500

    NIFI-353:
    - Creating an interface for retrieving content.
    - Creating an interface for reading content bytes.
    - Integrating these concepts into the exiting content viewer controller.

commit cd0a1bd42ac4b953cb1d8c979d7041373555882a
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Mon Feb 16 17:08:32 2015 -0500

    NIFI-353:
    - Renaming data-viewer to content-viewer.

commit 48b1572f177e2292ddbacc92a3a5838cc45c7a42
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Mon Feb 16 15:45:15 2015 -0500

    NIFI-353:
    - Renaming data-viewer to content-viewer.

commit b50953d9d22881bcdc34edf1a34557d975b6dcc7
Author: Matt Gilman <matt.c.gilman@gmail.com>
Date:   Sun Feb 15 18:20:17 2015 -0500

    NIFI-353:
    - Starting to integrate the data viewer controller into the Jetty Server.
    - Starting to set up the data viewer controller.
    - Starting to set up the standard data viewer.


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

Branch: refs/heads/develop
Commit: e05c9fd20e41cbb25f2e86b8a5b5791e6de65bcb
Parents: 0bd2784
Author: Matt Gilman <matt.c.gilman@gmail.com>
Authored: Sun Mar 22 23:13:07 2015 -0400
Committer: Matt Gilman <matt.c.gilman@gmail.com>
Committed: Sun Mar 22 23:13:07 2015 -0400

----------------------------------------------------------------------
 .../org/apache/nifi/web/ViewableContent.java    |  74 +++++
 nifi/nifi-assembly/pom.xml                      |   1 -
 .../nifi-framework/nifi-web/nifi-jetty/pom.xml  |  10 +
 .../org/apache/nifi/web/server/JettyServer.java | 108 +++++--
 .../nifi-web/nifi-web-api/pom.xml               |   5 +
 .../org/apache/nifi/web/NiFiServiceFacade.java  |   1 -
 .../nifi/web/StandardNiFiContentAccess.java     | 141 +++++++++
 .../nifi/web/StandardNiFiServiceFacade.java     |   3 -
 .../apache/nifi/web/StandardNiFiWebContext.java |   2 +-
 .../apache/nifi/web/api/ProvenanceResource.java |   2 +-
 .../nifi/web/controller/ControllerFacade.java   |   2 +-
 .../nifi/web/util/DownloadableContent.java      |  47 ---
 .../src/main/resources/nifi-web-api-context.xml |   7 +
 .../nifi-web/nifi-web-content-access/pom.xml    |  25 ++
 .../java/org/apache/nifi/web/ContentAccess.java |  33 +++
 .../apache/nifi/web/ContentRequestContext.java  |  51 ++++
 .../apache/nifi/web/DownloadableContent.java    |  62 ++++
 .../nifi-web/nifi-web-content-viewer/.gitignore |   1 +
 .../nifi-web/nifi-web-content-viewer/pom.xml    |  91 ++++++
 .../nifi/web/ContentViewerController.java       | 284 +++++++++++++++++++
 .../src/main/resources/META-INF/NOTICE          |  19 ++
 .../src/main/webapp/WEB-INF/jsp/footer.jsp      |  20 ++
 .../src/main/webapp/WEB-INF/jsp/header.jsp      |  92 ++++++
 .../src/main/webapp/WEB-INF/jsp/hexview.jsp     |  32 +++
 .../src/main/webapp/WEB-INF/jsp/no-viewer.jsp   |  20 ++
 .../src/main/webapp/WEB-INF/web.xml             |  26 ++
 .../src/main/webapp/css/main.css                | 113 ++++++++
 .../src/main/webapp/js/hexview/LICENSE          |  32 +++
 .../main/webapp/js/hexview/hexview.default.css  |  10 +
 .../src/main/webapp/js/hexview/hexview.js       | 199 +++++++++++++
 .../nifi-web-ui/src/main/webapp/WEB-INF/web.xml |  11 +
 .../js/codemirror/addon/fold/foldgutter.css     |  20 ++
 .../js/codemirror/lib/codemirror-compressed.js  |  14 +-
 .../webapp/js/jquery/combo/jquery.combo.css     |   8 +
 .../nifi-framework/nifi-web/pom.xml             |   8 +
 .../nifi-framework-bundle/pom.xml               |   5 +
 .../nifi-standard-content-viewer/pom.xml        |  71 +++++
 .../web/StandardContentViewerController.java    | 103 +++++++
 .../src/main/resources/META-INF/NOTICE          |  19 ++
 .../main/webapp/META-INF/nifi-content-viewer    |   3 +
 .../src/main/webapp/WEB-INF/jsp/codemirror.jsp  |  47 +++
 .../src/main/webapp/WEB-INF/web.xml             |  29 ++
 .../src/main/webapp/css/main.css                |  20 ++
 .../nifi-standard-nar/pom.xml                   |   5 +
 .../nifi-standard-bundle/pom.xml                |  10 +-
 45 files changed, 1800 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-api/src/main/java/org/apache/nifi/web/ViewableContent.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ViewableContent.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ViewableContent.java
new file mode 100644
index 0000000..f90221e
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ViewableContent.java
@@ -0,0 +1,74 @@
+/*
+ * 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.web;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Interface for obtaining content from the NiFi content repository.
+ */
+public interface ViewableContent {
+
+    public static final String CONTENT_REQUEST_ATTRIBUTE = "org.apache.nifi.web.content";
+    
+    public enum DisplayMode {
+        Original,
+        Formatted,
+        Hex;
+    }
+    
+    /**
+     * The stream to the viewable content. The data stream can only be read once so
+     * an extension can call this method or getContent.
+     * 
+     * @return 
+     */
+    InputStream getContentStream();
+
+    /**
+     * Gets the content as a string. The data stream can only be read once so
+     * an extension can call this method or getContentStream.
+     * 
+     * @return 
+     * @throws java.io.IOException 
+     */
+    String getContent() throws IOException;
+    
+    /**
+     * Returns the desired play mode. If the mode is Hex the
+     * framework will handle generating the mark up. The only
+     * values that an extension will see is Original or Formatted.
+     * 
+     * @return 
+     */
+    DisplayMode getDisplayMode();
+    
+    /**
+     * The contents file name.
+     *  
+     * @return 
+     */
+    String getFileName();
+    
+    /**
+     * The mime type of the content.
+     * 
+     * @return 
+     */
+    String getContentType();
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-assembly/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-assembly/pom.xml b/nifi/nifi-assembly/pom.xml
index ece7dbb..cae0f00 100644
--- a/nifi/nifi-assembly/pom.xml
+++ b/nifi/nifi-assembly/pom.xml
@@ -215,7 +215,6 @@
         <nifi.content.repository.always.sync>false</nifi.content.repository.always.sync>
         <nifi.content.viewer.url />
         
-        
         <nifi.restore.directory />
         <nifi.ui.banner.text />
         <nifi.ui.autorefresh.interval>30 sec</nifi.ui.autorefresh.interval>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
index 09a614d..9f1cf99 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
@@ -115,6 +115,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-web-content-access</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-custom-ui-utilities</artifactId>
             <scope>compile</scope>
         </dependency>
@@ -152,6 +157,11 @@
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-web-docs</artifactId>
             <type>war</type>
+        </dependency> 
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-web-content-viewer</artifactId>
+            <type>war</type>
         </dependency>   
         <dependency>
             <groupId>org.apache.nifi</groupId>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index 95052a7..54111a1 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -52,6 +52,7 @@ import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.NiFiWebContext;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.web.ContentAccess;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
@@ -99,7 +100,9 @@ public class JettyServer implements NiFiServer {
     private ExtensionMapping extensionMapping;
     private WebAppContext webApiContext;
     private WebAppContext webDocsContext;
+    private WebAppContext webContentViewerContext;
     private Collection<WebAppContext> customUiWebContexts;
+    private Collection<WebAppContext> contentViewerWebContexts;
     private final NiFiProperties props;
 
     /**
@@ -164,6 +167,7 @@ public class JettyServer implements NiFiServer {
         File webApiWar = null;
         File webErrorWar = null;
         File webDocsWar = null;
+        File webContentViewerWar = null;
         List<File> otherWars = new ArrayList<>();
         for (File war : warToNarWorkingDirectoryLookup.keySet()) {
             if (war.getName().toLowerCase().startsWith("nifi-web-api")) {
@@ -172,6 +176,8 @@ public class JettyServer implements NiFiServer {
                 webErrorWar = war;
             } else if (war.getName().toLowerCase().startsWith("nifi-web-docs")) {
                 webDocsWar = war;
+            } else if (war.getName().toLowerCase().startsWith("nifi-web-content-viewer")) {
+                webContentViewerWar = war;
             } else if (war.getName().toLowerCase().startsWith("nifi-web")) {
                 webUiWar = war;
             } else {
@@ -188,23 +194,29 @@ public class JettyServer implements NiFiServer {
             throw new RuntimeException("Unable to load nifi-web-docs WAR");
         } else if (webErrorWar == null) {
             throw new RuntimeException("Unable to load nifi-web-error WAR");
+        } else if (webContentViewerWar == null) {
+            throw new RuntimeException("Unable to load nifi-web-content-viewer WAR");
         }
 
         // handlers for each war and init params for the web api
         final HandlerCollection handlers = new HandlerCollection();
-        final Map<String, String> initParams = new HashMap<>();
+        final Map<String, String> customUiMappings = new HashMap<>();
+        final Map<String, String> mimeTypeMappings = new HashMap<>();
         final ClassLoader frameworkClassLoader = getClass().getClassLoader();
         final ClassLoader jettyClassLoader = frameworkClassLoader.getParent();
 
         // deploy the other wars
         if (CollectionUtils.isNotEmpty(otherWars)) {
             customUiWebContexts = new ArrayList<>();
+            contentViewerWebContexts = new ArrayList<>();
+            
             for (File war : otherWars) {
                 // see if this war is a custom processor ui
-                List<String> customUiProcessorTypes = getCustomUiProcessorTypes(war);
+                List<String> customUiProcessorTypes = getWarExtensions(war, "META-INF/nifi-processor");
+                List<String> contentViewerMimeTypes = getWarExtensions(war, "META-INF/nifi-content-viewer");
 
-                // only include wars that are for custom processor ui's
-                if (CollectionUtils.isNotEmpty(customUiProcessorTypes)) {
+                // only include wars that are for extensions
+                if (!customUiProcessorTypes.isEmpty() || !contentViewerMimeTypes.isEmpty()) {
                     String warName = StringUtils.substringBeforeLast(war.getName(), ".");
                     String warContextPath = String.format("/%s", warName);
 
@@ -216,19 +228,27 @@ public class JettyServer implements NiFiServer {
                         narClassLoaderForWar = jettyClassLoader;
                     }
 
-                    // create the custom ui web app context
-                    WebAppContext customUiContext = loadWar(war, warContextPath, narClassLoaderForWar);
+                    // create the extension web app context
+                    WebAppContext extensionUiContext = loadWar(war, warContextPath, narClassLoaderForWar);
 
-                    // hold on to a reference to all custom ui web app contexts
-                    customUiWebContexts.add(customUiContext);
+                    // also store it by type so we can populate the appropriate initialization parameters
+                    if (!customUiProcessorTypes.isEmpty()) {
+                        customUiWebContexts.add(extensionUiContext);
+                    } else {
+                        // record the mime type to web app mapping (need to handle type collision)
+                        contentViewerWebContexts.add(extensionUiContext);
+                    }
 
                     // include custom ui web context in the handlers
-                    handlers.addHandler(customUiContext);
+                    handlers.addHandler(extensionUiContext);
 
                     // add the initialization paramters
                     for (String customUiProcessorType : customUiProcessorTypes) {
                         // map the processor type to the custom ui path
-                        initParams.put(customUiProcessorType, warContextPath);
+                        customUiMappings.put(customUiProcessorType, warContextPath);
+                    }
+                    for (final String contentViewerMimeType : contentViewerMimeTypes) {
+                        mimeTypeMappings.put(contentViewerMimeType, warContextPath);
                     }
                 }
             }
@@ -239,10 +259,14 @@ public class JettyServer implements NiFiServer {
 
         // load the web api app
         webApiContext = loadWar(webApiWar, "/nifi-api", frameworkClassLoader);
-        Map<String, String> webApiInitParams = webApiContext.getInitParams();
-        webApiInitParams.putAll(initParams);
+        webApiContext.getInitParams().putAll(customUiMappings);
         handlers.addHandler(webApiContext);
 
+        // load the content viewer app
+        webContentViewerContext = loadWar(webContentViewerWar, "/nifi-content-viewer", frameworkClassLoader);
+        webContentViewerContext.getInitParams().putAll(mimeTypeMappings);
+        handlers.addHandler(webContentViewerContext);
+        
         // create a web app for the docs
         final String docsContextPath = "/nifi-docs";
 
@@ -292,18 +316,18 @@ public class JettyServer implements NiFiServer {
     }
 
     /**
-     * Loads the processor types that the specified war file is a custom UI for.
+     * Returns the extension in the specified WAR using the specified path.
      *
-     * @param warFile
+     * @param war
      * @return
      */
-    private List<String> getCustomUiProcessorTypes(final File warFile) {
+    private List<String> getWarExtensions(final File war, final String path) {
         List<String> processorTypes = new ArrayList<>();
         JarFile jarFile = null;
         try {
             // load the jar file and attempt to find the nifi-processor entry
-            jarFile = new JarFile(warFile);
-            JarEntry jarEntry = jarFile.getJarEntry("META-INF/nifi-processor");
+            jarFile = new JarFile(war);
+            JarEntry jarEntry = jarFile.getJarEntry(path);
 
             // ensure the nifi-processor entry was found
             if (jarEntry != null) {
@@ -320,7 +344,7 @@ public class JettyServer implements NiFiServer {
                 }
             }
         } catch (IOException ioe) {
-            logger.warn(String.format("Unable to inspect %s for a custom processor UI.", warFile));
+            logger.warn(String.format("Unable to inspect %s for a custom processor UI.", war));
         } finally {
             try {
                 // close the jar file - which closes all input streams obtained via getInputStream above
@@ -537,20 +561,48 @@ public class JettyServer implements NiFiServer {
 
             // ensure the appropriate wars deployed successfully before injecting the NiFi context and security filters - 
             // this must be done after starting the server (and ensuring there were no start up failures)
-            if (webApiContext != null && CollectionUtils.isNotEmpty(customUiWebContexts)) {
+            if (webApiContext != null) {
                 final ServletContext webApiServletContext = webApiContext.getServletHandler().getServletContext();
                 final WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(webApiServletContext);
-                final NiFiWebContext NiFiWebContext = webApplicationContext.getBean("nifiWebContext", NiFiWebContext.class);
 
-                for (final WebAppContext customUiContext : customUiWebContexts) {
-                    // set the NiFi context in each custom ui servlet context
-                    final ServletContext customUiServletContext = customUiContext.getServletHandler().getServletContext();
-                    customUiServletContext.setAttribute("nifi-web-context", NiFiWebContext);
-
-                    // add the security filter to any custom ui wars
+                if (CollectionUtils.isNotEmpty(customUiWebContexts)) {
+                    final NiFiWebContext niFiWebContext = webApplicationContext.getBean("nifiWebContext", NiFiWebContext.class);
+                    
+                    for (final WebAppContext customUiContext : customUiWebContexts) {
+                        // set the NiFi context in each custom ui servlet context
+                        final ServletContext customUiServletContext = customUiContext.getServletHandler().getServletContext();
+                        customUiServletContext.setAttribute("nifi-web-context", niFiWebContext);
+
+                        // add the security filter to any custom ui wars
+                        final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
+                        if (securityFilter != null) {
+                            customUiContext.addFilter(securityFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
+                        }
+                    }
+                }
+                
+                if (CollectionUtils.isNotEmpty(contentViewerWebContexts)) {
+                    for (final WebAppContext contentViewerContext : contentViewerWebContexts) {
+                        // add the security filter to any content viewer  wars
+                        final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
+                        if (securityFilter != null) {
+                            contentViewerContext.addFilter(securityFilter, "/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
+                        }
+                    }
+                }
+                
+                // ensure the web content viewer war was loaded
+                if (webContentViewerContext != null) {
+                    final ContentAccess contentAccess = webApplicationContext.getBean("contentAccess", ContentAccess.class);
+                    
+                    // add the content access
+                    final ServletContext webContentViewerServletContext = webContentViewerContext.getServletHandler().getServletContext();
+                    webContentViewerServletContext.setAttribute("nifi-content-access", contentAccess);
+                    
+                    // add the security filter to the content viewer controller
                     final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
                     if (securityFilter != null) {
-                        customUiContext.addFilter(securityFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
+                        webContentViewerContext.addFilter(securityFilter, "/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
                     }
                 }
             }
@@ -560,7 +612,7 @@ public class JettyServer implements NiFiServer {
                 final ServletContext webDocsServletContext = webDocsContext.getServletHandler().getServletContext();
                 webDocsServletContext.setAttribute("nifi-extension-mapping", extensionMapping);
             }
-
+            
             // if this nifi is a node in a cluster, start the flow service and load the flow - the 
             // flow service is loaded here for clustered nodes because the loading of the flow will 
             // initialize the connection between the node and the NCM. if the node connects (starts 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
index 0bd34cf..6a51838 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
@@ -104,6 +104,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-web-content-access</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-client-dto</artifactId>
             <scope>provided</scope>
         </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index ae6bf28..63d302c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -65,7 +65,6 @@ import org.apache.nifi.web.api.dto.status.ControllerStatusDTO;
 import org.apache.nifi.web.api.dto.status.NodeStatusDTO;
 import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO;
 import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
-import org.apache.nifi.web.util.DownloadableContent;
 
 /**
  * Defines the NiFiServiceFacade interface.

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
new file mode 100644
index 0000000..70aad94
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
@@ -0,0 +1,141 @@
+/*
+ * 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.web;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MultivaluedMap;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.manager.exception.UnknownNodeException;
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.cluster.node.Node;
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.controller.repository.claim.ContentDirection;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.user.NiFiUserDetails;
+import org.apache.nifi.web.util.WebUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ *
+ */
+public class StandardNiFiContentAccess implements ContentAccess {
+
+    private static final Logger logger = LoggerFactory.getLogger(StandardNiFiContentAccess.class);
+    public static final String CLIENT_ID_PARAM = "clientId";
+    
+    private NiFiProperties properties;
+    private NiFiServiceFacade serviceFacade;
+    private WebClusterManager clusterManager;
+    
+    @Override
+    @PreAuthorize("hasRole('ROLE_PROVENANCE')")
+    public DownloadableContent getContent(final ContentRequestContext request) {
+        // if clustered, send request to cluster manager
+        if (properties.isClusterManager()) {
+            // get the URI
+            URI dataUri;
+            try {
+                dataUri = new URI(request.getDataUri());
+            } catch (final URISyntaxException use) {
+                throw new ClusterRequestException(use);
+            }
+            
+            // set the request parameters
+            final MultivaluedMap<String, String> parameters = new MultivaluedMapImpl();
+            parameters.add(CLIENT_ID_PARAM, request.getClientId());
+            
+            // set the headers
+            final Map<String, String> headers = new HashMap<>();
+            if (StringUtils.isNotBlank(request.getProxiedEntitiesChain())) {
+                headers.put("X-ProxiedEntitiesChain", request.getProxiedEntitiesChain());
+            }
+
+            // add the user's authorities (if any) to the headers
+            final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+            if (authentication != null) {
+                final Object userDetailsObj = authentication.getPrincipal();
+                if (userDetailsObj instanceof NiFiUserDetails) {
+                    // serialize user details object
+                    final String hexEncodedUserDetails = WebUtils.serializeObjectToHex((Serializable) userDetailsObj);
+
+                    // put serialized user details in header
+                    headers.put("X-ProxiedEntityUserDetails", hexEncodedUserDetails);
+                }
+            }
+            
+            // get the target node and ensure it exists
+            final Node targetNode = clusterManager.getNode(request.getClusterNodeId());
+            if (targetNode == null) {
+                throw new UnknownNodeException("The specified cluster node does not exist.");
+            }
+
+            final Set<NodeIdentifier> targetNodes = new HashSet<>();
+            targetNodes.add(targetNode.getNodeId());
+
+            // replicate the request to the specific node
+            final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, dataUri, parameters, headers, targetNodes);
+            final ClientResponse clientResponse = nodeResponse.getClientResponse();
+            final MultivaluedMap<String, String> responseHeaders = clientResponse.getHeaders();
+            
+            // get the file name
+            final String contentDisposition = responseHeaders.getFirst("Content-Disposition");
+            final String filename = StringUtils.substringAfterLast(contentDisposition, "filename=");
+            
+            // get the content type
+            final String contentType = responseHeaders.getFirst("Content-Type");
+            
+            // create the downloadable content
+            return new DownloadableContent(filename, contentType, clientResponse.getEntityInputStream());
+        } else {
+            // example URI: http://localhost:8080/nifi-api/controller/provenance/events/1/content/input
+            final String eventDetails = StringUtils.substringAfterLast(request.getDataUri(), "events/");
+            final String rawEventId = StringUtils.substringBefore(eventDetails, "/content/");
+            final String rawDirection = StringUtils.substringAfterLast(eventDetails, "/content/");
+            
+            // get the content type
+            final Long eventId = Long.parseLong(rawEventId);
+            final ContentDirection direction = ContentDirection.valueOf(rawDirection.toUpperCase());
+            return serviceFacade.getContent(eventId, request.getDataUri(), direction);
+        }
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    public void setServiceFacade(NiFiServiceFacade serviceFacade) {
+        this.serviceFacade = serviceFacade;
+    }
+
+    public void setClusterManager(WebClusterManager clusterManager) {
+        this.clusterManager = clusterManager;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index c8683b0..14640d8 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -16,8 +16,6 @@
  */
 package org.apache.nifi.web;
 
-import org.apache.nifi.web.OptimisticLockingManager;
-import org.apache.nifi.web.ConfigurationSnapshot;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -150,7 +148,6 @@ import org.apache.nifi.web.dao.ProcessorDAO;
 import org.apache.nifi.web.dao.RemoteProcessGroupDAO;
 import org.apache.nifi.web.dao.SnippetDAO;
 import org.apache.nifi.web.dao.TemplateDAO;
-import org.apache.nifi.web.util.DownloadableContent;
 import org.apache.nifi.web.util.SnippetUtils;
 
 import org.apache.commons.collections4.CollectionUtils;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
index c1b1993..d9fa9e3 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
@@ -89,7 +89,7 @@ public class StandardNiFiWebContext implements NiFiWebContext {
     }
 
     @Override
-    @PreAuthorize("hasAnyRole('ROLE_DFM')")
+    @PreAuthorize("hasRole('ROLE_DFM')")
     public void saveActions(final Collection<ProcessorConfigurationAction> processorActions) {
         Objects.requireNonNull(processorActions, "Actions cannot be null.");
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java
index f0b38a1..5fef27f 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java
@@ -74,7 +74,7 @@ import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.nifi.web.api.request.DateTimeParameter;
 import org.apache.nifi.web.api.request.IntegerParameter;
 import org.apache.nifi.web.api.request.LongParameter;
-import org.apache.nifi.web.util.DownloadableContent;
+import org.apache.nifi.web.DownloadableContent;
 
 import org.apache.commons.lang3.StringUtils;
 import org.codehaus.enunciate.jaxrs.TypeHint;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index b009581..117555a 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -108,7 +108,7 @@ import org.apache.nifi.web.api.dto.search.SearchResultsDTO;
 import org.apache.nifi.web.api.dto.status.ControllerStatusDTO;
 import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO;
 import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
-import org.apache.nifi.web.util.DownloadableContent;
+import org.apache.nifi.web.DownloadableContent;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.UserService;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/DownloadableContent.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/DownloadableContent.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/DownloadableContent.java
deleted file mode 100644
index 9bb54f1..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/DownloadableContent.java
+++ /dev/null
@@ -1,47 +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.web.util;
-
-import java.io.InputStream;
-
-/**
- *
- */
-public final class DownloadableContent {
-
-    private final String filename;
-    private final String type;
-    private final InputStream content;
-
-    public DownloadableContent(String filename, String type, InputStream content) {
-        this.filename = filename;
-        this.type = type;
-        this.content = content;
-    }
-
-    public String getFilename() {
-        return filename;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public InputStream getContent() {
-        return content;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index 39677ca..a822442 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -30,6 +30,13 @@
             <bean class="org.apache.nifi.web.StandardOptimisticLockingManager" />
         </constructor-arg>
     </bean>
+    
+    <!-- content access -->
+    <bean id="contentAccess" class="org.apache.nifi.web.StandardNiFiContentAccess">
+        <property name="serviceFacade" ref="serviceFacade"/>
+        <property name="properties" ref="nifiProperties"/>
+        <property name="clusterManager" ref="clusterManager"/>
+    </bean>
 
     <!-- dto factory -->
     <bean id="dtoFactory" class="org.apache.nifi.web.api.dto.DtoFactory">

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/pom.xml
new file mode 100644
index 0000000..c5c12d3
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/pom.xml
@@ -0,0 +1,25 @@
+<?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>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-web</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+    <groupId>org.apache.nifi</groupId>
+    <artifactId>nifi-web-content-access</artifactId>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/ContentAccess.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/ContentAccess.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/ContentAccess.java
new file mode 100644
index 0000000..a093c59
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/ContentAccess.java
@@ -0,0 +1,33 @@
+/*
+ * 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.web;
+
+/**
+ * Provides access to content within NiFi.
+ * 
+ * @author unattributed
+ */
+public interface ContentAccess {
+
+    /**
+     * Gets the content for the specified claim.
+     *
+     * @param request
+     * @return
+     */
+    DownloadableContent getContent(ContentRequestContext request);
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/ContentRequestContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/ContentRequestContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/ContentRequestContext.java
new file mode 100644
index 0000000..f5744ee
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/ContentRequestContext.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.web;
+
+/**
+ * A request for content.
+ */
+public interface ContentRequestContext {
+    
+    /**
+     * The URI to the data.
+     * 
+     * @return 
+     */
+    String getDataUri();
+    
+    /**
+     * If clustered, this is the id of the node the data resides on.
+     * 
+     * @return 
+     */
+    String getClusterNodeId();
+    
+    /**
+     * The client id for the user making the request.
+     * 
+     * @return 
+     */
+    String getClientId();
+    
+    /**
+     * The proxy chain for the current request, if applicable.
+     * 
+     * @return 
+     */
+    String getProxiedEntitiesChain();
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/DownloadableContent.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/DownloadableContent.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/DownloadableContent.java
new file mode 100644
index 0000000..a23673f
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-access/src/main/java/org/apache/nifi/web/DownloadableContent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.web;
+
+import java.io.InputStream;
+
+/**
+ * Represents content that can be downloaded.
+ */
+public final class DownloadableContent {
+
+    private final String filename;
+    private final String type;
+    private final InputStream content;
+
+    public DownloadableContent(String filename, String type, InputStream content) {
+        this.filename = filename;
+        this.type = type;
+        this.content = content;
+    }
+
+    /**
+     * The filename of the content.
+     * 
+     * @return 
+     */
+    public String getFilename() {
+        return filename;
+    }
+
+    /**
+     * The content type of the content.
+     * 
+     * @return 
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * The content stream.
+     * 
+     * @return 
+     */
+    public InputStream getContent() {
+        return content;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/.gitignore
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/.gitignore b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/.gitignore
new file mode 100755
index 0000000..ea8c4bf
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/.gitignore
@@ -0,0 +1 @@
+/target

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/pom.xml
new file mode 100644
index 0000000..75464c2
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/pom.xml
@@ -0,0 +1,91 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <!--
+      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.
+    -->
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-web</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+    <groupId>org.apache.nifi</groupId>
+    <artifactId>nifi-web-content-viewer</artifactId>
+    <packaging>war</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-web-content-access</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ibm.icu</groupId>
+            <artifactId>icu4j</artifactId>
+            <version>54.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tika</groupId>
+            <artifactId>tika-core</artifactId>
+            <version>1.7</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet.jsp</groupId>
+            <artifactId>javax.servlet.jsp-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.el</groupId>
+            <artifactId>javax.el-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet.jsp.jstl</groupId>
+            <artifactId>javax.servlet.jsp.jstl-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
new file mode 100644
index 0000000..d9b082d
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
@@ -0,0 +1,284 @@
+/*
+ * 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.web;
+
+import com.ibm.icu.text.CharsetDetector;
+import com.ibm.icu.text.CharsetMatch;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.stream.io.StreamUtils;
+import org.apache.nifi.web.ViewableContent.DisplayMode;
+import org.apache.tika.detect.DefaultDetector;
+import org.apache.tika.io.TikaInputStream;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.mime.MediaType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.AccessDeniedException;
+
+/**
+ * Controller servlet for viewing content. This is responsible for generating
+ * the markup for the header and footer of the page. Included in that is the 
+ * combo that allows the user to choose how they wait to view the data
+ * (original, formatted, hex). If a data viewer is registered for the detected
+ * content type, it will include the markup it generates in the response.
+ */
+public class ContentViewerController extends HttpServlet {
+
+    private static final Logger logger = LoggerFactory.getLogger(ContentViewerController.class);
+    
+    // 1.5kb - multiple of 12 (3 bytes = 4 base 64 encoded chars)
+    private final static int BUFFER_LENGTH = 1536;
+    
+    /**
+     * Gets the content and defers to registered viewers to generate the markup.
+     * 
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
+        // get the content
+        final ServletContext servletContext = request.getServletContext();
+        final ContentAccess contentAccess = (ContentAccess) servletContext.getAttribute("nifi-content-access");
+        
+        // get the content
+        final DownloadableContent downloadableContent;
+        try {
+            downloadableContent = contentAccess.getContent(getContentRequest(request));
+        } catch (final ResourceNotFoundException rnfe) {
+            request.setAttribute("title", "Error");
+            request.setAttribute("messages", "Unable to find the specified content");
+            
+            // forward to the error page
+            final ServletContext viewerContext = servletContext.getContext("/nifi");
+            viewerContext.getRequestDispatcher("/message").forward(request, response);
+            return;
+        } catch (final AccessDeniedException ade) {
+            request.setAttribute("title", "Acess Denied");
+            request.setAttribute("messages", "Unable to approve access to the specified content: " + ade.getMessage());
+            
+            // forward to the error page
+            final ServletContext viewerContext = servletContext.getContext("/nifi");
+            viewerContext.getRequestDispatcher("/message").forward(request, response);
+            return;
+        } catch (final Exception e) {
+            request.setAttribute("title", "Error");
+            request.setAttribute("messages", "An unexcepted error has occurred: " + e.getMessage());
+            
+            // forward to the error page
+            final ServletContext viewerContext = servletContext.getContext("/nifi");
+            viewerContext.getRequestDispatcher("/message").forward(request, response);
+            return;
+        }
+
+        // determine how we want to view the data
+        String mode = request.getParameter("mode");
+        
+        // if the name isn't set, use original
+        if (mode == null) {
+            mode = DisplayMode.Original.name();
+        }
+        
+        // determine the display mode
+        final DisplayMode displayMode;
+        try {
+            displayMode = DisplayMode.valueOf(mode);
+        } catch (final IllegalArgumentException iae) {
+            request.setAttribute("title", "Error");
+            request.setAttribute("messages", "Invalid display mode: " + mode);
+            
+            // forward to the error page
+            final ServletContext viewerContext = servletContext.getContext("/nifi");
+            viewerContext.getRequestDispatcher("/message").forward(request, response);
+            return;
+        }
+        
+        // buffer the content to support reseting in case we need to detect the content type or char encoding
+        final BufferedInputStream bis = new BufferedInputStream(downloadableContent.getContent());
+        
+        // detect the content type
+        final DefaultDetector detector = new DefaultDetector();
+
+        // create the stream for tika to process, buffered to support reseting
+        final TikaInputStream tikaStream = TikaInputStream.get(bis);
+
+        // provide a hint based on the filename
+        final Metadata metadata = new Metadata();
+        metadata.set(Metadata.RESOURCE_NAME_KEY, downloadableContent.getFilename());
+
+        // Get mime type
+        final MediaType mediatype = detector.detect(tikaStream, metadata);
+        final String mimeType = mediatype.toString();
+        
+        // add attributes needed for the header
+        final StringBuffer requestUrl = request.getRequestURL();
+        request.setAttribute("requestUrl", requestUrl.toString());
+        request.setAttribute("dataRef", request.getParameter("ref"));
+        request.setAttribute("filename", downloadableContent.getFilename());
+        request.setAttribute("contentType", mimeType);
+        
+        // generate the header
+        request.getRequestDispatcher("/WEB-INF/jsp/header.jsp").include(request, response);
+        
+        // remove the attributes needed for the header
+        request.removeAttribute("requestUrl");
+        request.removeAttribute("dataRef");
+        request.removeAttribute("filename");
+        request.removeAttribute("contentType");
+        
+        // generate the markup for the content based on the display mode
+        if (DisplayMode.Hex.equals(displayMode)) {
+            final byte[] buffer = new byte[BUFFER_LENGTH];
+            final int read = StreamUtils.fillBuffer(bis, buffer, false);
+            
+            // trim the byte array if necessary
+            byte[] bytes = buffer;
+            if (read != buffer.length) {
+                bytes = new byte[read];
+                System.arraycopy(buffer, 0, bytes, 0, read);
+            }
+            
+            // convert bytes into the base 64 bytes
+            final String base64 = Base64.encodeBase64String(bytes);
+            
+            // defer to the jsp
+            request.setAttribute("content", base64);
+            request.getRequestDispatcher("/WEB-INF/jsp/hexview.jsp").include(request, response);
+        } else {
+            // lookup a viewer for the content
+            final String contentViewerUri = servletContext.getInitParameter(mimeType);
+
+            // handle no viewer for content type
+            if (contentViewerUri == null) {
+                request.getRequestDispatcher("/WEB-INF/jsp/no-viewer.jsp").include(request, response);
+            } else {
+                // create a request attribute for accessing the content
+                request.setAttribute(ViewableContent.CONTENT_REQUEST_ATTRIBUTE, new ViewableContent() {
+                    @Override
+                    public InputStream getContentStream() {
+                        return bis;
+                    }
+
+                    @Override
+                    public String getContent() throws IOException {
+                        // detect the charset
+                        final CharsetDetector detector = new CharsetDetector();
+                        detector.setText(bis);
+                        detector.enableInputFilter(true);
+                        final CharsetMatch match = detector.detect();
+
+                        // ensure we were able to detect the charset
+                        if (match == null) {
+                            throw new IOException("Unable to detect character encoding.");
+                        }
+
+                        // convert the stream using the detected charset
+                        return IOUtils.toString(bis, match.getName());
+                    }
+
+                    @Override
+                    public ViewableContent.DisplayMode getDisplayMode() {
+                        return displayMode;
+                    }
+
+                    @Override
+                    public String getFileName() {
+                        return downloadableContent.getFilename();
+                    }
+
+                    @Override
+                    public String getContentType() {
+                        return mimeType;
+                    }
+                });
+
+                try {
+                    // generate the content
+                    final ServletContext viewerContext = servletContext.getContext(contentViewerUri);
+                    viewerContext.getRequestDispatcher("/view-content").include(request, response);
+                } catch (final Exception e) {
+                    String message = e.getMessage() != null ? e.getMessage() : e.toString();
+                    message = "Unable to generate view of data: " + message;
+                    
+                    // log the error
+                    logger.error(message);
+                    if (logger.isDebugEnabled()) {
+                        logger.error(StringUtils.EMPTY, e);
+                    }
+                    
+                    // populate the request attributes
+                    request.setAttribute("title", "Error");
+                    request.setAttribute("messages", message);
+
+                    // forward to the error page
+                    final ServletContext viewerContext = servletContext.getContext("/nifi");
+                    viewerContext.getRequestDispatcher("/message").forward(request, response);
+                    return;
+                }
+                
+                // remove the request attribute
+                request.removeAttribute(ViewableContent.CONTENT_REQUEST_ATTRIBUTE);
+            }
+        }
+
+        // generate footer
+        request.getRequestDispatcher("/WEB-INF/jsp/footer.jsp").include(request, response);
+    }
+
+    /**
+     * Get the content request context based on the specified request.
+     * @param request
+     * @return 
+     */
+    private ContentRequestContext getContentRequest(final HttpServletRequest request) {
+        return new ContentRequestContext() {
+            @Override
+            public String getDataUri() {
+                return request.getParameter("ref");
+            }
+
+            @Override
+            public String getClusterNodeId() {
+                final String ref = request.getParameter("ref");
+                return StringUtils.substringAfterLast(ref, "clusterNodeId=");
+            }
+
+            @Override
+            public String getClientId() {
+                return request.getParameter("clientId");
+            }
+
+            @Override
+            public String getProxiedEntitiesChain() {
+                return request.getHeader("X-ProxiedEntitiesChain");
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/resources/META-INF/NOTICE
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/resources/META-INF/NOTICE b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/resources/META-INF/NOTICE
new file mode 100644
index 0000000..d91a952
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,19 @@
+nifi-web-docs
+Copyright 2014-2015 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+===========================================
+Apache Software License v2
+===========================================
+
+The following binary components are provided under the Apache Software License v2
+
+  (ASLv2) Apache Commons Lang
+    The following NOTICE information applies:
+      Apache Commons Lang
+      Copyright 2001-2014 The Apache Software Foundation
+
+      This product includes software from the Spring Framework,
+      under the Apache License 2.0 (see: StringUtils.containsWhitespace())

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/footer.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/footer.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/footer.jsp
new file mode 100644
index 0000000..5288315
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/footer.jsp
@@ -0,0 +1,20 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+        </div>
+    </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e05c9fd2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
new file mode 100644
index 0000000..82382f6
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
@@ -0,0 +1,92 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+        <link rel="shortcut icon" href="../nifi/images/nifi16.ico"/>
+        <title>NiFi</title>
+        <link href="css/main.css" rel="stylesheet" type="text/css" />
+        <link href="../nifi/css/message-pane.css" rel="stylesheet" type="text/css" />
+        <link href="../nifi/css/message-page.css" rel="stylesheet" type="text/css" />
+        <link rel="stylesheet" href="../nifi/js/jquery/combo/jquery.combo.css" type="text/css" />
+        <link rel="stylesheet" href="../nifi/css/reset.css" type="text/css" />
+        <script type="text/javascript" src="../nifi/js/jquery/jquery-2.1.1.min.js"></script>
+        <script type="text/javascript" src="../nifi/js/jquery/combo/jquery.combo.js"></script>
+        <script type="text/javascript">
+            var $$ = $.noConflict(true);
+            $$(document).ready(function () {
+                var url = '${requestUrl}';
+                var ref = '${param.ref}';
+                
+                // create the parameters
+                var params = {
+                    ref: ref
+                };
+                
+                // include the cluster node if appropriate
+                var clusterNodeId = '${param.clusterNodeId}';
+                if (clusterNodeId !== null && clusterNodeId !== '') {
+                    params['clusterNodeId'] = clusterNodeId;
+                }
+                
+                // determine the appropriate mode to select initially
+                var initialMode = '${param.mode}';
+                if (initialMode === null && initialMode === '') {
+                    initialMode = 'Original';
+                }
+                
+                var currentLocation = null;
+                $$('#view-as').combo({
+                    options: [{
+                            text: 'original',
+                            value: 'Original'
+                        }, {
+                            text: 'formatted',
+                            value: 'Formatted'
+                        }, {
+                            text: 'hex',
+                            value: 'Hex'
+                        }],
+                    selectedOption: {
+                        value: initialMode
+                    },
+                    select: function (option) {
+                        // just record the selection during creation
+                        if (currentLocation === null) {
+                            currentLocation = option.value;
+                            return;
+                        }
+                        
+                        // if the selection has changesd, reload the page
+                        if (currentLocation !== option.value) {
+                            window.location.href = url + '?' + $$.param($$.extend({
+                                mode: option.value
+                            }, params));
+                        }
+                    }
+                });
+            });
+        </script>
+    </head>
+    <body class="message-pane">
+        <div id="view-as-label">View as</div>
+        <div id="view-as" class="pointer button-normal"></div>
+        <div id="content-filename"><span class="content-label">filename</span>${filename}</div>
+        <div id="content-type"><span class="content-label">content type</span>${contentType}</div>
+        <div class="message-pane-message-box">
\ No newline at end of file


Mime
View raw message