Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3dd7005
feat: initial working hazelcast integration. localgateway working, ou…
stijnpotters1 Mar 19, 2026
25a68a7
fix: improved code to prefinal version
stijnpotters1 Mar 19, 2026
5dffdfe
fix: improved hazelcast connection logic
stijnpotters1 Mar 24, 2026
56d07dc
fix: improved LocalGateway logic, added tests and improved code more
stijnpotters1 Mar 24, 2026
e2b4642
Replace hazelcast implementation with Console-like one
Matthbo Apr 10, 2026
ac43c07
Update security
Matthbo Apr 13, 2026
56c0bb0
Make flow and framework detect each other
Matthbo Apr 14, 2026
3cdcd13
Attempt to send message to framework
Matthbo Apr 15, 2026
f905469
Move UserContext logic to ClientSession
Matthbo Apr 16, 2026
b9e5583
Tests and losing my mind
Matthbo Apr 16, 2026
d53d27a
Fix gateway communication and show in frontend
Matthbo Apr 17, 2026
4760c56
Fix issues
Matthbo Apr 22, 2026
d3e5fed
copilot feedback & test fixes
Matthbo Apr 22, 2026
9150f6f
Spotless
Matthbo Apr 22, 2026
dbb6b20
More test fixes
Matthbo Apr 22, 2026
389934e
Use both central & ff nexus repositories
Matthbo Apr 22, 2026
834709b
chore: update Java version to 25 in CI configurations
philipsens Apr 22, 2026
9dd5e9f
Update dependencies
Matthbo Apr 24, 2026
de088bb
Merge branch 'master' into feat/hazelcast-integration
Matthbo Apr 24, 2026
5e81b38
Also update frontend packages
Matthbo Apr 24, 2026
70f1454
Fix linting dependency
Matthbo Apr 24, 2026
c0d5f6a
Move requestmapping value to class for ClusterMembers
Matthbo May 1, 2026
c61bb65
Oops
Matthbo May 1, 2026
451616f
Merge remote-tracking branch 'origin/master' into feat/hazelcast-inte…
Matthbo May 4, 2026
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
13 changes: 13 additions & 0 deletions .run/Flow Hazelcast Cluster.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Flow Hazelcast Cluster" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker">
<deployment type="docker-compose.yml">
<settings>
<option name="removeImagesOnComposeDown" value="LOCAL" />
<option name="sourceFilePath" value="flow-framework-compose.yml" />
</settings>
</deployment>
<method v="2">
<option name="Maven.BeforeRunTask" enabled="true" file="$PROJECT_DIR$/pom.xml" goal="package" />
</method>
</configuration>
</component>
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM eclipse-temurin:21-jre-jammy AS runner
FROM eclipse-temurin:25-jre-jammy AS runner
ARG GID=2000
ARG UID=2000
WORKDIR /app
Expand Down
32 changes: 32 additions & 0 deletions docker/resources/Configuration.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Configuration>
<adapter name="HelloWorld">
<receiver name="HelloWorld">
<!-- This listener is used when the API is called as specified in OpenAPI 3.0 at the Webservices page -->
<listener
name="HelloWorld"
className="org.frankframework.http.rest.ApiListener"
uriPattern="hello-world"
allowAllParams="false"
/>
</receiver>
<receiver name="HelloWorld">
<!-- This listener is used by the scheduler and IbisLocalSender in adapter HelloWorlds -->
<listener
name="HelloWorld"
className="org.frankframework.receivers.JavaListener"
/>
</receiver>
<pipeline firstPipe="HelloWorld">
<exits>
<exit name="EXIT" state="success"/>
</exits>
<pipe
name="HelloWorld"
className="org.frankframework.pipes.EchoPipe"
getInputFromFixedValue="Hello World"
>
<forward name="success" path="EXIT"/>
</pipe>
</pipeline>
</adapter>
</Configuration>
4 changes: 4 additions & 0 deletions docker/resources/resources.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
jdbc:
- name: "frank2flowinstance"
type: "org.h2.jdbcx.JdbcDataSource"
url: "jdbc:h2:mem:test;NON_KEYWORDS=VALUE;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;TRACE_LEVEL_FILE=0;"
24 changes: 24 additions & 0 deletions flow-framework-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
services:
flow:
image: ghcr.io/frankframework/flow:latest
build:
dockerfile: ./docker/Dockerfile
context: .
environment:
SPRING_PROFILES_ACTIVE: cloud
management.gateway.outbound.class: org.frankframework.management.gateway.HazelcastOutboundGateway
ports:
Comment thread
Matthbo marked this conversation as resolved.
- "8080:8080"
- "5700-5709:5700-5709"

