Skip to content

Added hazelcast integration#381

Open
stijnpotters1 wants to merge 4 commits intomasterfrom
feat/hazelcast-integration
Open

Added hazelcast integration#381
stijnpotters1 wants to merge 4 commits intomasterfrom
feat/hazelcast-integration

Conversation

@stijnpotters1
Copy link
Contributor

@stijnpotters1 stijnpotters1 commented Mar 24, 2026

It works both for OutboundGateway and LocalGateway
image
image

Copilot AI review requested due to automatic review settings March 24, 2026 12:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an (optional) Hazelcast-based discovery path to list remote Frank instances/configurations and expose them to the frontend, primarily for local development workflows.

Changes:

  • Introduces a hazelcast.enabled feature flag (default off; enabled in application-local.properties) and publishes config metadata.
  • Adds backend Hazelcast integration (service/controller + local “configurations directory” responder + Hazelcast-related Spring config).
  • Adds frontend polling + UI section to discover and connect to remote instances.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/main/resources/application.properties Adds default hazelcast.enabled=false.
src/main/resources/application-local.properties Enables Hazelcast for local profile and adds configurations.directory hint.
src/main/resources/META-INF/additional-spring-configuration-metadata.json Documents hazelcast.enabled for Spring metadata tooling.
src/main/java/org/frankframework/flow/security/UserWorkspaceContext.java Adjusts request scope proxying for UserWorkspaceContext.
src/main/java/org/frankframework/flow/hazelcast/HazelcastService.java Discovers cluster members and fetches configurations via management bus.
src/main/java/org/frankframework/flow/hazelcast/HazelcastController.java Exposes GET /api/hazelcast/instances.
src/main/java/org/frankframework/flow/hazelcast/HazelcastConfigurationDTO.java DTO for configuration discovery payloads.
src/main/java/org/frankframework/flow/hazelcast/FrankInstanceDTO.java DTO for remote instance listing returned to the UI.
src/main/java/org/frankframework/flow/hazelcast/ConfigurationsDirectory.java Responds to bus “configuration find” requests using a local directory listing.
src/main/java/org/frankframework/flow/common/config/WebConfiguration.java Tweaks shared ObjectMapper and adds an InputStreamHttpMessageConverter bean.
src/main/java/org/frankframework/flow/common/config/SecurityChainConfigurer.java Adds a Spring Security filter chain (anonymous-only authorization as written).
src/main/java/org/frankframework/flow/common/config/HazelcastConfig.java Wires gateway factory + management bus channel under hazelcast.enabled.
src/main/java/org/frankframework/flow/common/config/ConfigurationsConfig.java Session-scoped configuration list fetched via outbound bus call.
src/main/java/org/frankframework/flow/FlowWarInitializer.java Adds a WAR initializer entrypoint class.
src/main/java/org/frankframework/flow/FlowApplication.java Updates app bootstrap and adds SpringBootServletInitializer + config properties scan.
src/main/frontend/app/services/hazelcast-service.ts Adds frontend API wrapper for instance discovery.
src/main/frontend/app/routes/projectlanding/project-landing.tsx Polls discovery endpoint and renders “Remote” instances section.
pom.xml Adds management-gateway dependency and additional Spring framework dependency/version property.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@stijnpotters1 stijnpotters1 self-assigned this Mar 24, 2026
@stijnpotters1 stijnpotters1 marked this pull request as ready for review March 24, 2026 13:52
Copilot AI review requested due to automatic review settings March 24, 2026 13:52
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarQube Cloud

@stijnpotters1 stijnpotters1 linked an issue Mar 24, 2026 that may be closed by this pull request
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +100 to +104
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-framework.version}</version>
</dependency>
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

This declares an explicit spring-beans dependency with a hard-coded Spring Framework version even though the project already uses the Spring Boot parent BOM. This is easy to forget to keep in sync with the rest of Spring artifacts and can cause version skew at runtime. Prefer relying on Spring Boot’s dependency management (remove the explicit spring-beans dependency/version), or override Spring Framework via the Boot-managed property only (without adding a redundant direct dependency).

