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 e88ed13d8d
    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 93b361e69b
    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 c9eb237895
    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 8b911c5aab
    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 a6740a6e2c
    Author: joewitt <joewitt@apache.org>
    Date:   Thu Mar 19 01:21:32 2015 -0400

        NIFI-399 addressed items in the ticket

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

        NIFI-399 initial port

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

        NIFI-396 reverting accidentially modified whitespace in TestStandardProcessSession

    commit cd183be441
    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 e2760f8c98
    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 7272d0df58
    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 eb5ec703ba
    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.
This commit is contained in:
Matt Gilman 2015-03-22 23:13:07 -04:00
parent 0bd27847ab
commit e05c9fd20e
44 changed files with 1754 additions and 40 deletions

View File

@ -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();
}

View File

@ -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>

View File

@ -113,6 +113,11 @@
<artifactId>nifi-client-dto</artifactId>
<scope>compile</scope>
</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>
@ -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>

View File

@ -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);
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
// 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

View File

@ -102,6 +102,11 @@
<artifactId>nifi-web-utils</artifactId>
<scope>provided</scope>
</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>

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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.");

View File

@ -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;

View File

@ -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;

View File

@ -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">

View File

@ -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>

View File

@ -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);
}

View File

@ -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();
}

View File

@ -14,12 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.util;
package org.apache.nifi.web;
import java.io.InputStream;
/**
*
* Represents content that can be downloaded.
*/
public final class DownloadableContent {
@ -33,14 +33,29 @@ public final class DownloadableContent {
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;
}

View File

@ -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>

View File

@ -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");
}
};
}
}

View File

@ -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())

View File

@ -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>

View File

@ -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">

View File

@ -0,0 +1,32 @@
<%--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<link rel="stylesheet" href="js/hexview/hexview.default.css" type="text/css" />
<script type="text/javascript" src="../nifi/js/jquery/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="js/hexview/hexview.js"></script>
<div id="hexview-content" class="hexviewwindow" title="">
${content}
<form id="hexviewwindow_params">
<input type="hidden" name="highlights" value="" />
<input type="hidden" name="row_width" value="16" />
<input type="hidden" name="word_size" value="1" />
<input type="hidden" name="hide_0x" value="1" />
<input type="hidden" name="caption" value="" />
</form>
</div>
<div id="trancation-message">Showing up to 1.5kb</div>

View File

@ -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 id="no-viewer">
No viewer is registered for this content type.
</div>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>nifi-content-viewer</display-name>
<servlet>
<servlet-name>ContentViewerController</servlet-name>
<servlet-class>org.apache.nifi.web.ContentViewerController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ContentViewerController</servlet-name>
<url-pattern></url-pattern>
</servlet-mapping>
</web-app>

View File

@ -0,0 +1,113 @@
/*
* 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.
*/
* {
margin: 0;
padding: 0;
}
#view-as-label {
position: absolute;
top: 72px;
left: 144px;
line-height: 24px;
font-size: 12px;
}
#view-as {
position: absolute;
top: 72px;
left: 200px;
width: 80px;
height: 18px;
}
#content-filename {
position: absolute;
top: 58px;
right: 50px;
line-height: 24px;
font-size: 12px;
}
#content-type {
position: absolute;
top: 75px;
right: 50px;
line-height: 24px;
font-size: 12px;
}
.content-label {
font-weight: bold;
margin-right: 8px;
}
.pointer {
cursor: pointer;
}
.hidden {
display: none;
}
/* hex viewer */
#hexview-content {
position: absolute;
right: 50px;
bottom: 50px;
left: 100px;
top: 100px;
border: 1px solid #aaa;
overflow: auto;
background-color: #fff;
}
#hexview-content table.hexviewerwindow_table {
border: none;
margin-left: 0;
background-color: #fff;
}
#hexview-content td {
padding: 2px;
}
#trancation-message {
position: absolute;
left: 100px;
bottom: 35px;
color: #666;
font-style: italic;
font-size: 11px;
}
/* no viewer */
#no-viewer {
position: absolute;
right: 50px;
bottom: 50px;
left: 100px;
top: 100px;
border: 1px solid #aaa;
overflow: auto;
background-color: #fff;
font-style: italic;
padding: 5px;
font-size: 13px;
}

View File