ff-test:
image: frankframework/frankframework:latest
ports:
- "8081:8080"
- "5710-5720:5700-5710"
volumes:
- ./docker/resources:/opt/frank/resources
environment:
- instance.name=Frank2FlowInstance
- dtap.stage=LOC
- management.gateway.inbound.class=org.frankframework.management.gateway.HazelcastInboundGateway,org.frankframework.management.bus.LocalGateway
34 changes: 30 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.2</version>
<version>4.0.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

Expand Down Expand Up @@ -60,7 +60,9 @@

<properties>
<revision>3.0.0-SNAPSHOT</revision>
<java.version>21</java.version>
<java.version>25</java.version>
<spring-framework.version>7.0.6</spring-framework.version>
<frankframework.version>10.1.0-SNAPSHOT</frankframework.version>
<jackson.version>2.20.1</jackson.version>
<lombok.version>1.18.42</lombok.version>
<spotless.version>3.0.0</spotless.version>
Expand All @@ -69,6 +71,9 @@
<jacoco.version>0.8.14</jacoco.version>
<apache.commons.version>1.28.0</apache.commons.version>

<!-- must match version with framework, which maven won't always do without explicit version -->
<hazelcast.version>5.6.0</hazelcast.version>

<sonar.organization>frank-framework</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.coverage.jacoco.xmlReportPaths>
Expand All @@ -79,8 +84,8 @@
<junit.includedTags/>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -177,6 +182,23 @@
<artifactId>Saxon-HE</artifactId>
<version>12.9</version>
</dependency>
<dependency>
<groupId>org.frankframework</groupId>
<artifactId>frankframework-management-gateway</artifactId>
<version>${frankframework.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.frankframework</groupId>
<artifactId>frankframework-security</artifactId>
<version>${frankframework.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>${hazelcast.version}</version>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -222,6 +244,10 @@
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
Expand Down
88 changes: 73 additions & 15 deletions src/main/frontend/app/routes/projectlanding/project-landing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState, useCallback, useRef } from 'react'
import { useNavigate } from 'react-router'
import FfIcon from '/icons/custom/ff!-icon.svg?react'
import ArchiveIcon from '/icons/solar/Archive.svg?react'
import { fetchInstanceConfigurations, type FFConfiguration } from '~/services/frank-framework-service'
import { useProjectStore } from '~/stores/project-store'

import ProjectRow from './project-row'
Expand Down Expand Up @@ -40,6 +41,9 @@ export default function ProjectLanding() {
const [isLocalEnvironment, setIsLocalEnvironment] = useState(true)
const [rootLocationName, setRootLocationName] = useState('Computer')
const [isOpeningProject, setIsOpeningProject] = useState(false)
const [isDiscovering, setIsDiscovering] = useState(false)
const [ffConfiguration, setFFConfiguration] = useState<FFConfiguration[]>([])
const [ffInstanceName, setFFInstanceName] = useState('')
const importInputRef = useRef<HTMLInputElement>(null)

useEffect(() => {
Expand Down Expand Up @@ -67,6 +71,27 @@ export default function ProjectLanding() {
}
}, [apiError])

useEffect(() => {
if (!isLocalEnvironment) return

const discover = () => {
setIsDiscovering(true)
fetchInstanceConfigurations()
.then((ffInstance) => {
setFFInstanceName(ffInstance.name)
setFFConfiguration(ffInstance.configurations)
})
.finally(() => setIsDiscovering(false))
}

discover()
const interval = setInterval(discover, 60_000)

return () => {
clearInterval(interval)
}
}, [isLocalEnvironment])

const handleOpenProject = useCallback(
async (rootPath: string) => {
setIsOpeningProject(true)
Expand Down Expand Up @@ -166,7 +191,7 @@ export default function ProjectLanding() {
<div className="bg-backdrop flex min-h-screen w-full flex-col items-center pt-20">
<Header />

<main className="border-border bg-background flex min-h-[400px] w-2/5 flex-col rounded border shadow">
<main className="border-border bg-background flex min-h-100 w-2/5 flex-col rounded border shadow">
<Toolbar onSearchChange={setSearchTerm} />

<div className="flex flex-1 overflow-hidden">
Expand All @@ -183,6 +208,9 @@ export default function ProjectLanding() {
onProjectClick={handleOpenProject}
onRemoveProject={onRemoveProject}
onExportProject={onExportProject}
frameworkInstanceName={ffInstanceName}
frameworkConfigurations={ffConfiguration}
isDiscovering={isDiscovering}
/>
</div>

Expand Down Expand Up @@ -250,7 +278,7 @@ const Sidebar = ({
onCloneClick: () => void
onImportClick: () => void
}) => (
<nav className="border-border flex w-1/4 min-w-[200px] flex-col gap-3 border-r p-4">
<nav className="border-border flex w-1/4 min-w-50 flex-col gap-3 border-r p-4">
<ActionButton label={isLocal ? 'Open Local Folder' : 'Open Workspace Project'} onClick={onOpenClick} />
<ActionButton label="Clone Repository" onClick={onCloneClick} />
<ActionButton label="New Project" onClick={onNewClick} />
Expand All @@ -264,34 +292,64 @@ const ProjectList = ({
onProjectClick,
onRemoveProject,
onExportProject,
frameworkInstanceName,
frameworkConfigurations,
isDiscovering,
}: {
projects: RecentProject[]
isLocal: boolean
onProjectClick: (rootPath: string) => void
onRemoveProject: (rootPath: string) => void
onExportProject: (projectName: string) => void
frameworkInstanceName: string
frameworkConfigurations: FFConfiguration[]
isDiscovering: boolean
}) => (
<section className="h-full flex-1 overflow-y-auto p-4">
{projects.length === 0 ? (
{frameworkConfigurations.length > 0 && (
<div className="mb-4">
<p className="mb-2 text-xs font-semibold tracking-wider text-slate-500 uppercase">Remote</p>
{frameworkConfigurations.map((configuration) => (
<div
key={configuration.name}
className="hover:bg-backdrop mb-2 flex w-full cursor-pointer items-center justify-between rounded px-3 py-2"
>
<div className="flex flex-col">
<div className="font-medium">{configuration.name}</div>
{configuration.filename && <p className="text-foreground-muted text-xs">{configuration.filename}</p>}
</div>
<span className="rounded bg-green-100 px-2 py-0.5 text-xs text-green-700">{frameworkInstanceName}</span>
</div>
))}
</div>
)}
{isDiscovering && frameworkConfigurations.length === 0 && (
<p className="mb-2 text-xs text-slate-400 italic">Scanning for remote instances...</p>
)}
{projects.length === 0 && frameworkConfigurations.length === 0 && !isDiscovering && (
<p className="text-muted-foreground mt-10 text-center text-sm italic">No projects found</p>
) : (
projects.map((project) => (
<ProjectRow
key={project.rootPath}
project={project}
isLocal={isLocal}
onClick={() => onProjectClick(project.rootPath)}
onRemove={() => onRemoveProject(project.rootPath)}
onExport={() => onExportProject(project.name)}
/>
))
)}
{projects.length > 0 && (
<>
<p className="mb-2 text-xs font-semibold tracking-wider text-slate-400 uppercase">Recent</p>
{projects.map((project) => (
<ProjectRow
key={project.rootPath}
project={project}
isLocal={isLocal}
onClick={() => onProjectClick(project.rootPath)}
onRemove={() => onRemoveProject(project.rootPath)}
onExport={() => onExportProject(project.name)}
/>
))}
</>
)}
</section>
)

const Toolbar = ({ onSearchChange }: { onSearchChange: (val: string) => void }) => (
<div className="border-border flex h-12 border-b">
<div className="border-border flex w-1/4 min-w-[200px] items-center border-r px-4 text-xs font-bold tracking-wider text-slate-500 uppercase">
<div className="border-border flex w-1/4 min-w-50 items-center border-r px-4 text-xs font-bold tracking-wider text-slate-500 uppercase">
<ArchiveIcon className="mr-2 h-4 w-4" /> Recent
</div>
<div className="flex flex-1 items-center px-4">
Expand Down
18 changes: 18 additions & 0 deletions src/main/frontend/app/services/frank-framework-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { apiFetch } from '~/utils/api'

interface FFInstance {
name: string
configurations: FFConfiguration[]
}

export interface FFConfiguration {
name: string
stubbed: boolean
version?: string
parent?: string
filename?: string
}

export async function fetchInstanceConfigurations(signal?: AbortSignal): Promise<FFInstance> {
return apiFetch<FFInstance>('/projects/configurations', { signal })
}
1 change: 1 addition & 0 deletions src/main/java/org/frankframework/flow/FlowApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class FlowApplication {

public static void main(String[] args) {
SpringApplication app = configureApplication();

app.run(args);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.frankframework.flow.common;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import jakarta.annotation.security.RolesAllowed;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.frankframework.lifecycle.DynamicRegistration;

/**
* To avoid repeating this list of user roles, use a default annotation
*
* @see DynamicRegistration#ALL_IBIS_USER_ROLES
*/
@Documented
@Retention(RUNTIME)
@Target(METHOD)
@RolesAllowed({ "IbisObserver", "IbisDataAdmin", "IbisAdmin", "IbisTester" })
public @interface AllowAllFrankUserRoles {
}
Loading
Loading