Repository: incubator-nifi Updated Branches: refs/heads/NIFI-250 e750579d7 -> 6c0587785
NIFI-250: - Starting to refactor support for UI extensions. - Deprecating previous support. Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/6c058778 Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/6c058778 Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/6c058778 Branch: refs/heads/NIFI-250 Commit: 6c058778534adae967ee34ec5fb2b8c20b66d43b Parents: e750579 Author: Matt Gilman <[email protected]> Authored: Wed Mar 18 16:09:44 2015 -0400 Committer: Matt Gilman <[email protected]> Committed: Wed Mar 18 16:09:44 2015 -0400 ---------------------------------------------------------------------- .../apache/nifi/web/ComponentConfiguration.java | 122 ++++++ .../apache/nifi/web/ConfigurationAction.java | 86 +++++ .../org/apache/nifi/web/NiFiWebContext.java | 1 + .../apache/nifi/web/NiFiWebContextConfig.java | 1 + .../nifi/web/ProcessorConfigurationAction.java | 1 + .../java/org/apache/nifi/web/ProcessorInfo.java | 1 + .../nifi/web/UiExtensionRequestContext.java | 95 +++++ ...kControllerServiceInitializationContext.java | 6 + .../mock/MockControllerServiceLookup.java | 5 + .../MockReportingInitializationContext.java | 7 + .../web/HttpServletRequestContextConfig.java | 1 + .../nifi-framework/nifi-web/nifi-jetty/pom.xml | 10 + .../nifi-ui-extension-controller/pom.xml | 66 ++++ .../extension/ConfigureComponentController.java | 253 +++++++++++++ .../src/main/webapp/WEB-INF/web.xml | 26 ++ .../nifi-web/nifi-ui-extension/pom.xml | 21 ++ .../apache/nifi/ui/extension/UiExtension.java | 46 +++ .../nifi/ui/extension/UiExtensionMapping.java | 40 ++ .../nifi/ui/extension/UiExtensionType.java | 26 ++ .../controller/UiExtensionControllerFacade.java | 101 +++++ .../UiExtensionControllerRequest.java | 64 ++++ .../nifi-web/nifi-web-api/pom.xml | 5 + .../apache/nifi/web/StandardNiFiWebContext.java | 1 + .../StandardUiExtensionControllerFacade.java | 377 +++++++++++++++++++ .../src/main/resources/nifi-web-api-context.xml | 7 + .../nifi-framework/nifi-web/pom.xml | 13 +- nifi/pom.xml | 5 + 27 files changed, 1384 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentConfiguration.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentConfiguration.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentConfiguration.java new file mode 100644 index 0000000..bb47760 --- /dev/null +++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentConfiguration.java @@ -0,0 +1,122 @@ +/* + * 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.util.Collection; +import java.util.Map; + +/** + * + */ +public class ComponentConfiguration { + + private final String id; + private final String name; + private final String type; + private final String state; + private final String annotationData; + private final Map<String, String> properties; + private final Collection<String> validationErrors; + + private ComponentConfiguration(final Builder builder) { + this.id = builder.id; + this.name = builder.name; + this.type = builder.type; + this.state = builder.state; + this.annotationData = builder.annotationData; + this.properties = builder.properties; + this.validationErrors = builder.validationErrors; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public String getState() { + return state; + } + + public String getAnnotationData() { + return annotationData; + } + + public Map<String, String> getProperties() { + return properties; + } + + public Collection<String> getValidationErrors() { + return validationErrors; + } + + public static final class Builder { + + private String id; + private String name; + private String type; + private String state; + private String annotationData; + private Map<String, String> properties; + private Collection<String> validationErrors; + + public Builder id(final String id) { + this.id = id; + return this; + } + + public Builder name(final String name) { + this.name = name; + return this; + } + + public Builder type(final String type) { + this.type = type; + return this; + } + + public Builder state(final String state) { + this.state = state; + return this; + } + + public Builder annotationData(final String annotationData) { + this.annotationData = annotationData; + return this; + } + + public Builder properties(final Map<String, String> properties) { + this.properties = properties; + return this; + } + + public Builder validateErrors(final Collection<String> validationErrors) { + this.validationErrors = validationErrors; + return this; + } + + public ComponentConfiguration build() { + return new ComponentConfiguration(this); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java new file mode 100644 index 0000000..d46d489 --- /dev/null +++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web; + +/** + * + */ +public class ConfigurationAction { + + private final String field; + private final String previousValue; + private final String value; + + private ConfigurationAction(final Builder builder) { + this.field = builder.field; + this.previousValue = builder.previousValue; + this.value = builder.value; + } + + /** + * Gets the name of the field, property, etc that has been modified. + * + * @return + */ + public String getField() { + return field; + } + + /** + * Gets the previous value. + * + * @return + */ + public String getPreviousValue() { + return previousValue; + } + + /** + * Gets the new value. + * + * @return + */ + public String getValue() { + return value; + } + + public static class Builder { + + private String field; + private String previousValue; + private String value; + + public Builder field(final String field) { + this.field = field; + return this; + } + + public Builder previousValue(final String previousValue) { + this.previousValue = previousValue; + return this; + } + + public Builder value(final String value) { + this.value = value; + return this; + } + + public ConfigurationAction build() { + return new ConfigurationAction(this); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java index 4c4f25d..01702ad 100644 --- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java +++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java @@ -24,6 +24,7 @@ import org.apache.nifi.controller.ControllerService; * NiFi web context providing limited access to dataflow configuration for * processor custom UIs. */ +@Deprecated public interface NiFiWebContext { /** http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java index 808b9d6..2df94e4 100644 --- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java +++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java @@ -19,6 +19,7 @@ package org.apache.nifi.web; /** * Context configuration for methods invoked from the NiFiWebContext. */ +@Deprecated public interface NiFiWebContextConfig { /** http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java index 8385e4a..ce5e069 100644 --- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java +++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java @@ -19,6 +19,7 @@ package org.apache.nifi.web; /** * */ +@Deprecated public class ProcessorConfigurationAction { private final String processorId; http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java index 0481098..e87e73e 100644 --- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java +++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java @@ -22,6 +22,7 @@ import java.util.Map; /** * */ +@Deprecated public class ProcessorInfo { private final String id; http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionRequestContext.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionRequestContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionRequestContext.java new file mode 100644 index 0000000..24c4226 --- /dev/null +++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionRequestContext.java @@ -0,0 +1,95 @@ +/* + * 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.util.Collection; + +import org.apache.nifi.controller.ControllerService; + +/** + * NiFi web context providing limited access to dataflow configuration for + * processor custom UIs. + */ +public interface UiExtensionRequestContext { + + public static final String ATTRIBUTE_KEY = "ui-extension-request-context"; + + /** + * Gets the ControllerService for the specified identifier. If a + * corresponding service cannot be found, null is returned. If this NiFi is + * clustered, the only services available will be those those + * availability is NCM only. + * + * @param serviceIdentifier + * @return + */ + ControllerService getControllerService(String serviceIdentifier); + + /** + * Provides a mechanism for custom UIs to save actions to appear in NiFi + * configuration history. Note all fields within each Action must be + * populated. Null values will result in a failure to insert the audit + * record. Since the saving to these actions is separate from the actual + * configuration change, a failure to insert here will just generate a + * warning log message. The recording of these actions typically happens + * after a configuration change is applied. Since those changes have already + * been applied to the flow, we cannot revert them because of a failure to + * insert an audit record. + * + * @param actions + */ + void saveActions(Collection<ConfigurationAction> actions); + + /** + * Gets the current user dn. Returns null if no user is found. + * + * @return + */ + String getCurrentUserDn(); + + /** + * Gets the current user name. Returns null if no user is found. + * + * @return + */ + String getCurrentUserName(); + + /** + * Sets the annotation data for the underlying component. + * + * @param annotationData + * @return the configuration for the underlying component + * @throws ResourceNotFoundException if the underlying component does not exit + * @throws InvalidRevisionException if a revision other than the current + * revision is given + * @throws ClusterRequestException if the annotation data was unable to be + * set for the underlying component. This exception will only be thrown when operating + * in a cluster. + */ + ComponentConfiguration setAnnotationData(String annotationData) throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException; + + /** + * Gets the configuration for the underlying component (including annotation data). + * + * @return the configuration for the underlying component + * @throws ResourceNotFoundException if the underlying component does not exit + * @throws ClusterRequestException if the underlying component was unable to be + * retrieved from the cluster. This exception will only be thrown when + * operating in a cluster. + */ + ComponentConfiguration getComponentDetails() throws ResourceNotFoundException, ClusterRequestException; +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java index fcd3ea3..69ce8d9 100644 --- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java @@ -18,6 +18,7 @@ package org.apache.nifi.documentation.mock; import org.apache.nifi.controller.ControllerServiceInitializationContext; import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.logging.ComponentLog; /** * A Mock ControllerServiceInitializationContext so that ControllerServices can @@ -37,4 +38,9 @@ public class MockControllerServiceInitializationContext implements ControllerSer return new MockControllerServiceLookup(); } + @Override + public ComponentLog getLogger() { + return null; + } + } http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java index f11bc68..303c983 100644 --- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java @@ -52,4 +52,9 @@ public class MockControllerServiceLookup implements ControllerServiceLookup { return Collections.emptySet(); } + @Override + public String getControllerServiceName(String serviceIdentifier) { + return null; + } + } http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java index 910ce5a..39baf0d 100644 --- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java @@ -19,6 +19,7 @@ package org.apache.nifi.documentation.mock; import java.util.concurrent.TimeUnit; import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.logging.ComponentLog; import org.apache.nifi.reporting.ReportingInitializationContext; import org.apache.nifi.scheduling.SchedulingStrategy; @@ -60,4 +61,10 @@ public class MockReportingInitializationContext implements ReportingInitializati public SchedulingStrategy getSchedulingStrategy() { return SchedulingStrategy.TIMER_DRIVEN; } + + @Override + public ComponentLog getLogger() { + return null; + } + } http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java index e39ebcc..e376ab6 100644 --- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java @@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletRequest; * An implementation of the NiFiWebContextConfig that retrieves configuration * from a HttpServletRequest instance. */ +@Deprecated public class HttpServletRequestContextConfig implements NiFiWebContextConfig { public static final String PROCESSOR_ID_PARAM = "processorId"; http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/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..d86961c 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 @@ -120,6 +120,11 @@ </dependency> <dependency> <groupId>org.apache.nifi</groupId> + <artifactId>nifi-ui-extension</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> <artifactId>nifi-web-security</artifactId> <scope>compile</scope> </dependency> @@ -155,6 +160,11 @@ </dependency> <dependency> <groupId>org.apache.nifi</groupId> + <artifactId>nifi-ui-extension-controller</artifactId> + <type>war</type> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> <artifactId>nifi-framework-cluster</artifactId> <scope>compile</scope> </dependency> http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/pom.xml ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/pom.xml new file mode 100644 index 0000000..ee2e22c --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/pom.xml @@ -0,0 +1,66 @@ +<?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/xsd/maven-4.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> + <artifactId>nifi-web</artifactId> + <groupId>org.apache.nifi</groupId> + <version>0.1.0-incubating-SNAPSHOT</version> + </parent> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-ui-extension-controller</artifactId> + <packaging>war</packaging> + <dependencies> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-user-actions</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-ui-extension</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</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> http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/java/org/apache/nifi/ui/extension/ConfigureComponentController.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/java/org/apache/nifi/ui/extension/ConfigureComponentController.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/java/org/apache/nifi/ui/extension/ConfigureComponentController.java new file mode 100644 index 0000000..25103ef --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/java/org/apache/nifi/ui/extension/ConfigureComponentController.java @@ -0,0 +1,253 @@ +/* + * 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.ui.extension; + +import org.apache.nifi.web.controller.UiExtensionControllerRequest; +import org.apache.nifi.web.controller.UiExtensionControllerFacade; +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +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.nifi.action.Action; +import org.apache.nifi.action.Component; +import org.apache.nifi.action.Operation; +import org.apache.nifi.action.component.details.ExtensionDetails; +import org.apache.nifi.action.details.ConfigureDetails; +import org.apache.nifi.controller.ControllerService; +import org.apache.nifi.web.ClusterRequestException; +import org.apache.nifi.web.ComponentConfiguration; +import org.apache.nifi.web.ConfigurationAction; +import org.apache.nifi.web.InvalidRevisionException; +import org.apache.nifi.web.ResourceNotFoundException; +import org.apache.nifi.web.Revision; +import org.apache.nifi.web.UiExtensionRequestContext; + + +/** + * + */ +public class ConfigureComponentController extends HttpServlet { + + public static final String ID_PARAM = "id"; + public static final String CLIENT_ID_PARAM = "clientId"; + public static final String VERSION_PARAM = "version"; + + /** + * + * @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 { + // get the content + final ServletContext servletContext = request.getServletContext(); + final Object extensionMappings = (Object) servletContext.getAttribute("nifi-ui-extension-mapping"); + final UiExtensionControllerFacade controllerFacade = (UiExtensionControllerFacade) servletContext.getAttribute("nifi-ui-extension-controller-facade"); + + // get the component type + final String type = request.getParameter("type"); + + // ensure the request has + if (type == null) { + response.getWriter().println("Request missing the component type."); + return; + } + + // change later + final UiExtensionType extensionType = UiExtensionType.Processor; + + // build the ui extension request context config + final UiExtensionControllerRequest config = getRequestContextConig(extensionType, request); + + // get the initial component details + final ComponentConfiguration details = controllerFacade.getComponentDetails(config); + + // lookup a viewer for the content + final String uiExtensionUri = servletContext.getInitParameter(type); + + // ensure the registered viewer is found + if (uiExtensionUri == null) { + response.getWriter().println("No custom UI is registered for " + type); + } + + // set the attribute for the custom ui to interact with nifi + request.setAttribute(UiExtensionRequestContext.ATTRIBUTE_KEY, new UiExtensionRequestContext() { + @Override + public ControllerService getControllerService(final String serviceIdentifier) { + return controllerFacade.getControllerService(serviceIdentifier); + } + + @Override + public void saveActions(Collection<ConfigurationAction> uiExtensionActions) { + final Collection<Action> actions = new ArrayList<>(); + + // conver the action models + if (uiExtensionActions != null) { + final Date now = new Date(); + + // create the extension details + ExtensionDetails extensionDetails = new ExtensionDetails(); + extensionDetails.setType(details.getType()); + + for (final ConfigurationAction extensionAction : uiExtensionActions) { + // create the action details + final ConfigureDetails actionDetails = new ConfigureDetails(); + actionDetails.setName(extensionAction.getField()); + actionDetails.setValue(extensionAction.getValue()); + actionDetails.setPreviousValue(extensionAction.getPreviousValue()); + + // create a configuration action + Action configurationAction = new Action(); + configurationAction.setUserDn(getCurrentUserDn()); + configurationAction.setUserName(getCurrentUserName()); + configurationAction.setOperation(Operation.Configure); + configurationAction.setTimestamp(now); + configurationAction.setSourceId(details.getId()); + configurationAction.setSourceName(details.getName()); + configurationAction.setSourceType(Component.Processor); + configurationAction.setComponentDetails(extensionDetails); + configurationAction.setActionDetails(actionDetails); + + // add the action + actions.add(configurationAction); + } + } + + // save the actions + if (!actions.isEmpty()) { + controllerFacade.saveActions(actions); + } + } + + @Override + public String getCurrentUserDn() { + return controllerFacade.getCurrentUserDn(); + } + + @Override + public String getCurrentUserName() { + return controllerFacade.getCurrentUserName(); + } + + @Override + public ComponentConfiguration setAnnotationData(String annotationData) throws ClusterRequestException, InvalidRevisionException, ResourceNotFoundException{ + return controllerFacade.setAnnotationData(config, annotationData); + } + + @Override + public ComponentConfiguration getComponentDetails() throws ClusterRequestException, ResourceNotFoundException { + return controllerFacade.getComponentDetails(config); + } + }); + + // generate the content + final ServletContext customUiContext = servletContext.getContext(uiExtensionUri); + customUiContext.getRequestDispatcher("/configure").forward(request, response); + } + + /** + * Creates a UiExtensionRequestContextConfig from the specified request. + * + * @param request + * @return + */ + private UiExtensionControllerRequest getRequestContextConig(final UiExtensionType extensionType, final HttpServletRequest request) { + return new UiExtensionControllerRequest() { + + @Override + public UiExtensionType getExtensionType() { + return extensionType; + } + + @Override + public String getScheme() { + return request.getScheme(); + } + + @Override + public String getId() { + return request.getParameter(ID_PARAM); + } + + @Override + public Revision getRevision() { + final String versionParamVal = request.getParameter(VERSION_PARAM); + Long version; + try { + version = Long.parseLong(versionParamVal); + } catch (final Exception ex) { + version = null; + } + + final String clientId = request.getParameter(CLIENT_ID_PARAM); + + return new Revision(version, clientId); + } + + @Override + public String getProxiedEntitiesChain() { + String xProxiedEntitiesChain = request.getHeader("X-ProxiedEntitiesChain"); + final X509Certificate cert = extractClientCertificate(request); + if (cert != null) { + final String extractedPrincipal = extractPrincipal(cert); + final String formattedPrincipal = formatProxyDn(extractedPrincipal); + if (xProxiedEntitiesChain == null || xProxiedEntitiesChain.trim().isEmpty()) { + xProxiedEntitiesChain = formattedPrincipal; + } else { + xProxiedEntitiesChain += formattedPrincipal; + } + } + + return xProxiedEntitiesChain; + } + }; + } + + /** + * Utility methods that have been copied into this class to reduce the + * dependency footprint of this artifact. These utility methods typically + * live in web-utilities but that would pull in spring, jersey, jackson, + * etc. + */ + + private X509Certificate extractClientCertificate(HttpServletRequest request) { + X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); + + if (certs != null && certs.length > 0) { + return certs[0]; + } + + return null; + } + + private String extractPrincipal(X509Certificate cert) { + return cert.getSubjectDN().getName().trim(); + } + + private String formatProxyDn(String dn) { + return "<" + dn + ">"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/webapp/WEB-INF/web.xml ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/webapp/WEB-INF/web.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..a2e6a39 --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/webapp/WEB-INF/web.xml @@ -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-ui-extension</display-name> + <servlet> + <servlet-name>configure-component-controller</servlet-name> + <servlet-class>org.apache.nifi.ui.extension.ConfigureComponentController</servlet-class> + </servlet> + <servlet-mapping> + <servlet-name>configure-component-controller</servlet-name> + <url-pattern>/configure</url-pattern> + </servlet-mapping> +</web-app> http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/pom.xml ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/pom.xml new file mode 100644 index 0000000..22388b3 --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/pom.xml @@ -0,0 +1,21 @@ +<?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/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> + <artifactId>nifi-ui-extension</artifactId> + <packaging>jar</packaging> + <dependencies> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-user-actions</artifactId> + </dependency> + </dependencies> +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java new file mode 100644 index 0000000..9040896 --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.ui.extension; + +/** + * Information about a UI extension required to be invoked. + */ +public class UiExtension { + + private final UiExtensionType extensionType; + private final String contextPath; + private final ClassLoader classloader; + + public UiExtension(final UiExtensionType extensionType, final String contextPath, final ClassLoader classloader) { + this.extensionType = extensionType; + this.contextPath = contextPath; + this.classloader = classloader; + } + + public UiExtensionType getExtensionType() { + return extensionType; + } + + public String getContextPath() { + return contextPath; + } + + public ClassLoader getClassloader() { + return classloader; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java new file mode 100644 index 0000000..6431686 --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.ui.extension; + +import java.util.Map; + +/** + * Mapping of all discovered UI extensions. + */ +public class UiExtensionMapping { + + private final Map<String, UiExtension> uiExtensions; + + public UiExtensionMapping(Map<String, UiExtension> uiExtensions) { + this.uiExtensions = uiExtensions; + } + + public boolean hasUiExtension(final String type) { + return uiExtensions.containsKey(type); + } + + public UiExtension getUiExtension(final String type) { + return uiExtensions.get(type); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionType.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionType.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionType.java new file mode 100644 index 0000000..1beeea6 --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionType.java @@ -0,0 +1,26 @@ +/* + * 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.ui.extension; + +/** + * Types of UI extensions. + */ +public enum UiExtensionType { + ProcessorConfiguration, + ControllerServiceConfiguration, + ReportingTaskConfiguration +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerFacade.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerFacade.java new file mode 100644 index 0000000..cf53b7b --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerFacade.java @@ -0,0 +1,101 @@ +/* + * 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.controller; + +import java.util.Collection; +import org.apache.nifi.action.Action; + +import org.apache.nifi.controller.ControllerService; +import org.apache.nifi.web.ClusterRequestException; +import org.apache.nifi.web.ComponentConfiguration; +import org.apache.nifi.web.InvalidRevisionException; +import org.apache.nifi.web.ResourceNotFoundException; + +/** + * Provides limited access to the controller for UI extensions. + */ +public interface UiExtensionControllerFacade { + + /** + * Gets the ControllerService for the specified identifier. If a + * corresponding service cannot be found, null is returned. If this NiFi is + * clustered, the only services available will be those those + * availability is NCM only. + * + * @param serviceIdentifier + * @return + */ + ControllerService getControllerService(String serviceIdentifier); + + /** + * Provides a mechanism for custom UIs to save actions to appear in NiFi + * configuration history. Note all fields within each Action must be + * populated. Null values will result in a failure to insert the audit + * record. Since the saving to these actions is separate from the actual + * configuration change, a failure to insert here will just generate a + * warning log message. The recording of these actions typically happens + * after a configuration change is applied. Since those changes have already + * been applied to the flow, we cannot revert them because of a failure to + * insert an audit record. + * + * @param actions + */ + void saveActions(Collection<Action> actions); + + /** + * Gets the current user dn. Returns null if no user is found. + * + * @return + */ + String getCurrentUserDn(); + + /** + * Gets the current user name. Returns null if no user is found. + * + * @return + */ + String getCurrentUserName(); + + /** + * Sets the annotation data for the component in the specified request. + * + * @param configurationRequest + * @param annotationData + * @return the configuration for the component specified request + * @throws ResourceNotFoundException if the specified component does not exit + * @throws InvalidRevisionException if a revision other than the current + * revision is given + * @throws ClusterRequestException if the annotation data was unable to be + * set for the component. This exception will only be thrown when operating + * in a cluster. + */ + ComponentConfiguration setAnnotationData(UiExtensionControllerRequest configurationRequest, String annotationData) + throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException; + + /** + * Gets the configuration for the underlying component. + * + * @param configurationRequest + * @return the configuration for the component specified in request + * @throws ResourceNotFoundException if the processor does not exit + * @throws ClusterRequestException if the component was unable to be + * retrieved from the cluster. This exception will only be thrown when + * operating in a cluster. + */ + ComponentConfiguration getComponentDetails(UiExtensionControllerRequest configurationRequest) + throws ResourceNotFoundException, ClusterRequestException; +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerRequest.java ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerRequest.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerRequest.java new file mode 100644 index 0000000..f3d591f --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerRequest.java @@ -0,0 +1,64 @@ +/* + * 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.controller; + +import org.apache.nifi.ui.extension.UiExtensionType; +import org.apache.nifi.web.Revision; + +/** + * A UI extensions request object containing required items to querying the controller. + */ +public interface UiExtensionControllerRequest { + + /** + * The type of UI extension supports. + * + * @return + */ + UiExtensionType getExtensionType(); + + /** + * The request protocol scheme (http or https). When scheme is https, the + * X509Certificate can be used for subsequent remote requests. + * + * @return the protocol scheme + */ + String getScheme(); + + /** + * @return the component ID + */ + String getId(); + + /** + * @return the revision + */ + Revision getRevision(); + + /** + * Returns the proxied entities chain. The format of the chain is as + * follows: + * + * <code> + * <CN=original-proxied-entity><CN=first-proxy><CN=second-proxy>... + * </code> + * + * @return the proxied entities chain or null if no chain + */ + String getProxiedEntitiesChain(); + +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/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..221eac5 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 @@ -154,6 +154,11 @@ </dependency> <dependency> <groupId>org.apache.nifi</groupId> + <artifactId>nifi-ui-extension</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> <artifactId>nifi-administration</artifactId> <scope>provided</scope> </dependency> http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/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 b4f49ee..b6ddabf 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 @@ -66,6 +66,7 @@ import org.apache.nifi.web.util.ClientResponseUtils; * Implements the NiFiWebContext interface to support a context in both * standalone and clustered environments. */ +@Deprecated public class StandardNiFiWebContext implements NiFiWebContext { private static final Logger logger = LoggerFactory.getLogger(StandardNiFiWebContext.class); http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/StandardUiExtensionControllerFacade.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/StandardUiExtensionControllerFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/StandardUiExtensionControllerFacade.java new file mode 100644 index 0000000..1466535 --- /dev/null +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/StandardUiExtensionControllerFacade.java @@ -0,0 +1,377 @@ +/* + * 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.controller; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +import org.apache.nifi.action.Action; +import org.apache.nifi.admin.service.AuditService; +import org.apache.nifi.cluster.manager.NodeResponse; +import org.apache.nifi.cluster.manager.impl.WebClusterManager; +import org.apache.nifi.controller.ControllerService; +import org.apache.nifi.web.security.user.NiFiUserDetails; +import org.apache.nifi.web.security.user.NiFiUserUtils; +import org.apache.nifi.user.NiFiUser; +import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.api.dto.ProcessorConfigDTO; +import org.apache.nifi.web.api.dto.ProcessorDTO; +import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.entity.ProcessorEntity; +import org.apache.nifi.web.util.WebUtils; +import org.apache.nifi.ui.extension.UiExtensionType; + +import org.apache.commons.lang3.StringUtils; +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; + +import com.sun.jersey.core.util.MultivaluedMapImpl; +import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.web.ClusterRequestException; +import org.apache.nifi.web.ComponentConfiguration; +import org.apache.nifi.web.ConfigurationSnapshot; +import org.apache.nifi.web.InvalidRevisionException; +import org.apache.nifi.web.NiFiServiceFacade; +import org.apache.nifi.web.ResourceNotFoundException; +import org.apache.nifi.web.Revision; +import org.apache.nifi.web.util.ClientResponseUtils; + +/** + * Implements the UiExtensionControllerFacade interface to supply a simplified + * interface over a NiFi controller in both clustered and standalone environments. + */ +public class StandardUiExtensionControllerFacade implements UiExtensionControllerFacade { + + private static final Logger logger = LoggerFactory.getLogger(StandardUiExtensionControllerFacade.class); + + private static final String CLIENT_ID_PARAM = "clientId"; + private static final String VERBOSE_PARAM = "verbose"; + + private NiFiProperties properties; + private NiFiServiceFacade serviceFacade; + private WebClusterManager clusterManager; + private ControllerServiceLookup controllerServiceLookup; + private AuditService auditService; + + @Override + public ControllerService getControllerService(String serviceIdentifier) { + return controllerServiceLookup.getControllerService(serviceIdentifier); + } + + @Override + @PreAuthorize("hasAnyRole('ROLE_DFM')") + public void saveActions(final Collection<Action> actions) { + Objects.requireNonNull(actions, "Actions cannot be null."); + + // - when running standalone or cluster ncm - actions from custom UIs are stored locally + // - clustered nodes do not serve custom UIs directly to users so they should never be invoking this method + + try { + // record the operations + auditService.addActions(actions); + } catch (Throwable t) { + logger.warn("Unable to record actions: " + t.getMessage()); + if (logger.isDebugEnabled()) { + logger.warn(StringUtils.EMPTY, t); + } + } + } + + @Override + public String getCurrentUserDn() { + String userDn = NiFiUser.ANONYMOUS_USER_DN; + + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + if (user != null) { + userDn = user.getDn(); + } + + return userDn; + } + + @Override + public String getCurrentUserName() { + String userName = NiFiUser.ANONYMOUS_USER_DN; + + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + if (user != null) { + userName = user.getUserName(); + } + + return userName; + } + + @Override + public ComponentConfiguration getComponentDetails(UiExtensionControllerRequest config) { + final String id = config.getId(); + + if (StringUtils.isBlank(id)) { + throw new ResourceNotFoundException(String.format("Context config did not have a component ID.")); + } + + // ensure the path could be + if (config.getExtensionType() == null) { + throw new ResourceNotFoundException(String.format("The type must be one of ['%s']", StringUtils.join(UiExtensionType.values(), "', '"))); + } + + // get the component facade for interacting directly with that type of object + ComponentFacade componentFacade = null; + switch (config.getExtensionType()) { + case ProcessorConfiguration: + componentFacade = new ProcessorFacade(); + break; + case ControllerServiceConfiguration: + break; + case ReportingTaskConfiguration: + break; + } + + if (componentFacade == null) { + throw new ResourceNotFoundException(String.format("The type must be one of ['%s']", StringUtils.join(UiExtensionType.values(), "', '"))); + } + + return componentFacade.getComponentDetails(config); + } + + @Override + @PreAuthorize("hasAnyRole('ROLE_DFM')") + public ComponentConfiguration setAnnotationData(UiExtensionControllerRequest config, String annotationData) { + final String id = config.getId(); + + if (StringUtils.isBlank(id)) { + throw new ResourceNotFoundException(String.format("Context config did not have a component ID.")); + } + + // get the component facade for interacting directly with that type of object + ComponentFacade componentFacade = null; + switch (config.getExtensionType()) { + case ProcessorConfiguration: + componentFacade = new ProcessorFacade(); + break; + case ControllerServiceConfiguration: + break; + case ReportingTaskConfiguration: + break; + } + + if (componentFacade == null) { + throw new ResourceNotFoundException(String.format("The type must be one of ['%s']", StringUtils.join(UiExtensionType.values(), "', '"))); + } + + return componentFacade.setAnnotationData(config, annotationData); + } + + private interface ComponentFacade { + ComponentConfiguration getComponentDetails(UiExtensionControllerRequest config); + ComponentConfiguration setAnnotationData(UiExtensionControllerRequest config, String annotationData); + } + + private class ProcessorFacade implements ComponentFacade { + @Override + public ComponentConfiguration getComponentDetails(UiExtensionControllerRequest config) { + final Revision revision = config.getRevision(); + final String id = config.getId(); + + final ProcessorDTO processor; + if (properties.isClusterManager()) { + // create the request URL + URI requestUrl; + try { + String path = "/nifi-api/cluster/processors/" + URLEncoder.encode(id, "UTF-8"); + requestUrl = new URI(config.getScheme(), null, "localhost", 0, path, null, null); + } catch (final URISyntaxException | UnsupportedEncodingException use) { + throw new ClusterRequestException(use); + } + + // set the request parameters + MultivaluedMap<String, String> parameters = new MultivaluedMapImpl(); + parameters.add(CLIENT_ID_PARAM, revision.getClientId()); + parameters.add(VERBOSE_PARAM, "true"); + + // replicate request + NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, requestUrl, parameters, getHeaders(config)); + + // check for issues replicating request + checkResponse(nodeResponse, revision, id); + + // return processor + final ProcessorEntity entity = nodeResponse.getClientResponse().getEntity(ProcessorEntity.class); + processor = entity.getProcessor(); + } else { + processor = serviceFacade.getProcessor(id); + } + + // return the processor info + return getComponentConfiguration(processor); + } + + @Override + public ComponentConfiguration setAnnotationData(UiExtensionControllerRequest config, String annotationData) { + final Revision revision = config.getRevision(); + final String id = config.getId(); + + final ProcessorDTO processor; + if (properties.isClusterManager()) { + // create the request URL + URI requestUrl; + try { + String path = "/nifi-api/cluster/processors/" + URLEncoder.encode(id, "UTF-8"); + requestUrl = new URI(config.getScheme(), null, "localhost", 0, path, null, null); + } catch (final URISyntaxException | UnsupportedEncodingException use) { + throw new ClusterRequestException(use); + } + + // create the revision + RevisionDTO revisionDto = new RevisionDTO(); + revisionDto.setClientId(revision.getClientId()); + revisionDto.setVersion(revision.getVersion()); + + // create the processor entity + ProcessorEntity processorEntity = new ProcessorEntity(); + processorEntity.setRevision(revisionDto); + + // create the processor dto + ProcessorDTO processorDto = new ProcessorDTO(); + processorEntity.setProcessor(processorDto); + processorDto.setId(id); + + // create the processor configuration with the given annotation data + ProcessorConfigDTO configDto = new ProcessorConfigDTO(); + processorDto.setConfig(configDto); + configDto.setAnnotationData(annotationData); + + // set the content type to json + final Map<String, String> headers = getHeaders(config); + headers.put("Content-Type", "application/json"); + + // replicate request + NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.PUT, requestUrl, processorEntity, headers); + + // check for issues replicating request + checkResponse(nodeResponse, revision, id); + + // return processor + final ProcessorEntity entity = nodeResponse.getClientResponse().getEntity(ProcessorEntity.class); + processor = entity.getProcessor(); + } else { + final ConfigurationSnapshot<ProcessorDTO> response = serviceFacade.setProcessorAnnotationData(revision, id, annotationData); + processor = response.getConfiguration(); + } + + // return the processor info + return getComponentConfiguration(processor); + } + + private ComponentConfiguration getComponentConfiguration(final ProcessorDTO processor) { + final ProcessorConfigDTO processorConfig = processor.getConfig(); + return new ComponentConfiguration.Builder() + .id(processor.getId()) + .name(processor.getName()) + .state(processor.getState()) + .annotationData(processorConfig.getAnnotationData()) + .properties(processorConfig.getProperties()) + .validateErrors(processor.getValidationErrors()).build(); + } + } + + /** + * Gets the headers for the request to replicate to each node while + * clustered. + * + * @param config + * @return + */ + private Map<String, String> getHeaders(final UiExtensionControllerRequest config) { + final Map<String, String> headers = new HashMap<>(); + headers.put("Accept", "application/json,application/xml"); + if (StringUtils.isNotBlank(config.getProxiedEntitiesChain())) { + headers.put("X-ProxiedEntitiesChain", config.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); + } + } + return headers; + } + + /** + * Checks the specified response and drains the stream appropriately. + * + * @param nodeResponse + * @param revision + * @param id + */ + private void checkResponse(final NodeResponse nodeResponse, final Revision revision, final String id) { + if (nodeResponse.hasThrowable()) { + ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse()); + throw new ClusterRequestException(nodeResponse.getThrowable()); + } else if (nodeResponse.getClientResponse().getStatus() == Response.Status.CONFLICT.getStatusCode()) { + ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse()); + throw new InvalidRevisionException(String.format("Invalid revision specified %s", revision)); + } else if (nodeResponse.getClientResponse().getStatus() == Response.Status.NOT_FOUND.getStatusCode()) { + ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse()); + throw new ResourceNotFoundException("Unable to find component with id: " + id); + } else if (nodeResponse.getClientResponse().getStatus() != Response.Status.OK.getStatusCode()) { + ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse()); + throw new ClusterRequestException("Method resulted in an unsuccessful HTTP response code: " + nodeResponse.getClientResponse().getStatus()); + } + } + + public void setClusterManager(WebClusterManager clusterManager) { + this.clusterManager = clusterManager; + } + + public void setProperties(NiFiProperties properties) { + this.properties = properties; + } + + public void setServiceFacade(NiFiServiceFacade serviceFacade) { + this.serviceFacade = serviceFacade; + } + + public void setAuditService(AuditService auditService) { + this.auditService = auditService; + } + + public void setControllerServiceLookup(ControllerServiceLookup controllerServiceLookup) { + this.controllerServiceLookup = controllerServiceLookup; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/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 940eb62..d4ab6b7 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 @@ -124,6 +124,13 @@ <property name="auditService" ref="auditService"/> <property name="controllerServiceLookup" ref="controllerServiceProvider"/> </bean> + <bean id="nifiUiExtensionControllerFacade" class="org.apache.nifi.web.controller.StandardUiExtensionControllerFacade"> + <property name="serviceFacade" ref="serviceFacade"/> + <property name="properties" ref="nifiProperties"/> + <property name="clusterManager" ref="clusterManager"/> + <property name="auditService" ref="auditService"/> + <property name="controllerServiceLookup" ref="controllerServiceProvider"/> + </bean> <!-- rest endpoints --> <bean id="controllerResource" class="org.apache.nifi.web.api.ControllerResource" scope="singleton"> http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml ---------------------------------------------------------------------- diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml index 11c3c0a..7215672 100644 --- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml +++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml @@ -12,8 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> +--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.apache.nifi</groupId> @@ -31,6 +30,8 @@ <module>nifi-web-docs</module> <module>nifi-web-ui</module> <module>nifi-jetty</module> + <module>nifi-ui-extension-controller</module> + <module>nifi-ui-extension</module> </modules> <dependencyManagement> <dependencies> @@ -54,10 +55,16 @@ </dependency> <dependency> <groupId>org.apache.nifi</groupId> + <artifactId>nifi-ui-extension-controller</artifactId> + <type>war</type> + <version>0.1.0-incubating-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> <artifactId>nifi-web-ui</artifactId> <type>war</type> <version>0.1.0-incubating-SNAPSHOT</version> </dependency> </dependencies> </dependencyManagement> -</project> +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6c058778/nifi/pom.xml ---------------------------------------------------------------------- diff --git a/nifi/pom.xml b/nifi/pom.xml index e7d8f5b..2e2346a 100644 --- a/nifi/pom.xml +++ b/nifi/pom.xml @@ -676,6 +676,11 @@ </dependency> <dependency> <groupId>org.apache.nifi</groupId> + <artifactId>nifi-ui-extension</artifactId> + <version>0.1.0-incubating-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> <artifactId>nifi-flowfile-packager</artifactId> <version>0.1.0-incubating-SNAPSHOT</version> </dependency>