@ -0,0 +1,32 @@
HexViewJS License
-----------------
HexViewJS is written by Nick McVeity <nmcveity@gmail.com> and is
licensed under the terms of the MIT license reproduced below.
========================================================================
Copyright (c) 2010 Nick McVeity <nmcveity@gmail.com>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================

View File

@ -0,0 +1,10 @@
.hexviewerwindow { font-family: monospace; background-color: #F2F2F2;}
div.hexviewerwindow { padding: 20px; }
.hexviewerwindow_table { border-collapse:collapse; border: 5px solid grey; margin-left: 16px; caption-side:bottom; }
.hexviewerwindow_offset {background: #A9D0F5; padding-right: 8px; }
.hexviewerwindow_visual {background: #A9F5F2; padding-left: 8px; }
.hexviewerwindow_code {}
.hexviewerwindow_code_hi {background: #F4FA58; }
.hexviewerwindow_border_start {border-left: solid #E0E0E0 1px; }
.hexviewerwindow_border_middle {border-bottom: solid #E0E0E0 1px; border-top: solid #E0E0E0 1px;}
.hexviewerwindow_border_end {border-right: solid #E0E0E0 1px; border-top: solid #E0E0E0 1px; }

View File

@ -0,0 +1,199 @@
$(document).ready(function () {
var HEX = '0123456789ABCDEF';
function dec2_to_hex(dec)
{
if (dec < 0)
dec = 0;
if (dec > 255)
dec = 255;
return HEX.charAt(Math.floor(dec / 16)) + HEX.charAt(dec % 16);
}
function dec_to_hex8(dec)
{
var str = "";
for (var i = 3; i >= 0; i--)
{
str += dec2_to_hex((dec >> (i*8)) & 255);
}
return str;
}
function remove_whitespace(str)
{
return str.replace(/\n/g, "")
.replace(/\t/g, "")
.replace(/ /g, "")
.replace(/\r/g, "");
}
var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function base64_decode(encoded)
{
var decoded = "";
for (var i = 0; i < encoded.length; i += 4)
{
var ch0 = encoded.charAt(i+0);
var ch1 = encoded.charAt(i+1);
var ch2 = encoded.charAt(i+2);
var ch3 = encoded.charAt(i+3);
var index0 = BASE64_CHARS.indexOf(ch0);
var index1 = BASE64_CHARS.indexOf(ch1);
var index2 = BASE64_CHARS.indexOf(ch2);
var index3 = BASE64_CHARS.indexOf(ch3);
decoded += String.fromCharCode((index0 << 2) | (index1 >> 4));
decoded += String.fromCharCode(((index1 & 15) << 4) | (index2 >> 2));
// skip the base64 padding as those weren't present in the actual bytes
var token = String.fromCharCode(((index2 & 3) << 6) | index3);
if (index3 !== 64 || token !== '@') {
decoded += token;
}
}
return decoded;
}
function markup_hexviewwindow(div, index)
{
var entityMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2f;'
};
function escapeHtml(string) {
if (string === null || typeof string === 'undefined') {
return '';
} else {
return String(string).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
}
}
var bin_data = base64_decode(remove_whitespace(div.text()));
var line_data;
var title = div.attr("title");
var highlights_str = $("form#hexviewwindow_params input[name='highlights']", div).attr("value").split(',');
var highlights = [];
for (var i = 0; i < highlights_str.length; i++)
{
highlights.push(highlights_str[i].split(":"));
}
var params = title.split(':');
var step = parseInt($("form#hexviewwindow_params input[name='row_width']", div).attr("value"));
var word_size = parseInt($("form#hexviewwindow_params input[name='word_size']", div).attr("value"));
var hide_0x = parseInt($("form#hexviewwindow_params input[name='hide_0x']", div).attr("value"));
var decimal_offset = parseInt($("form#hexviewwindow_params input[name='decimal_offset']", div).attr("value"));
var start_byte_1 = parseInt($("form#hexviewwindow_params input[name='start_byte_1']", div).attr("value"));
var caption = $("form#hexviewwindow_params input[name='caption']", div).attr("value");
div.text("");
div.append("<table></table>");
var offset = (start_byte_1 ? 1 : 0);
function apply_highlights(index)
{
for (var j = 0; j < highlights.length; j++)
{
if ((index >= highlights[j][0]) && (index <= highlights[j][1]))
{
if (index === highlights[j][0])
{
$("table tr td:last", div).addClass("hexviewerwindow_border_start");
}
if (index === highlights[j][1])
{
$("table tr td:last", div).addClass("hexviewerwindow_border_end");
}
$("table tr td:last", div).addClass("hexviewerwindow_code_hi hexviewerwindow_border_middle");
$("table tr td:last", div).attr("style", "background-color: " + highlights[j][2] + ";");
$("table tr td:last", div).attr("title", highlights[j][3]);
runlen += 1;
}
else
{
$("table tr td:last", div).addClass("hexviewerwindow_code");
}
}
}
if (caption)
$("table", div).append("<caption>" + escapeHtml(caption) + "</caption>");
while (bin_data.length > 0)
{
line_data = bin_data.slice(0, step);
bin_data = bin_data.slice(step);
$("table", div).addClass("hexviewerwindow_table");
$("table", div).append("<tr></tr>").addClass("hexviewerwindow");
$("table tr:last", div).append("<td>" + (decimal_offset ? ("00000000"+offset).slice(-8) : "0x" + dec_to_hex8(offset)) + "</td>");
$("table tr td:last", div).addClass("hexviewerwindow_offset");
var runlen = 0;
for (var i = 0; i < line_data.length; i += word_size)
{
var num = "";
for (var j = 0; j < word_size; j++)
{
num += dec2_to_hex(line_data.charCodeAt(i+j));
}
$("table tr:last", div).append("<td>" + (hide_0x ? "" : "0x") + num + "</td>");
apply_highlights(offset+i);
}
var text = "";
for (var i = 0; i < line_data.length; i++)
{
var cc = line_data.charCodeAt(i);
if ((cc >= 32) && (cc <= 126))
{
text = text + line_data.charAt(i);
}
else
{
text = text + ".";
}
}
if (line_data.length < step)
$("table tr td:last", div).attr("colspan", Math.floor((step - line_data.length) / word_size) + 1);
offset += step;
$("table tr:last", div).append("<td>" + escapeHtml(text) + "</td>");
$("table tr td:last", div).addClass("hexviewerwindow_visual");
}
}
$("div.hexviewwindow").each(function (index) {
markup_hexviewwindow($(this), index);
});
});

View File

@ -115,6 +115,17 @@
<url-pattern>/bulletin-board</url-pattern>
</servlet-mapping>
<!-- servlet to support message page -->
<servlet>
<servlet-name>MessagePage</servlet-name>
<jsp-file>/WEB-INF/pages/message-page.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>MessagePage</servlet-name>
<url-pattern>/message</url-pattern>
</servlet-mapping>
<!-- servlet to support image downloading -->
<servlet>

View File

@ -0,0 +1,20 @@
.CodeMirror-foldmarker {
color: blue;
text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
font-family: arial;
line-height: .3;
cursor: pointer;
}
.CodeMirror-foldgutter {
width: .7em;
}
.CodeMirror-foldgutter-open,
.CodeMirror-foldgutter-folded {
cursor: pointer;
}
.CodeMirror-foldgutter-open:after {
content: "\25BE";
}
.CodeMirror-foldgutter-folded:after {
content: "\25B8";
}

View File

@ -86,3 +86,11 @@ div.combo-nifi-tooltip {
background-color: #FFFFA3;
color: #454545;
}
div.button-normal {
background: transparent url(../../../images/bgButton.png) repeat-x center center;
}
div.button-over {
background: transparent url(../../../images/bgButtonOver.png) repeat-x center center;
}

View File

@ -29,8 +29,10 @@
<module>nifi-web-api</module>
<module>nifi-web-error</module>
<module>nifi-web-docs</module>
<module>nifi-web-content-viewer</module>
<module>nifi-web-ui</module>
<module>nifi-jetty</module>
<module>nifi-web-content-access</module>
</modules>
<dependencyManagement>
<dependencies>
@ -52,6 +54,12 @@
<type>war</type>
<version>0.1.0-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-web-content-viewer</artifactId>
<type>war</type>
<version>0.1.0-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-web-ui</artifactId>

View File

@ -63,6 +63,11 @@
<artifactId>nifi-client-dto</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-web-content-access</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security</artifactId>

View File

@ -0,0 +1,71 @@
<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-standard-bundle</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
</parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-content-viewer</artifactId>
<packaging>war</packaging>
<dependencies>
<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.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</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>

View File

@ -0,0 +1,103 @@
/*
* 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.PrintWriter;
import java.io.StringWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.nifi.web.ViewableContent.DisplayMode;
import org.codehaus.jackson.map.ObjectMapper;
/**
*
*/
@WebServlet(name = "StandardContentViewer", urlPatterns = {"/view-content"})
public class StandardContentViewerController extends HttpServlet {
/**
*
* @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(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
final ViewableContent content = (ViewableContent) request.getAttribute(ViewableContent.CONTENT_REQUEST_ATTRIBUTE);
// handle json/xml
if ("application/json".equals(content.getContentType()) || "application/xml".equals(content.getContentType()) || "text/plain".equals(content.getContentType())) {
final String formatted;
// leave the content alone if specified
if (DisplayMode.Original.equals(content.getDisplayMode())) {
formatted = content.getContent();
} else {
if ("application/json".equals(content.getContentType())) {
// format json
final ObjectMapper mapper = new ObjectMapper();
final Object objectJson = mapper.readValue(content.getContent(), Object.class);
formatted = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectJson);
} else if ("application/xml".equals(content.getContentType())) {
// format xml
final StringWriter writer = new StringWriter();
try {
final StreamSource source = new StreamSource(content.getContentStream());
final StreamResult result = new StreamResult(writer);
final TransformerFactory transformFactory = TransformerFactory.newInstance();
final Transformer transformer = transformFactory.newTransformer();
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(source, result);
} catch (final TransformerFactoryConfigurationError | TransformerException te) {
throw new IOException("Unable to transform content as XML: " + te, te);
}
// get the transformed xml
formatted = writer.toString();
} else {
// leave plain text alone when formatting
formatted = content.getContent();
}
}
// defer to the jsp
request.setAttribute("mode", content.getContentType());
request.setAttribute("content", formatted);
request.getRequestDispatcher("/WEB-INF/jsp/codemirror.jsp").include(request, response);
} else {
final PrintWriter out = response.getWriter();
out.println("Unexpected content type: " + content.getContentType());
}
}
}

View File

@ -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())

View File

@ -0,0 +1,3 @@
application/xml
application/json
text/plain

View File

@ -0,0 +1,47 @@
<%--
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" %>
<link rel="stylesheet" href="../nifi/js/codemirror/lib/codemirror.css" type="text/css" />
<link rel="stylesheet" href="../nifi/js/codemirror/addon/fold/foldgutter.css" type="text/css" />
<script type="text/javascript" src="../nifi/js/codemirror/lib/codemirror-compressed.js"></script>
<script type="text/javascript" src="../nifi/js/jquery/jquery-2.1.1.min.js"></script>
<textarea id="codemirror-content">${content}</textarea>
<script type="text/javascript">
$(document).ready(function() {
var field = document.getElementById('codemirror-content');
var editor = CodeMirror.fromTextArea(field, {
mode: '${mode}',
lineNumbers: true,
matchBrackets: true,
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
readOnly: true
});
var setEditorSize = function() {
editor.setSize($(window).width() - 150, $(window).height() - 150);
};
// reset the editor size when the window changes
$(window).resize(setEditorSize);
// initialize the editor size
setEditorSize();
});
</script>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>nifi-standard-content-viewer</display-name>
<servlet>
<servlet-name>StandardContentViewer</servlet-name>
<servlet-class>org.apache.nifi.web.StandardContentViewerController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>StandardContentViewer</servlet-name>
<url-pattern>/view-content</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>view-content</welcome-file>
</welcome-file-list>
</web-app>

View File

@ -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.
*/
* {
margin: 0;
padding: 0;
}

View File

@ -40,5 +40,10 @@
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-reporting-tasks</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-content-viewer</artifactId>
<type>war</type>
</dependency>
</dependencies>
</project>

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<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
@ -26,8 +27,9 @@
<module>nifi-standard-processors</module>
<module>nifi-standard-prioritizers</module>
<module>nifi-standard-reporting-tasks</module>
<module>nifi-standard-content-viewer</module>
<module>nifi-standard-nar</module>
</modules>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
@ -45,6 +47,12 @@
<artifactId>nifi-standard-reporting-tasks</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-content-viewer</artifactId>
<type>war</type>
<version>0.1.0-incubating-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>