Suggested change
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-framework.version}</version>
</dependency>

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +31
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

/**
* Spring Boot entrypoint when running as a WAR application deployed to an external servlet container.
* For standalone JAR execution, see {@link FlowApplication}.
*/
public class FlowWarInitializer extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(FlowApplication.class);
}
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

FlowApplication already extends SpringBootServletInitializer, and this class adds a second WebApplicationInitializer for WAR deployments. In a servlet container, having two SpringBootServletInitializer subclasses can trigger multiple application context initializations. Keep only one initializer (either remove this class, or stop extending SpringBootServletInitializer in FlowApplication).

Suggested change
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* Spring Boot entrypoint when running as a WAR application deployed to an external servlet container.
* For standalone JAR execution, see {@link FlowApplication}.
*/
public class FlowWarInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(FlowApplication.class);
}
}
/**
* Spring Boot entrypoint when running as a WAR application deployed to an external servlet container.
* For standalone JAR execution, see {@link FlowApplication}.
* <p>
* This class is retained only for backward compatibility. Actual initialization
* is handled by {@link FlowApplication}, which extends SpringBootServletInitializer.
*/
public class FlowWarInitializer {
// No-op: initialization is performed by FlowApplication.
}

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +158
return index == 0
? path.getRoot().toString()
: path.getRoot().resolve(path.subpath(0, index)).toString();
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

buildPathUpTo assumes path.getRoot() is non-null. For relative paths (or some non-default Path implementations), getRoot() can be null and this will throw a NullPointerException. Consider handling a null root by returning path.subpath(0, index).toString() (and for index==0, return an empty path or the original input) so relative paths are supported safely.

Suggested change
return index == 0
? path.getRoot().toString()
: path.getRoot().resolve(path.subpath(0, index)).toString();
Path root = path.getRoot();
if (root == null) {
// Relative path: safely build prefix without assuming a root
return index == 0
? ""
: path.subpath(0, index).toString();
}
// Absolute path: preserve existing behavior
return index == 0
? root.toString()
: root.resolve(path.subpath(0, index)).toString();

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +63
try {
List<HazelcastConfigurationDTO> configurations = fetchConfigurations(gateway, member);
result.add(toFrankInstance(member.getName(), member.getId().toString(), configurations));
} catch (Exception e) {
log.warn("Member [{}] did not respond ({}), skipping", member.getName(), e.getMessage());
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

collectWorkerInstances adds a FrankInstanceDTO even when the configuration list is empty (e.g., member returns no configs or parsing fails). This can surface “live” instances in the UI that can never be opened (projectPath ends up null). Consider skipping members with empty configurations (or at least only adding the instance when a non-blank directory/projectPath can be derived).

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +94
useEffect(() => {
if (!isLocalEnvironment) return
const controller = new AbortController()

const discover = () => {
discoverFrankInstances(controller.signal)
.then(setFrankInstances)
.catch(() => {
// Discovery failure is non-critical; Hazelcast may not be running
})
.finally(() => setIsDiscovering(false))
}

setIsDiscovering(true)
discover()
const interval = setInterval(discover, 3000)

return () => {
controller.abort()
clearInterval(interval)
}
}, [isLocalEnvironment])
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

The polling loop can start a new discoverFrankInstances request every 3s even if the previous request hasn’t completed yet, which can lead to overlapping in-flight requests and unnecessary load. Consider guarding against concurrent runs (e.g., track an in-flight flag) or aborting the previous request before starting a new one, and ensure state updates are skipped after cleanup to avoid setting state on an unmounted component.

Copilot uses AI. Check for mistakes.
@Matthbo
Copy link
Member

Matthbo commented Mar 24, 2026

I'll look at this later this week as this is quite important to be done correctly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Manieren van opstarten (Hazelcast)

3 participants