Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ private String getProposedUri(final FlowRegistryClientConfigurationContext conte
final URI fullUri = URI.create(configuredUrl);
final int port = fullUri.getPort();
final String portSuffix = port < 0 ? "" : ":" + port;
final String uriString = fullUri.getScheme() + "://" + fullUri.getHost() + portSuffix;
final String path = fullUri.getPath();
final String pathSuffix = path == null ? "" : path;
final String uriString = fullUri.getScheme() + "://" + fullUri.getHost() + portSuffix + pathSuffix;
uri = URI.create(uriString);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("The given Registry URL is not valid: " + configuredUrl);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.registry.flow;

import org.apache.nifi.components.PropertyValue;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Method;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class TestNifiRegistryFlowRegistryClient {

@Test
public void testGetProposedUri() throws Exception {
final NifiRegistryFlowRegistryClient client = new NifiRegistryFlowRegistryClient();
final FlowRegistryClientConfigurationContext context = mock(FlowRegistryClientConfigurationContext.class);
final PropertyValue propertyValue = mock(PropertyValue.class);

// Test with root context
when(context.getProperty(NifiRegistryFlowRegistryClient.PROPERTY_URL)).thenReturn(propertyValue);
when(propertyValue.evaluateAttributeExpressions()).thenReturn(propertyValue);
when(propertyValue.getValue()).thenReturn("https://localhost:18443");

// Access private method via reflection
Method getProposedUri = NifiRegistryFlowRegistryClient.class.getDeclaredMethod("getProposedUri", FlowRegistryClientConfigurationContext.class);
getProposedUri.setAccessible(true);
String uri = (String) getProposedUri.invoke(client, context);

assertEquals("https://localhost:18443", uri);

// Test with custom context path
when(propertyValue.getValue()).thenReturn("https://localhost:18443/my-registry");
uri = (String) getProposedUri.invoke(client, context);

// Before fix, this returns "https://localhost:18443" (stripped path)
// We assert expected behavior (preserving path) to confirm failure
assertEquals("https://localhost:18443/my-registry", uri);

// Test with trailing slash
when(propertyValue.getValue()).thenReturn("https://localhost:18443/my-registry/");
uri = (String) getProposedUri.invoke(client, context);
// URI.create(s).toString() normalizes? No, URI toString returns as is usually,
// but the method constructs a new URI.
// The current implementation reconstructs URI from scheme, host, port.
// My proposed fix will append path.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-utils</artifactId>
<version>2.4.0</version>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is unrelated and should be reverted.

</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-kubernetes-client</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.dsl.Resource;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.components.AbstractConfigurableComponent;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.PropertyValue;
Expand Down Expand Up @@ -54,6 +56,10 @@
/**
* State Provider implementation based on Kubernetes ConfigMaps with Base64 encoded keys to meet Kubernetes constraints
*/
@Tags({"kubernetes", "state", "provider", "configmap", "cluster"})
@CapabilityDescription("Provides a State Provider that stores state in a Kubernetes ConfigMap. "
+ "This allows state to be shared across a Kubernetes cluster. "
+ "The State Provider uses the Kubernetes Client to read and write ConfigMaps.")
Comment on lines +59 to +62

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is unrelated and should be reverted

public class KubernetesConfigMapStateProvider extends AbstractConfigurableComponent implements StateProvider {
static final PropertyDescriptor CONFIG_MAP_NAME_PREFIX = new PropertyDescriptor.Builder()
.name("ConfigMap Name Prefix")
Expand Down
1 change: 1 addition & 0 deletions nifi-registry/nifi-registry-assembly/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
<nifi.registry.web.https.port />
<nifi.registry.web.https.network.interface.default />
<nifi.registry.web.https.application.protocols>h2 http/1.1</nifi.registry.web.https.application.protocols>
<nifi.registry.web.context.path />
<nifi.registry.jetty.work.dir>./work/jetty</nifi.registry.jetty.work.dir>
<nifi.registry.web.jetty.threads>200</nifi.registry.web.jetty.threads>
<nifi.registry.web.should.send.server.version>true</nifi.registry.web.should.send.server.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ public void stop() {
}

protected List<URI> getApplicationUrls() {
// Get the context path prefix and build the full application path
final String contextPathPrefix = properties.getWebContextPath();
final String applicationPath = contextPathPrefix + APPLICATION_PATH;

return Arrays.stream(server.getConnectors())
.map(connector -> (ServerConnector) connector)
.map(serverConnector -> {
Expand All @@ -164,7 +168,7 @@ protected List<URI> getApplicationUrls() {
final String connectorHost = serverConnector.getHost();
final String host = StringUtils.defaultIfEmpty(connectorHost, HOST_UNSPECIFIED);
try {
return new URI(scheme, null, host, port, APPLICATION_PATH, null, null);
return new URI(scheme, null, host, port, applicationPath, null, null);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,27 @@ public Handler getHandler(final NiFiRegistryProperties properties) {
final File libDirectory = properties.getWarLibDirectory();
final File workDirectory = properties.getWebWorkingDirectory();

// Get the context path prefix (e.g., "/integration-hub") and normalize it
final String contextPathPrefix = properties.getWebContextPath();
final String uiContextPath = contextPathPrefix + UI_CONTEXT_PATH;
final String apiContextPath = contextPathPrefix + API_CONTEXT_PATH;
final String docsContextPath = contextPathPrefix + DOCS_CONTEXT_PATH;

final Handler.Collection handlers = new ContextHandlerCollection();
// Add Header Writer Handler before others
handlers.addHandler(new HeaderWriterHandler());

final WebAppContext userInterfaceContext = getWebAppContext(libDirectory, workDirectory, ClassLoader.getSystemClassLoader(), UI_FILE_PATTERN, UI_CONTEXT_PATH);
final WebAppContext userInterfaceContext = getWebAppContext(libDirectory, workDirectory, ClassLoader.getSystemClassLoader(), UI_FILE_PATTERN, uiContextPath);
userInterfaceContext.setInitParameter(OIDC_SUPPORTED_PARAMETER, Boolean.toString(properties.isOidcEnabled()));
handlers.addHandler(userInterfaceContext);

final ClassLoader apiClassLoader = getApiClassLoader(properties.getDatabaseDriverDirectory());
final WebAppContext apiContext = getWebAppContext(libDirectory, workDirectory, apiClassLoader, API_FILE_PATTERN, API_CONTEXT_PATH);
final WebAppContext apiContext = getWebAppContext(libDirectory, workDirectory, apiClassLoader, API_FILE_PATTERN, apiContextPath);
apiContext.setAttribute(PROPERTIES_PARAMETER, properties);
apiContext.setThrowUnavailableOnStartupException(true);
handlers.addHandler(apiContext);

final WebAppContext docsContext = getWebAppContext(libDirectory, workDirectory, ClassLoader.getSystemClassLoader(), DOCS_FILE_PATTERN, DOCS_CONTEXT_PATH);
final WebAppContext docsContext = getWebAppContext(libDirectory, workDirectory, ClassLoader.getSystemClassLoader(), DOCS_FILE_PATTERN, docsContextPath);
final Path docsDir = getDocsDirectory();
final ServletHolder docsServletHolder = getDocsServletHolder(docsDir);
docsContext.addServlet(docsServletHolder, HTML_DOCS_PATH);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public class NiFiRegistryProperties extends ApplicationProperties {

public static final String WEB_WORKING_DIR = "nifi.registry.web.jetty.working.directory";
public static final String WEB_THREADS = "nifi.registry.web.jetty.threads";
public static final String WEB_CONTEXT_PATH = "nifi.registry.web.context.path";
public static final String WEB_SHOULD_SEND_SERVER_VERSION = "nifi.registry.web.should.send.server.version";

public static final String SECURITY_KEYSTORE = "nifi.registry.security.keystore";
Expand Down Expand Up @@ -514,4 +515,30 @@ public Set<String> getHttpsNetworkInterfaceNames() {
}
return networkInterfaceNames;
}

/**
* Returns the web context path prefix. The path will be normalized to ensure it starts with a leading slash
* and does not end with a trailing slash. Returns an empty string if not configured.
*
* @return the normalized context path prefix (e.g., "/integration-hub")
*/
public String getWebContextPath() {
final String contextPath = getProperty(WEB_CONTEXT_PATH);
if (StringUtils.isBlank(contextPath)) {
return "";
}
return normalizeContextPath(contextPath.trim());
}

private String normalizeContextPath(String cp) {
if (cp == null || cp.equalsIgnoreCase("")) {
return "";
} else {
String trimmedCP = cp.trim();
// Ensure it starts with a leading slash and does not end in a trailing slash
trimmedCP = trimmedCP.startsWith("/") ? trimmedCP : "/" + trimmedCP;
trimmedCP = trimmedCP.endsWith("/") ? trimmedCP.substring(0, trimmedCP.length() - 1) : trimmedCP;
return trimmedCP;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.registry.properties;

import org.junit.jupiter.api.Test;

import java.util.HashMap;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

public class TestNiFiRegistryPropertiesContextPath {

@Test
public void testGetWebContextPathNotSet() {
final NiFiRegistryProperties properties = new NiFiRegistryProperties();
final String contextPath = properties.getWebContextPath();
assertNotNull(contextPath);
assertEquals("", contextPath);
}

@Test
public void testGetWebContextPathWithLeadingSlash() {
final Map<String, String> props = new HashMap<>();
props.put(NiFiRegistryProperties.WEB_CONTEXT_PATH, "/integration-hub");
final NiFiRegistryProperties properties = new NiFiRegistryProperties(props);
final String contextPath = properties.getWebContextPath();
assertEquals("/integration-hub", contextPath);
}

@Test
public void testGetWebContextPathWithoutLeadingSlash() {
final Map<String, String> props = new HashMap<>();
props.put(NiFiRegistryProperties.WEB_CONTEXT_PATH, "integration-hub");
final NiFiRegistryProperties properties = new NiFiRegistryProperties(props);
final String contextPath = properties.getWebContextPath();
assertEquals("/integration-hub", contextPath);
}

@Test
public void testGetWebContextPathWithTrailingSlash() {
final Map<String, String> props = new HashMap<>();
props.put(NiFiRegistryProperties.WEB_CONTEXT_PATH, "/integration-hub/");
final NiFiRegistryProperties properties = new NiFiRegistryProperties(props);
final String contextPath = properties.getWebContextPath();
assertEquals("/integration-hub", contextPath);
}

@Test
public void testGetWebContextPathWithBothSlashes() {
final Map<String, String> props = new HashMap<>();
props.put(NiFiRegistryProperties.WEB_CONTEXT_PATH, "integration-hub/");
final NiFiRegistryProperties properties = new NiFiRegistryProperties(props);
final String contextPath = properties.getWebContextPath();
assertEquals("/integration-hub", contextPath);
}

@Test
public void testGetWebContextPathWithWhitespace() {
final Map<String, String> props = new HashMap<>();
props.put(NiFiRegistryProperties.WEB_CONTEXT_PATH, " /integration-hub ");
final NiFiRegistryProperties properties = new NiFiRegistryProperties(props);
final String contextPath = properties.getWebContextPath();
assertEquals("/integration-hub", contextPath);
}

@Test
public void testGetWebContextPathEmptyString() {
final Map<String, String> props = new HashMap<>();
props.put(NiFiRegistryProperties.WEB_CONTEXT_PATH, "");
final NiFiRegistryProperties properties = new NiFiRegistryProperties(props);
final String contextPath = properties.getWebContextPath();
assertEquals("", contextPath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ nifi.registry.web.https.host=${nifi.registry.web.https.host}
nifi.registry.web.https.port=${nifi.registry.web.https.port}
nifi.registry.web.https.network.interface.default=${nifi.registry.web.https.network.interface.default}
nifi.registry.web.https.application.protocols=${nifi.registry.web.https.application.protocols}
nifi.registry.web.context.path=${nifi.registry.web.context.path}
nifi.registry.web.jetty.working.directory=${nifi.registry.jetty.work.dir}
nifi.registry.web.jetty.threads=${nifi.registry.web.jetty.threads}
nifi.registry.web.should.send.server.version=${nifi.registry.web.should.send.server.version}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="shortcut icon" href="/nifi-registry-docs/images/registry-favicon.png"/>
<link rel="shortcut icon" href="${pageContext.request.contextPath}/images/registry-favicon.png"/>
<title>NiFi Registry Documentation</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/application.js"></script>
Expand Down
8 changes: 8 additions & 0 deletions readme
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Build the NiFi Source First, you need to compile the source code you just patched so that it generates those .zip files. From the root (C:\MyData\Test\nifi-2.4.0-source-release\original-repo\nifi), run:

Run - mvn clean install -DskipTests
(Note: This might take ~10-15 minutes since NiFi is huge. It will create nifi-assembly/target/nifi-2.4.0-bin.zip and nifi-toolkit/nifi-toolkit-assembly/target/nifi-toolkit-2.4.0-bin.zip)

Move the Zips to your Docker folder Copy those two generated .zip files into your nifi-docker/dockerhub/ folder so Docker can see them.

docker build -f Dockerfile-local -t nifi:2.4.0-custom .
Loading