Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Contents

...

GSoC 2026 - Dubbo Lightweight Refactoring

Background and Goal

Over the past several major releases, Apache Dubbo has accumulated a large number of modules, dependencies, and legacy integrations. This results in increased framework size, slower startup time, and higher dependency complexity for users who only need core RPC functionality.

With the increasing adoption of cloud-native and microservice environments, lightweight frameworks with minimal dependencies are becoming increasingly important.

Therefore, this project aims to analyze and refactor Dubbo's dependency structure to make the framework more modular and lightweight.

Goal

The project aims to improve Dubbo’s modularization and reduce unnecessary dependencies.

Expected tasks include:

  1. Analyze the dependency graph of the Dubbo core modules.
  1. Identify redundant or unnecessary dependencies.
  1. Refactor module boundaries to improve modularity.
  1. Reduce the size of the minimal runtime dependency set.
  1. Provide documentation and benchmarks comparing before and after results.

Possible Extensions

  • Provide a minimal runtime profile for Dubbo.
  • Introduce optional dependency loading mechanisms.
  • Optimize startup time and memory footprint.

Relevant Skills

  • Java
  • Build tools (Maven / Gradle)
  • Dependency management
  • Microservice frameworks

Potential Mentors

  1. Albumen Kevin, Apache Dubbo PMC, albumenj@apache.org
  2. dev@dubbo.apache.org

 

Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Liang He, mail: heliang666s (at) apache.org
Project Devs, mail:

GSoC 2026

– Aligning Dubbo APIs with Modern gRPC APIs

- Further strengthen the multi-language development of Dubbo.

BackGround And Goal

The RPC module of Dubbo Rust And Dubbo Python needs to be aligned with Dubbo Java to achieve feature parity: including but not limited to RPC protocol support (e.g., Dubbo, Triple, gRPC), serialization/deserialization mechanisms, load balancing strategies, and fault tolerance capabilities.

Goal

Expectd Tasks include:

1. Dubbo Rust or Dubbo Python: Re-architecture and Documentation Reorganization
2. Develop the registry model for the RPC module of Dubbo Rust/Dubbo Python to support the Triple protocol
3.Support for a variety of load balancing strategies

Relevant Skills
  • Java
  • Python or Rust
  • gRPC
  • HTTP
  • RPC Framworks

Background and Goal

Apache Dubbo aims to provide full interoperability with the gRPC ecosystem through the Triple protocol, which is designed to be fully compatible with gRPC communication and tooling.

However, the gRPC ecosystem has evolved significantly in recent years, including improvements in APIs, streaming interfaces, and client/server abstractions.

Some of these updates have not yet been fully reflected in Dubbo's current API design and SDK capabilities.

This project aims to evaluate the latest gRPC APIs and align Dubbo’s APIs with modern gRPC design patterns where appropriate.

Goal

Expected tasks include:

  1. Investigate the latest gRPC API changes and features.
  1. Identify gaps between Dubbo’s APIs and the gRPC ecosystem.
  1. Improve or extend Dubbo APIs to better align with gRPC usage patterns.
  1. Improve interoperability between Dubbo services and gRPC clients.
  1. Provide examples and documentation for cross-framework usage.

Possible Extensions

  • Improve gRPC streaming support
  • Enhance proto-based service definitions
  • Improve developer experience for cross-language services

Relevant Skills

  • Java
  • gRPC
  • RPC frameworks
  • Distributed systems
    Potential Mentors
  1. Albumen KevinRain Yu,Apache Dubbo PMC,albumenj@apacherainyu@apache.orgImage Modified
  2. dev@dubbo.apache.org
 
Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Liang HeYu Yu, mail: heliang666s rainyu (at) apache.org
Project Devs, mail:

Apache Fineract

GSoC 2026 - Convert Dubbo capabilities into AI Skills.

Background and Goal

With the continuous evolution of AI technology and the growing popularity of AI coding, this GSOC project aims to add a series of AI Skills for Dubbo. These AI Skills will clearly describe Dubbo's core capabilities, key modules such as RPC, registry, and distributed system, as well as Dubbo's design principles. The goal is to help developers and relevant staff better understand, develop, and use Dubbo and its affiliated projects, and enable users to more efficiently understand and use Dubbo with the help of AI tools.

Goal

This project is designed to build a complete Dubbo Skills.
Expected tasks include:

  1. Analyze the dependency graph of the Dubbo core modules.
  2. Gain an in-depth understanding of Dubbo's inherent design and the design of each of its modules.  
  1. Develop a complete standalone Skills directory that can be correctly identified and utilized by AI
     

Relevant Skills

  • Java
  • Build tools (Maven / Gradle)
  • AI
  • Microservice frameworks

Potential Mentors

  1. Albumen Kevin, Apache Dubbo PMC, albumenj@apache.orgImage Added
  2. dev@dubbo.apache.org
Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Yu Yu, mail: rainyu (at) apache.org
Project Devs, mail:

Apache Fineract

Loan Origination (POC)

No one should work on this specific ticket unless assigned - the GSOC candidate we choose will be assigned this ticket.  

For more information, you should be reviewing emails on this subject and following the Wiki pages. 
https://lists.apache.org/list.html?dev@fineract.apache.org
https://cwiki.apache.org/confluence/display/FINERACT/GSOC+Program+at+Fineract 

 

LOAN ORIGINATION CONTEXT 

Fineract has some loan origination functionality but it is not robust enough for many operations.  Several vendors, working with Fineract have created new Loan Origination plug ins.  

There is also a major enhancement underway that would build out a full Loan Origination flow by supporting the backend needs of data storage for such LOS.   See ticket https://issues.apache.org/jira/browse/FINERACT-2418 .  

The GSOC student would be expected to propose something as a POC (proof of concept) that would use and expand upon the developed Fineract backend solution.  It should not revisit the design of that, and it should be separated enough as to not collide with ongoing work in the project that may be moving faster.   

It may be useful to build a new component outside of Fineract to create the flows that would demonstrate the LOS functionality.  

That is, this is a moving target, and we would need different proposals from prospective candidates to explore the area of Loan Origination.  This may require expertise in risk assessment, loan origination models and business acumen.  There will not be much more explanation that this available.  The student would be expected to be a self starter.  

The mentor for this would need to be an expert at risk modeling, understand Loan Origination, and support a conceptual basis that may involve some things internal to Fineract and some processing elements outside of Fineract.  Please comment below if you are an existing Fineract contributor with this expertise.  

To try to illustrate:   one possible GSOC Proposal archtype we could accept would be a survey of Loan Origination Models, their strengths and weaknesses and to identify commonalities for the community to focus on.  This would thus be a Requirements exercise and may help identify future roadmap concepts.  In this case, the code to be developed may just expose a few APIs into different screen flows.  Thus, perhaps FIGMA flows (or similar) connecting to a set of APIs on the backend. 

If those new LOS APIs are existing in June 2026 (ticket 2418 resolved), then those APIs are to be used.  if they are NOT there in Fineract, then the student would be requested to create a fork and to implement the POC outside of the main Dev branch.  

I welcome additions to this write up.  jdailey  


Difficulty: Minor
Project size: ~350

Loan Origination (POC)

No one should work on this specific ticket unless assigned - the GSOC candidate we choose will be assigned this ticket.  

For more information, you should be reviewing emails on this subject and following the Wiki pages. 
https://lists.apache.org/list.html?dev@fineract.apache.org
https://cwiki.apache.org/confluence/display/FINERACT/GSOC+Program+at+Fineract 

 

LOAN ORIGINATION CONTEXT 

Fineract has some loan origination functionality but it is not robust enough for many operations.  Several vendors, working with Fineract have created new Loan Origination plug ins.  

There is also a major enhancement underway that would build out a full Loan Origination flow by supporting the backend needs of data storage for such LOS.   See ticket https://issues.apache.org/jira/browse/FINERACT-2418 .  

The GSOC student would be expected to propose something as a POC (proof of concept) that would use and expand upon the developed Fineract backend solution.  It should not revisit the design of that, and it should be separated enough as to not collide with ongoing work in the project that may be moving faster.   

It may be useful to build a new component outside of Fineract to create the flows that would demonstrate the LOS functionality.  

That is, this is a moving target, and we would need different proposals from prospective candidates to explore the area of Loan Origination.  This may require expertise in risk assessment, loan origination models and business acumen.  There will not be much more explanation that this available.  The student would be expected to be a self starter.  

The mentor for this would need to be an expert at risk modeling, understand Loan Origination, and support a conceptual basis that may involve some things internal to Fineract and some processing elements outside of Fineract.  Please comment below if you are an existing Fineract contributor with this expertise.  

To try to illustrate:   one possible GSOC Proposal archtype we could accept would be a survey of Loan Origination Models, their strengths and weaknesses and to identify commonalities for the community to focus on.  This would thus be a Requirements exercise and may help identify future roadmap concepts.  In this case, the code to be developed may just expose a few APIs into different screen flows.  Thus, perhaps FIGMA flows (or similar) connecting to a set of APIs on the backend. 

If those new LOS APIs are existing in June 2026 (ticket 2418 resolved), then those APIs are to be used.  if they are NOT there in Fineract, then the student would be requested to create a fork and to implement the POC outside of the main Dev branch.  

I welcome additions to this write up.  jdailey  

Difficulty: Minor
Project size: ~350 hour (large)
Potential mentors:
James Dailey, mail: jdailey (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

Fineract Backoffice Interface (POC)

To enable a more comprehensive Fineract project, there will be a new administrative backend User Interface (UI) component.  It will be a separate GitHub repository within the Apache Fineract project.  

It will be aimed explaining the key functionality of fineract to devs and to act as the demo infrastructure.  It will be aimed at being downloaded as part of the Docker container from the ASF, for example.  

It should include, for the system user and dev, a page showing all of the APIs organized in a sensible way, and generated automatically at each build.  

This back office component is NOT THE SAME as the end-user POC that is proposed in https://issues.apache.org/jira/browse/FINERACT-2440  

This does overlap partially with external open source projects that are offered under different licenses.  However, this will be apache 2.0 license.  

This project will use Angular.  

This project should re-imagine the Fineract use cases in a way that is visually simple, distinct, and relates to the several user groups that we see in the project:  Fintechs, embedded lending programs, non banking financial institutions (lenders), small banks, etc

Use cases will include, but not be limited to: 

  • login and select user type
  • configure other users 
  • set up a new loan product 
  • disburse a loan 
  • create a savings account 
  • configure global variables 
  • run dashboards 
Difficulty: Minor
Project size: ~350 hour (large)
Potential mentors:
James Dailey, mail: jdailey (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

Front end application MVP (POC)

No one should work on this specific ticket unless assigned - the GSOC candidate we choose will be assigned this ticket.  

For more information, you should be reviewing emails on this subject and following the Wiki pages. 
https://lists.apache.org/list.html?dev@fineract.apache.org
https://cwiki.apache.org/confluence/display/FINERACT/GSOC+Program+at+Fineract 

Build a simple self-service front end that talks to the Self-Service API

We need a new, user-friendly front end app that connects to our Backend for Front end (Self-Service API component) This will be the “customer portal” experience where users can log in, see their accounts, and check recent activity. It should be straightforward, easy to use, and a good reference example for others to build on.  

Functionality needed would include:  

  • Login
  • Check balances
  • Transfer between accounts owned by the same customer.  
  • Submit application for a new loan 

Testing end to end required. 
Solid UI design 
Modern app framework 
Documentation 

Difficulty: Minor
Project size: ~350 hour (large)
Potential mentors:
James Dailey, mail: jdailey (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

Fix typos in GLClosures and AccountingRule Swagger descriptions

Found and fixed spelling errors in the Swagger-generated API documentation:

  • GLClosuresApiResource.java: Changed "identifer" to "identifier".
  • AccountingRuleApiResource.java: Changed "Journal entires" to "journal entries".
Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Suthankan Balenthiran, mail: suthankanb (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

BI connector and demonstration

No

Create a new backend for front end component POC

Note:  GSOC applicants - this is a "draft concept".   Do not work on your proposal until we kick off the process at Fineract for evaluating.  We may significantly edit this concept or create new ones to replace it.  

No one should work on this specific ticket unless assigned - the GSOC candidate we choose will be assigned this ticket.  

For more information, you should be reviewing emails on this subject and following the Wiki pages. 
https://lists.apache.org/list.html?dev@fineract.apache.org
https://cwiki.apache.org/confluence/display/FINERACT/GSOC+Program+at+Fineract 

Build a Self-Service API Component that Connects to Apache Fineract

When the project removed self-service APIs in 2025, it did so understanding that we would need an outside component to make that connection as part of an overall solution. 

This project is to create - as a Proof of Concept (POC) - a new dedicated Self-Service API component or service that integration with Fineract backend.  It will need to expose APIs to consumer facing applications for typical activities like viewing account balances, transaction initiation, loan application, etc.  

The idea is for GSOC candidates to propose a design and build the POC. 

Minimal criteria include testing, authentication methodology, documentation.  

The idea is to create a connector and a demonstration of analytics that would consume and organize data from Fineract.  

For example, create a way to pull data out of Fineract and make it easy to use in common analytics  such as Power BI or Tableau or, better yet, an open source variant.   The data should probably go to a Data Warehouse.  

Start by proposing and exploring different options and write up the pros and cons.  

Create a demonstration project that takes into account security, levels of access, and security of PII data if it existsNot included in this GSOC would be the end consumer APP, although that may be undertaken by another project and coordination would be needed

Difficulty: Minor
Project size: ~350 hour (large)
Potential mentors:
James Dailey, mail: jdailey (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

Fix typos in GLClosures and AccountingRule Swagger descriptions

Found and fixed spelling errors in the Swagger-generated API documentation:

  • GLClosuresApiResource.java: Changed "identifer" to "identifier".
  • AccountingRuleApiResource.java: Changed "Journal entires" to "journal entries".
Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Suthankan Balenthiran, mail: suthankanb (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

Fineract Backoffice Interface (POC)

To enable a more comprehensive Fineract project, there will be a new administrative backend User Interface (UI) component.  It will be a separate GitHub repository within the Apache Fineract project.  

It will be aimed explaining the key functionality of fineract to devs and to act as the demo infrastructure.  It will be aimed at being downloaded as part of the Docker container from the ASF, for example.  

It should include, for the system user and dev, a page showing all of the APIs organized in a sensible way, and generated automatically at each build.  

This back office component is NOT THE SAME as the end-user POC that is proposed in https://issues.apache.org/jira/browse/FINERACT-2440  

This does overlap partially with external open source projects that are offered under different licenses.  However, this will be apache 2.0 license.  

This project will use Angular.  

This project should re-imagine the Fineract use cases in a way that is visually simple, distinct, and relates to the several user groups that we see in the project:  Fintechs, embedded lending programs, non banking financial institutions (lenders), small banks, etc

Use cases will include, but not be limited to: 

  • login and select user type
  • configure other users 
  • set up a new loan product 
  • disburse a loan 
  • create a savings account 
  • configure global variables 
  • run dashboards 
Difficulty: Minor
Project size: ~350 hour (large)
Potential mentors:
James Dailey, mail: jdailey (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

fineract-client-feign usage for integration tests

No one should work on this specific ticket unless assigned - the GSOC candidate we choose will be assigned this ticket.  

For more information, you should be reviewing emails on this subject and following the Wiki pages. 
https://lists.apache.org/list.html?dev@fineract.apache.org
https://cwiki.apache.org/confluence/display/FINERACT/GSOC+Program+at+Fineract 


"Moving away from RestAssured (low-level) API calls in integration tests and rather use fineract-client-feign would be a great improvement" 

Summary (with some assist from chatgpt for clarity) 

Apache Fineract has a large set of REST APIs and many integration tests currently call those APIs using RestAssured(low-level HTTP requests). This ticket is to help modernize the tests by switching them to use fineract-client-feign, which is Fineract’s higher-level API client. 

Goal

Create a simple migration approach and then migrate a small set of integration tests from RestAssured to fineract-client-feign.

Why we’re doing this

  • Makes tests easier to read and maintain (less raw HTTP code).
  • Encourages consistent API usage across tests.
  • Reduces duplicated request-building logic (headers, base URLs, auth, etc.).

Scope of Work

1) Create a short migration plan

Write a short note (in the Jira ticket comments or a small doc) that answers:

  • Where are the current RestAssured-based integration tests located?
  • What’s the recommended pattern for using fineract-client-feign in tests?
  • What should be migrated first (start small)?

2) Pick a small “starter set” of tests

Identify 2–5 integration tests that:

  • Are simple (e.g., create/read/update a resource)
  • Don’t involve complicated multi-step workflows
  • Run reliably in CI

3) Implement the migration for the starter set

For each selected test:

  • Replace RestAssured calls with fineract-client-feign client calls
  • Keep the same assertions (same expected behavior)
  • Ensure the tests still pass locally and in CI

4) Document the new pattern

Add a short README note or comments in the test code showing:

  • How to initialize/configure the Feign client for tests
  • How auth/session is handled
  • A small “before vs after” explanation (1 paragraph is enough)

Acceptance Criteria

  • A brief migration plan is written and linked in the ticket.
  • At least 2 integration tests have been converted to use fineract-client-feign.
  • All tests pass (locally and/or in CI).
  • A short note exists explaining how to write future integration tests using fineract-client-feign.

Notes / Hints for a beginner

  • Start by converting just one very small test to learn the pattern.
  • Keep changes small and easy to review (one test per commit is ideal).
  • If something is unclear (e.g., how auth is set up), add a comment in the ticket describing what you found.

Out of Scope (for this ticket)

  • Migrating all integration tests across the repo
  • Refactoring production API code
  • Changing API behavior—this is only a test client swap
Difficulty: Minor
Project size: ~350 hour (large)
Potential mentors:
James Dailey, mail: jdailey (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

Fix SBOM generation errors and issues

```

Task :custom:acme:note:starter:cyclonedxDirectBom
Unable to resolve POM for org.eclipse.platform:org.eclipse.swt:3.124.100
java.lang.IllegalStateException: An error occurred attempting to resolve effective POM
        at org.cyclonedx.gradle.MavenHelper.resolveEffectivePom(MavenHelper.java:404

Fix SBOM generation errors and issues

```

Task :custom:acme:note:starter:cyclonedxDirectBom
Unable to resolve POM for org.eclipse.platform:org.eclipse.swt:3.124.100
java.lang.IllegalStateException: An error occurred attempting to resolve effective POM
        at org.cyclonedx.gradle.MavenHelper.resolveEffectivePom(MavenHelper.java:404)
        at org.cyclonedx.gradle.MavenProjectLookup.getResolvedMavenProject(MavenProjectLookup.java:77)
        at org.cyclonedx.gradle.DependencyGraphTraverser.extractMetaDataFromRepository(DependencyGraphTraverser.java:183)
        at org.cyclonedx.gradle.DependencyGraphTraverser.toSbomComponent(DependencyGraphTraverser.java:153)
        at org.cyclonedx.gradle.DependencyGraphTraverser.lambda$toSbomComponents$0(DependencyGraphTraverser.java:139)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1858)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
        at org.cyclonedx.gradle.DependencyGraphTraverser.toSbomComponents(DependencyGraphTraverser.java:140)
        at org.cyclonedx.gradle.DependencyGraphTraverser.traverseGraph(DependencyGraphTraverser.java:134)
        at org.cyclonedx.gradle.SbomGraphProvider.lambda$traverseProject$0(SbomGraphProvider.java:141)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:657)
        at org.cyclonedx.gradle.SbomGraphProvider.call(SbomGraphProvider.java:83)
        at org.cyclonedx.gradle.SbomGraphProvider.call(SbomGraphProvider.java:48)
        at org.gradle.api.internal.provider.DefaultProvider.calculateOwnValue(DefaultProvider.java:73)
        at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateOwnPresentValue(AbstractMinimalProvider.java:82)
        at org.gradle.api.internal.provider.AbstractMinimalProvider.get(AbstractMinimalProvider.java:102)
        at org.cyclonedx.gradle.CyclonedxDirectTaskMavenProjectLookup.createBomgetResolvedMavenProject(CyclonedxDirectTaskMavenProjectLookup.java:10077)
        at javaorg.base/jdkcyclonedx.internalgradle.reflectDependencyGraphTraverser.DirectMethodHandleAccessor.invokeextractMetaDataFromRepository(DirectMethodHandleAccessorDependencyGraphTraverser.java:103183)
        at javaorg.base/javacyclonedx.langgradle.reflectDependencyGraphTraverser.Method.invoketoSbomComponent(MethodDependencyGraphTraverser.java:580153)
        at org.cyclonedx.gradle.internal.reflect.JavaMethod.invoke(JavaMethodDependencyGraphTraverser.lambda$toSbomComponents$0(DependencyGraphTraverser.java:125139)
        at orgjava.gradlebase/java.apiutil.internalstream.project.taskfactory.StandardTaskAction.doExecute(StandardTaskActionReferencePipeline$3$1.accept(ReferencePipeline.java:58197)
        at orgjava.gradlebase/java.apiutil.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskActionHashMap$EntrySpliterator.forEachRemaining(HashMap.java:511858)
        at orgjava.gradlebase/java.apiutil.internalstream.project.taskfactory.StandardTaskAction.execute(StandardTaskActionAbstractPipeline.copyInto(AbstractPipeline.java:29509)
        at orgjava.gradlebase/java.apiutil.internalstream.tasks.execution.TaskExecution$3.run(TaskExecutionAbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:244499)
        at orgjava.gradlebase/java.internalutil.operationsstream.DefaultBuildOperationRunner$1ReduceOps$ReduceOp.executeevaluateSequential(DefaultBuildOperationRunnerReduceOps.java:30921)
        at orgjava.gradlebase/java.internalutil.operationsstream.DefaultBuildOperationRunner$1AbstractPipeline.executeevaluate(DefaultBuildOperationRunnerAbstractPipeline.java:27234)
        at orgjava.gradlebase/java.internalutil.operationsstream.DefaultBuildOperationRunner$2ReferencePipeline.executecollect(DefaultBuildOperationRunnerReferencePipeline.java:67682)
        at org.cyclonedx.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunnerDependencyGraphTraverser.toSbomComponents(DependencyGraphTraverser.java:60140)
        at org.cyclonedx.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167DependencyGraphTraverser.traverseGraph(DependencyGraphTraverser.java:134)
        at org.cyclonedx.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunnerSbomGraphProvider.lambda$traverseProject$0(SbomGraphProvider.java:60141)
        at orgjava.gradlebase/java.internalutil.operationsstream.DefaultBuildOperationRunnerReferencePipeline$3$1.runaccept(DefaultBuildOperationRunnerReferencePipeline.java:48197)
        at orgjava.gradlebase/java.apiutil.internal.tasks.execution.TaskExecution.executeAction(TaskExecutionSpliterators$ArraySpliterator.forEachRemaining(Spliterators.java:2291024)
        at orgjava.gradlebase/java.apiutil.internalstream.tasks.execution.TaskExecution.executeActions(TaskExecutionAbstractPipeline.copyInto(AbstractPipeline.java:212509)
        at orgjava.gradlebase/java.apiutil.internalstream.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecutionAbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:195499)
        at orgjava.gradlebase/java.apiutil.internalstream.tasks.execution.TaskExecution.execute(TaskExecutionReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:162921)
        at orgjava.gradlebase/java.internalutil.executionstream.stepsAbstractPipeline.ExecuteStep.executeInternalevaluate(ExecuteStepAbstractPipeline.java:105234)
        at orgjava.gradlebase/java.internalutil.executionstream.stepsReferencePipeline.ExecuteStep.access$000reduce(ExecuteStepReferencePipeline.java:44657)
        at org.cyclonedx.gradle.internal.execution.steps.ExecuteStep$1SbomGraphProvider.call(ExecuteStepSbomGraphProvider.java:5983)
        at org.cyclonedx.gradle.internal.execution.steps.ExecuteStep$1SbomGraphProvider.call(ExecuteStepSbomGraphProvider.java:5648)
        at org.gradle.api.internal.operationsprovider.DefaultBuildOperationRunner$CallableBuildOperationWorkerDefaultProvider.executecalculateOwnValue(DefaultBuildOperationRunnerDefaultProvider.java:21073)
        at org.gradle.api.internal.operationsprovider.DefaultBuildOperationRunner$CallableBuildOperationWorkerAbstractMinimalProvider.executecalculateOwnPresentValue(DefaultBuildOperationRunnerAbstractMinimalProvider.java:20582)
        at org.gradle.api.internal.operationsprovider.DefaultBuildOperationRunner$2AbstractMinimalProvider.executeget(DefaultBuildOperationRunnerAbstractMinimalProvider.java:67102)
        at org.cyclonedx.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunnerCyclonedxDirectTask.createBom(CyclonedxDirectTask.java:60100)
        at orgjava.gradlebase/jdk.internal.operationsreflect.DefaultBuildOperationRunnerDirectMethodHandleAccessor.executeinvoke(DefaultBuildOperationRunnerDirectMethodHandleAccessor.java:167103)
        at orgjava.gradlebase/java.internallang.operationsreflect.DefaultBuildOperationRunnerMethod.executeinvoke(DefaultBuildOperationRunnerMethod.java:60580)
        at org.gradle.internal.operationsreflect.DefaultBuildOperationRunnerJavaMethod.callinvoke(DefaultBuildOperationRunnerJavaMethod.java:54125)
        at org.gradle.api.internal.executionproject.stepstaskfactory.ExecuteStepStandardTaskAction.executedoExecute(ExecuteStepStandardTaskAction.java:5658)
        at org.gradle.api.internal.executionproject.stepstaskfactory.ExecuteStepStandardTaskAction.execute(ExecuteStepStandardTaskAction.java:4451)
        at org.gradle.api.internal.executionproject.stepstaskfactory.CancelExecutionStepStandardTaskAction.execute(CancelExecutionStepStandardTaskAction.java:4229)
        at org.gradle.api.internal.tasks.execution.stepsTaskExecution$3.TimeoutStep.executeWithoutTimeoutrun(TimeoutStepTaskExecution.java:75244)
        at org.gradle.internal.executionoperations.steps.TimeoutStepDefaultBuildOperationRunner$1.execute(TimeoutStepDefaultBuildOperationRunner.java:5530)
        at org.gradle.internal.executionoperations.steps.PreCreateOutputParentsStepDefaultBuildOperationRunner$1.execute(PreCreateOutputParentsStepDefaultBuildOperationRunner.java:5027)
        at org.gradle.internal.executionoperations.steps.PreCreateOutputParentsStepDefaultBuildOperationRunner$2.execute(PreCreateOutputParentsStepDefaultBuildOperationRunner.java:2867)
        at org.gradle.internal.executionoperations.steps.RemovePreviousOutputsStepDefaultBuildOperationRunner$2.execute(RemovePreviousOutputsStepDefaultBuildOperationRunner.java:6760)
        at org.gradle.internal.executionoperations.stepsDefaultBuildOperationRunner.RemovePreviousOutputsStep.executeexecute(RemovePreviousOutputsStepDefaultBuildOperationRunner.java:37167)
        at org.gradle.internal.executionoperations.stepsDefaultBuildOperationRunner.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStepDefaultBuildOperationRunner.java:6160)
        at org.gradle.internal.executionoperations.stepsDefaultBuildOperationRunner.BroadcastChangingOutputsStep.executerun(BroadcastChangingOutputsStepDefaultBuildOperationRunner.java:2648)
        at org.gradle.api.internal.tasks.execution.stepsTaskExecution.CaptureOutputsAfterExecutionStep.executeexecuteAction(CaptureOutputsAfterExecutionStepTaskExecution.java:69229)
        at org.gradle.api.internal.tasks.execution.stepsTaskExecution.CaptureOutputsAfterExecutionStep.executeexecuteActions(CaptureOutputsAfterExecutionStepTaskExecution.java:46212)
        at org.gradle.api.internal.tasks.execution.stepsTaskExecution.ResolveInputChangesStep.executeexecuteWithPreviousOutputFiles(ResolveInputChangesStepTaskExecution.java:40195)
        at org.gradle.api.internal.tasks.execution.steps.ResolveInputChangesStepTaskExecution.execute(ResolveInputChangesStepTaskExecution.java:29162)
        at org.gradle.internal.execution.steps.BuildCacheStepExecuteStep.executeWithoutCacheexecuteInternal(BuildCacheStepExecuteStep.java:189105)
        at org.gradle.internal.execution.steps.BuildCacheStepExecuteStep.executeAndStoreInCacheaccess$000(BuildCacheStepExecuteStep.java:14544)
        at org.gradle.internal.execution.steps.BuildCacheStepExecuteStep$1.lambda$executeWithCache$4call(BuildCacheStepExecuteStep.java:10159)
        at java.base/java.util.Optional.orElseGet(Optionalorg.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:36456)
        at org.gradle.internal.executionoperations.stepsDefaultBuildOperationRunner$CallableBuildOperationWorker.BuildCacheStep.lambda$executeWithCache$5execute(BuildCacheStepDefaultBuildOperationRunner.java:101210)
        at org.gradle.internal.operations.Try$SuccessDefaultBuildOperationRunner$CallableBuildOperationWorker.mapexecute(TryDefaultBuildOperationRunner.java:175205)
        at org.gradle.internal.executionoperations.stepsDefaultBuildOperationRunner$2.BuildCacheStep.executeWithCacheexecute(BuildCacheStepDefaultBuildOperationRunner.java:8567)
        at org.gradle.internal.executionoperations.stepsDefaultBuildOperationRunner$2.BuildCacheStep.lambda$execute$0execute(BuildCacheStepDefaultBuildOperationRunner.java:7460)
        at org.gradle.internal.operations.Either$LeftDefaultBuildOperationRunner.foldexecute(EitherDefaultBuildOperationRunner.java:115167)
        at org.gradle.internal.executionoperations.cachingDefaultBuildOperationRunner.CachingState.foldexecute(CachingStateDefaultBuildOperationRunner.java:6260)
        at org.gradle.internal.executionoperations.stepsDefaultBuildOperationRunner.BuildCacheStep.executecall(BuildCacheStepDefaultBuildOperationRunner.java:7354)
        at org.gradle.internal.execution.steps.BuildCacheStepExecuteStep.execute(BuildCacheStepExecuteStep.java:4856)
        at org.gradle.internal.execution.steps.StoreExecutionStateStepExecuteStep.execute(StoreExecutionStateStepExecuteStep.java:4644)
        at org.gradle.internal.execution.steps.StoreExecutionStateStepCancelExecutionStep.execute(StoreExecutionStateStepCancelExecutionStep.java:3542)
        at org.gradle.internal.execution.steps.SkipUpToDateStepTimeoutStep.executeBecauseexecuteWithoutTimeout(SkipUpToDateStepTimeoutStep.java:75)
        at org.gradle.internal.execution.steps.SkipUpToDateStepTimeoutStep.lambda$execute$2execute(SkipUpToDateStepTimeoutStep.java:5355)
        at java.base/java.util.Optional.orElseGet(Optionalorg.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:36450)
        at org.gradle.internal.execution.steps.SkipUpToDateStepPreCreateOutputParentsStep.execute(SkipUpToDateStepPreCreateOutputParentsStep.java:5328)
        at org.gradle.internal.execution.steps.SkipUpToDateStepRemovePreviousOutputsStep.execute(SkipUpToDateStepRemovePreviousOutputsStep.java:3567)
        at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStepRemovePreviousOutputsStep.execute(MarkSnapshottingInputsFinishedStepRemovePreviousOutputsStep.java:37)
        at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStepBroadcastChangingOutputsStep.execute(MarkSnapshottingInputsFinishedStepBroadcastChangingOutputsStep.java:2761)
        at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStepBroadcastChangingOutputsStep.executeDelegateexecute(ResolveIncrementalCachingStateStepBroadcastChangingOutputsStep.java:4926)
        at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStepCaptureOutputsAfterExecutionStep.executeDelegateexecute(ResolveIncrementalCachingStateStepCaptureOutputsAfterExecutionStep.java:2769)
        at org.gradle.internal.execution.steps.AbstractResolveCachingStateStepCaptureOutputsAfterExecutionStep.execute(AbstractResolveCachingStateStepCaptureOutputsAfterExecutionStep.java:7146)
        at org.gradle.internal.execution.steps.AbstractResolveCachingStateStepResolveInputChangesStep.execute(AbstractResolveCachingStateStepResolveInputChangesStep.java:3940)
        at org.gradle.internal.execution.steps.ResolveChangesStepResolveInputChangesStep.execute(ResolveChangesStepResolveInputChangesStep.java:6529)
        at org.gradle.internal.execution.steps.ResolveChangesStepBuildCacheStep.executeexecuteWithoutCache(ResolveChangesStepBuildCacheStep.java:36189)
        at org.gradle.internal.execution.steps.ValidateStepBuildCacheStep.executeexecuteAndStoreInCache(ValidateStepBuildCacheStep.java:107145)
        at org.gradle.internal.execution.steps.ValidateStepBuildCacheStep.executelambda$executeWithCache$4(ValidateStepBuildCacheStep.java:56101)
        at orgjava.gradlebase/java.internalutil.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStepOptional.orElseGet(Optional.java:64364)
        at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStepBuildCacheStep.executelambda$executeWithCache$5(AbstractCaptureStateBeforeExecutionStepBuildCacheStep.java:43101)
        at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.executeWithNonEmptySources(AbstractSkipEmptyWorkStepTry$Success.map(Try.java:125175)
        at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStepBuildCacheStep.executeexecuteWithCache(AbstractSkipEmptyWorkStepBuildCacheStep.java:5685)
        at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStepBuildCacheStep.executelambda$execute$0(AbstractSkipEmptyWorkStepBuildCacheStep.java:3674)
        at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStepEither$Left.fold(Either.java:38115)
        at org.gradle.internal.execution.stepscaching.LoadPreviousExecutionStateStepCachingState.executefold(LoadPreviousExecutionStateStepCachingState.java:3662)
        at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStepBuildCacheStep.execute(LoadPreviousExecutionStateStepBuildCacheStep.java:2373)
        at org.gradle.internal.execution.steps.HandleStaleOutputsStepBuildCacheStep.execute(HandleStaleOutputsStepBuildCacheStep.java:7548)
        at org.gradle.internal.execution.steps.HandleStaleOutputsStepStoreExecutionStateStep.execute(HandleStaleOutputsStepStoreExecutionStateStep.java:4146)
        at org.gradle.internal.execution.steps.AssignMutableWorkspaceStepStoreExecutionStateStep.lambda$execute$0execute(AssignMutableWorkspaceStepStoreExecutionStateStep.java:35)
        at org.gradle.api.internal.tasksexecution.executionsteps.TaskExecution$4SkipUpToDateStep.withWorkspaceexecuteBecause(TaskExecutionSkipUpToDateStep.java:28975)
        at org.gradle.internal.execution.steps.AssignMutableWorkspaceStepSkipUpToDateStep.executelambda$execute$2(AssignMutableWorkspaceStepSkipUpToDateStep.java:3153)
        at orgjava.gradlebase/java.internalutil.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStepOptional.orElseGet(Optional.java:22364)
        at org.gradle.internal.execution.steps.ChoosePipelineStepSkipUpToDateStep.execute(ChoosePipelineStepSkipUpToDateStep.java:4053)
        at org.gradle.internal.execution.steps.ChoosePipelineStepSkipUpToDateStep.execute(ChoosePipelineStepSkipUpToDateStep.java:2335)
        at org.gradle.internal.execution.steps.legacy.ExecuteWorkBuildOperationFiringStepMarkSnapshottingInputsFinishedStep.lambda$execute$2execute(ExecuteWorkBuildOperationFiringStepMarkSnapshottingInputsFinishedStep.java:6737)
        at java.base/java.util.Optional.orElseGet(Optionalorg.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:36427)
        at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStepResolveIncrementalCachingStateStep.executeexecuteDelegate(ExecuteWorkBuildOperationFiringStepResolveIncrementalCachingStateStep.java:6749)
        at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStepResolveIncrementalCachingStateStep.executeexecuteDelegate(ExecuteWorkBuildOperationFiringStepResolveIncrementalCachingStateStep.java:3927)
        at org.gradle.internal.execution.steps.IdentityCacheStepAbstractResolveCachingStateStep.execute(IdentityCacheStepAbstractResolveCachingStateStep.java:4671)
        at org.gradle.internal.execution.steps.IdentityCacheStepAbstractResolveCachingStateStep.execute(IdentityCacheStepAbstractResolveCachingStateStep.java:3439)
        at org.gradle.internal.execution.steps.IdentifyStepResolveChangesStep.execute(IdentifyStepResolveChangesStep.java:4865)
        at org.gradle.internal.execution.steps.IdentifyStepResolveChangesStep.execute(IdentifyStepResolveChangesStep.java:3536)
        at org.gradle.internal.execution.implsteps.DefaultExecutionEngine$1ValidateStep.execute(DefaultExecutionEngineValidateStep.java:64107)
        at org.gradle.apiinternal.internalexecution.taskssteps.executionValidateStep.ExecuteActionsTaskExecuter.executeIfValidexecute(ExecuteActionsTaskExecuterValidateStep.java:12756)
        at org.gradle.api.internal.tasksexecution.executionsteps.ExecuteActionsTaskExecuterAbstractCaptureStateBeforeExecutionStep.execute(ExecuteActionsTaskExecuterAbstractCaptureStateBeforeExecutionStep.java:11664)
        at org.gradle.api.internal.tasksexecution.executionsteps.ProblemsTaskPathTrackingTaskExecuterAbstractCaptureStateBeforeExecutionStep.execute(ProblemsTaskPathTrackingTaskExecuterAbstractCaptureStateBeforeExecutionStep.java:4143)
        at org.gradle.api.internal.tasksexecution.executionsteps.FinalizePropertiesTaskExecuterAbstractSkipEmptyWorkStep.executeexecuteWithNonEmptySources(FinalizePropertiesTaskExecuterAbstractSkipEmptyWorkStep.java:46125)
        at org.gradle.api.internal.tasksexecution.executionsteps.ResolveTaskExecutionModeExecuterAbstractSkipEmptyWorkStep.execute(ResolveTaskExecutionModeExecuterAbstractSkipEmptyWorkStep.java:5156)
        at org.gradle.api.internal.tasksexecution.executionsteps.SkipTaskWithNoActionsExecuterAbstractSkipEmptyWorkStep.execute(SkipTaskWithNoActionsExecuterAbstractSkipEmptyWorkStep.java:5736)
        at org.gradle.apiinternal.internalexecution.taskssteps.executionlegacy.SkipOnlyIfTaskExecuterMarkSnapshottingInputsStartedStep.execute(SkipOnlyIfTaskExecuterMarkSnapshottingInputsStartedStep.java:7438)
        at org.gradle.api.internal.tasksexecution.executionsteps.CatchExceptionTaskExecuterLoadPreviousExecutionStateStep.execute(CatchExceptionTaskExecuterLoadPreviousExecutionStateStep.java:36)
        at org.gradle.api.internal.tasksexecution.executionsteps.EventFiringTaskExecuter$1LoadPreviousExecutionStateStep.executeTaskexecute(EventFiringTaskExecuterLoadPreviousExecutionStateStep.java:7723)
        at org.gradle.api.internal.tasksexecution.executionsteps.EventFiringTaskExecuter$1HandleStaleOutputsStep.callexecute(EventFiringTaskExecuterHandleStaleOutputsStep.java:5575)
        at org.gradle.api.internal.tasksexecution.executionsteps.EventFiringTaskExecuter$1HandleStaleOutputsStep.callexecute(EventFiringTaskExecuterHandleStaleOutputsStep.java:5241)
        at org.gradle.internal.execution.operationssteps.DefaultBuildOperationRunner$CallableBuildOperationWorkerAssignMutableWorkspaceStep.executelambda$execute$0(DefaultBuildOperationRunnerAssignMutableWorkspaceStep.java:21035)
        at org.gradle.api.internal.tasks.operationsexecution.DefaultBuildOperationRunner$CallableBuildOperationWorkerTaskExecution$4.executewithWorkspace(DefaultBuildOperationRunnerTaskExecution.java:205289)
        at org.gradle.internal.execution.operationssteps.DefaultBuildOperationRunner$2AssignMutableWorkspaceStep.execute(DefaultBuildOperationRunnerAssignMutableWorkspaceStep.java:6731)
        at org.gradle.internal.execution.operationssteps.DefaultBuildOperationRunner$2AssignMutableWorkspaceStep.execute(DefaultBuildOperationRunnerAssignMutableWorkspaceStep.java:6022)
        at org.gradle.internal.operationsexecution.DefaultBuildOperationRunnersteps.ChoosePipelineStep.execute(DefaultBuildOperationRunnerChoosePipelineStep.java:16740)
        at org.gradle.internal.operationsexecution.steps.DefaultBuildOperationRunnerChoosePipelineStep.execute(DefaultBuildOperationRunnerChoosePipelineStep.java:6023)
        at org.gradle.internal.execution.operationssteps.DefaultBuildOperationRunnerExecuteWorkBuildOperationFiringStep.calllambda$execute$2(DefaultBuildOperationRunnerExecuteWorkBuildOperationFiringStep.java:5467)
        at orgjava.gradlebase/java.apiutil.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuterOptional.orElseGet(Optional.java:52364)
        at org.gradle.internal.execution.plansteps.LocalTaskNodeExecutorExecuteWorkBuildOperationFiringStep.execute(LocalTaskNodeExecutorExecuteWorkBuildOperationFiringStep.java:4267)
        at org.gradle.internal.execution.taskgraphsteps.DefaultTaskExecutionGraph$InvokeNodeExecutorsActionExecuteWorkBuildOperationFiringStep.execute(DefaultTaskExecutionGraphExecuteWorkBuildOperationFiringStep.java:33139)
        at org.gradle.internal.execution.taskgraphsteps.DefaultTaskExecutionGraph$InvokeNodeExecutorsActionIdentityCacheStep.execute(DefaultTaskExecutionGraphIdentityCacheStep.java:31846)
        at org.gradle.internal.execution.taskgraphsteps.DefaultTaskExecutionGraph$BuildOperationAwareExecutionActionIdentityCacheStep.lambda$execute$0execute(DefaultTaskExecutionGraphIdentityCacheStep.java:31434)
        at org.gradle.internal.execution.operationssteps.CurrentBuildOperationRefIdentifyStep.withexecute(CurrentBuildOperationRefIdentifyStep.java:8548)
        at org.gradle.internal.execution.taskgraphsteps.DefaultTaskExecutionGraph$BuildOperationAwareExecutionActionIdentifyStep.execute(DefaultTaskExecutionGraphIdentifyStep.java:31435)
        at org.gradle.internal.execution.taskgraphimpl.DefaultTaskExecutionGraph$BuildOperationAwareExecutionActionDefaultExecutionEngine$1.execute(DefaultTaskExecutionGraphDefaultExecutionEngine.java:30364)
        at org.gradle.api.internal.tasks.execution.planExecuteActionsTaskExecuter.DefaultPlanExecutor$ExecutorWorker.executeexecuteIfValid(DefaultPlanExecutorExecuteActionsTaskExecuter.java:459127)
        at org.gradle.api.internal.tasks.execution.planExecuteActionsTaskExecuter.DefaultPlanExecutor$ExecutorWorker.runexecute(DefaultPlanExecutorExecuteActionsTaskExecuter.java:376116)
        at org.gradle.api.internal.tasks.execution.planProblemsTaskPathTrackingTaskExecuter.DefaultPlanExecutor.processexecute(DefaultPlanExecutorProblemsTaskPathTrackingTaskExecuter.java:11141)
        at org.gradle.api.internal.tasks.execution.taskgraphFinalizePropertiesTaskExecuter.DefaultTaskExecutionGraph.executeWithServicesexecute(DefaultTaskExecutionGraphFinalizePropertiesTaskExecuter.java:13846)
        at org.gradle.executionapi.taskgraphinternal.DefaultTaskExecutionGraphtasks.execution.ResolveTaskExecutionModeExecuter.execute(DefaultTaskExecutionGraphResolveTaskExecutionModeExecuter.java:12351)
        at org.gradle.api.internal.tasks.execution.SelectedTaskExecutionActionSkipTaskWithNoActionsExecuter.execute(SelectedTaskExecutionActionSkipTaskWithNoActionsExecuter.java:3557)
        at org.gradle.api.internal.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:51tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
        at org.gradle.api.internal.tasks.execution.BuildOperationFiringBuildWorkerExecutor$ExecuteTasksEventFiringTaskExecuter$1.call(BuildOperationFiringBuildWorkerExecutorEventFiringTaskExecuter.java:5455)
        at org.gradle.api.internal.tasks.execution.BuildOperationFiringBuildWorkerExecutor$ExecuteTasksEventFiringTaskExecuter$1.call(BuildOperationFiringBuildWorkerExecutorEventFiringTaskExecuter.java:4352)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
        at org.gradle.api.internal.tasks.execution.BuildOperationFiringBuildWorkerExecutorEventFiringTaskExecuter.execute(BuildOperationFiringBuildWorkerExecutorEventFiringTaskExecuter.java:4052)
        at org.gradle.internalexecution.buildplan.DefaultBuildLifecycleControllerLocalTaskNodeExecutor.lambda$executeTasks$10execute(DefaultBuildLifecycleControllerLocalTaskNodeExecutor.java:31342)
        at org.gradle.internalexecution.modeltaskgraph.StateTransitionControllerDefaultTaskExecutionGraph$InvokeNodeExecutorsAction.doTransitionexecute(StateTransitionControllerDefaultTaskExecutionGraph.java:266331)
        at org.gradle.internalexecution.modeltaskgraph.StateTransitionControllerDefaultTaskExecutionGraph$InvokeNodeExecutorsAction.lambda$tryTransition$8execute(StateTransitionControllerDefaultTaskExecutionGraph.java:177318)
        at org.gradle.internalexecution.worktaskgraph.DefaultSynchronizerDefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.withLocklambda$execute$0(DefaultSynchronizerDefaultTaskExecutionGraph.java:46314)
        at org.gradle.internal.modeloperations.StateTransitionControllerCurrentBuildOperationRef.tryTransitionwith(StateTransitionControllerCurrentBuildOperationRef.java:17785)
        at org.gradle.internalexecution.buildtaskgraph.DefaultBuildLifecycleControllerDefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.executeTasksexecute(DefaultBuildLifecycleControllerDefaultTaskExecutionGraph.java:304314)
        at org.gradle.internalexecution.buildtaskgraph.DefaultBuildWorkGraphController$DefaultBuildWorkGraphDefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.runWorkexecute(DefaultBuildWorkGraphControllerDefaultTaskExecutionGraph.java:220303)
        at org.gradle.internalexecution.workplan.DefaultWorkerLeaseServiceDefaultPlanExecutor$ExecutorWorker.withLocksexecute(DefaultWorkerLeaseServiceDefaultPlanExecutor.java:263459)
        at org.gradle.internalexecution.workplan.DefaultWorkerLeaseServiceDefaultPlanExecutor$ExecutorWorker.runAsWorkerThreadrun(DefaultWorkerLeaseServiceDefaultPlanExecutor.java:127376)
        at org.gradle.compositeexecution.internalplan.DefaultBuildControllerDefaultPlanExecutor.doRunprocess(DefaultBuildControllerDefaultPlanExecutor.java:181111)
        at org.gradle.compositeexecution.internaltaskgraph.DefaultBuildControllerDefaultTaskExecutionGraph.access$000executeWithServices(DefaultBuildControllerDefaultTaskExecutionGraph.java:50138)
        at org.gradle.compositeexecution.internaltaskgraph.DefaultBuildController$BuildOpRunnableDefaultTaskExecutionGraph.lambda$run$0execute(DefaultBuildControllerDefaultTaskExecutionGraph.java:198123)
        at org.gradle.internalexecution.operationsSelectedTaskExecutionAction.CurrentBuildOperationRef.withexecute(CurrentBuildOperationRefSelectedTaskExecutionAction.java:8535)
        at org.gradle.compositeexecution.internalDryRunBuildExecutionAction.DefaultBuildController$BuildOpRunnable.runexecute(DefaultBuildControllerDryRunBuildExecutionAction.java:19851)
        at javaorg.base/javagradle.utilexecution.concurrent.Executors$RunnableAdapterBuildOperationFiringBuildWorkerExecutor$ExecuteTasks.call(ExecutorsBuildOperationFiringBuildWorkerExecutor.java:57254)
        at javaorg.base/javagradle.utilexecution.concurrentBuildOperationFiringBuildWorkerExecutor$ExecuteTasks.FutureTask.runcall(FutureTaskBuildOperationFiringBuildWorkerExecutor.java:31743)
        at org.gradle.internal.concurrentoperations.ExecutorPolicy$CatchAndRecordFailuresDefaultBuildOperationRunner$CallableBuildOperationWorker.onExecuteexecute(ExecutorPolicyDefaultBuildOperationRunner.java:64210)
        at org.gradle.internal.concurrentoperations.AbstractManagedExecutor$1DefaultBuildOperationRunner$CallableBuildOperationWorker.runexecute(AbstractManagedExecutorDefaultBuildOperationRunner.java:48205)
        at javaorg.base/javagradle.utilinternal.concurrentoperations.ThreadPoolExecutorDefaultBuildOperationRunner$2.runWorkerexecute(ThreadPoolExecutorDefaultBuildOperationRunner.java:114467)
        at javaorg.base/javagradle.utilinternal.concurrentoperations.ThreadPoolExecutor$WorkerDefaultBuildOperationRunner$2.runexecute(ThreadPoolExecutorDefaultBuildOperationRunner.java:64260)
        at javaorg.base/javagradle.langinternal.Threadoperations.DefaultBuildOperationRunner.runexecute(ThreadDefaultBuildOperationRunner.java:1583167)
Caused by:         at org.apachegradle.maveninternal.modeloperations.building.ModelBuildingException: 1 problem was encountered while building the effective model for org.eclipse.platform:org.eclipse.swt:3.124.100
[ERROR] 'dependencies.dependency.artifactId' for org.eclipse.platform:org.eclipse.swt.${osgi.platform}:jar with value 'org.eclipse.swt.${osgi.platform}' does not match a valid id pattern. @ DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
        at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor.execute(BuildOperationFiringBuildWorkerExecutor.java:40)
        at org.apachegradle.maveninternal.modelbuild.buildingDefaultBuildLifecycleController.DefaultModelProblemCollector.newModelBuildingExceptionlambda$executeTasks$10(DefaultModelProblemCollectorDefaultBuildLifecycleController.java:176313)
        at org.apachegradle.maveninternal.model.buildingStateTransitionController.DefaultModelBuilder.builddoTransition(DefaultModelBuilderStateTransitionController.java:550266)
        at org.apachegradle.maveninternal.model.buildingStateTransitionController.DefaultModelBuilder.buildlambda$tryTransition$8(DefaultModelBuilderStateTransitionController.java:419177)
        at org.apachegradle.maveninternal.modelwork.buildingDefaultSynchronizer.DefaultModelBuilder.buildwithLock(DefaultModelBuilderDefaultSynchronizer.java:24846)
        at org.cyclonedx.gradle.MavenHelper.resolveEffectivePom(MavenHelperinternal.model.StateTransitionController.tryTransition(StateTransitionController.java:402177)
        at org.gradle.internal.build.DefaultBuildLifecycleController.. 189 more
Unknown file extension: custom/acme/note/starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Adam Saghy, mail: adamsaghy (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

BI connector and demonstration

No one should work on this specific ticket unless assigned - the GSOC candidate we choose will be assigned this ticket.  

For more information, you should be reviewing emails on this subject and following the Wiki pages. 
https://lists.apache.org/list.html?dev@fineract.apache.org
https://cwiki.apache.org/confluence/display/FINERACT/GSOC+Program+at+Fineract 

The idea is to create a connector and a demonstration of analytics that would consume and organize data from Fineract.  

For example, create a way to pull data out of Fineract and make it easy to use in common analytics  such as Power BI or Tableau or, better yet, an open source variant.   The data should probably go to a Data Warehouse.  

Start by proposing and exploring different options and write up the pros and cons.  

Create a demonstration project that takes into account security, levels of access, and security of PII data if it exists. 

Difficulty: Minor
Project size: ~350 hour (large)
Potential mentors:
James Dailey, mail: jdailey (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

executeTasks(DefaultBuildLifecycleController.java:304)
        at org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultBuildWorkGraph.runWork(DefaultBuildWorkGraphController.java:220)
        at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
        at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
        at org.gradle.composite.internal.DefaultBuildController.doRun(DefaultBuildController.java:181)
        at org.gradle.composite.internal.DefaultBuildController.access$000(DefaultBuildController.java:50)
        at org.gradle.composite.internal.DefaultBuildController$BuildOpRunnable.lambda$run$0(DefaultBuildController.java:198)
        at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:85)
        at org.gradle.composite.internal.DefaultBuildController$BuildOpRunnable.run(DefaultBuildController.java:198)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
        at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
        at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: org.apache.maven.model.building.ModelBuildingException: 1 problem was encountered while building the effective model for org.eclipse.platform:org.eclipse.swt:3.124.100
[ERROR] 'dependencies.dependency.artifactId' for org.eclipse.platform:org.eclipse.swt.${osgi.platform}:jar with value 'org.eclipse.swt.${osgi.platform}' does not match a valid id pattern. @ 

        at org.apache.maven.model.building.DefaultModelProblemCollector.newModelBuildingException(DefaultModelProblemCollector.java:176)
        at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:550)
        at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:419)
        at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:248)
        at org.cyclonedx.gradle.MavenHelper.resolveEffectivePom(MavenHelper.java:402)
        ... 189 more
Unknown file extension: custom/acme/note/starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Difficulty: Major

fineract-client-feign usage for integration tests

No one should work on this specific ticket unless assigned - the GSOC candidate we choose will be assigned this ticket.  

For more information, you should be reviewing emails on this subject and following the Wiki pages. 
https://lists.apache.org/list.html?dev@fineract.apache.org
https://cwiki.apache.org/confluence/display/FINERACT/GSOC+Program+at+Fineract 

"Moving away from RestAssured (low-level) API calls in integration tests and rather use fineract-client-feign would be a great improvement" 

Summary (with some assist from chatgpt for clarity) 

Apache Fineract has a large set of REST APIs and many integration tests currently call those APIs using RestAssured(low-level HTTP requests). This ticket is to help modernize the tests by switching them to use fineract-client-feign, which is Fineract’s higher-level API client. 

Goal

Create a simple migration approach and then migrate a small set of integration tests from RestAssured to fineract-client-feign.

Why we’re doing this

  • Makes tests easier to read and maintain (less raw HTTP code).
  • Encourages consistent API usage across tests.
  • Reduces duplicated request-building logic (headers, base URLs, auth, etc.).

Scope of Work

1) Create a short migration plan

Write a short note (in the Jira ticket comments or a small doc) that answers:

  • Where are the current RestAssured-based integration tests located?
  • What’s the recommended pattern for using fineract-client-feign in tests?
  • What should be migrated first (start small)?

2) Pick a small “starter set” of tests

Identify 2–5 integration tests that:

  • Are simple (e.g., create/read/update a resource)
  • Don’t involve complicated multi-step workflows
  • Run reliably in CI

3) Implement the migration for the starter set

For each selected test:

  • Replace RestAssured calls with fineract-client-feign client calls
  • Keep the same assertions (same expected behavior)
  • Ensure the tests still pass locally and in CI

4) Document the new pattern

Add a short README note or comments in the test code showing:

  • How to initialize/configure the Feign client for tests
  • How auth/session is handled
  • A small “before vs after” explanation (1 paragraph is enough)

Acceptance Criteria

  • A brief migration plan is written and linked in the ticket.
  • At least 2 integration tests have been converted to use fineract-client-feign.
  • All tests pass (locally and/or in CI).
  • A short note exists explaining how to write future integration tests using fineract-client-feign.

Notes / Hints for a beginner

  • Start by converting just one very small test to learn the pattern.
  • Keep changes small and easy to review (one test per commit is ideal).
  • If something is unclear (e.g., how auth is set up), add a comment in the ticket describing what you found.

Out of Scope (for this ticket)

  • Migrating all integration tests across the repo
  • Refactoring production API code
  • Changing API behavior—this is only a test client swap
Difficulty: Minor
Project size: ~350 hour (large)
Potential mentors:
James DaileyAdam Saghy, mail: jdailey adamsaghy (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

New command processing infrastructure

Background and Motivation

Fineract accumulated some technical debt over the years. One area that is implicated is type-safety of internal and external facing APIs, the most prominent of which is Fineract's REST API. In general the package layout of the project reflects a more or less classic layered architecture (REST API, data transfer/value objects, business logic services, storage/repositories). The project predates some of the more modern frameworks and best practices that are available today and on occasions the data structures that are exchanged offer some challenges (e.g. generic types). Fineract's code base reflects that, especially where JSON de-/serialization is involved. Nowadays, this task would be simply delegated to the Jackson framework, but when Fineract (Mifos) started the decision was made to use Google's GSON library and create handcrafted helper classes to deal with JSON parsing. While this provided a lot of flexibility this approach had some downsides:

  • the lowest common denominator is the string type (aka JSON blob); this is where we lose the type information
  • the strings are transformed into JSONObjects; a little bit better than raw strings, but barely more than a hash map
  • a ton of "magic" strings are needed to get/set values
  • this approach makes refactoring unnecessarily more difficult
  • to be able to serve an OpenAPI descriptor (as JSON and YAML) we had to re-introduce the type information at the REST API level with dummy classes that contain only the specified attributes; those classes are only used with the Swagger annotations and no were else
  • some developers skipped the layered architecture and found it too tedious to maintain DTOs and JSON helper classes, and as a result just passed JSONObjects right to the business logic layer
  • now the business logic is unnecessarily aware of how Fineract communicates to the outside world and makes replacing/enhancing the communication protocol (e.g. with GRPC) pretty much impossible

The list doesn't end here, but in the end things boil down to two main points:

  • poor developer experience: boilerplate code and missing type safety cost more time
  • bugs: the more code the more likely errors get introduced, especially when type safety is missing and we have to rely on runtime errors (vs. compile time).

There has been already some preparatory work done concerning type safety, but until now we avoided dealing with the real source of this issue. Fineract's architectures devises read from write requests ("CQRS", https://martinfowler.com/bliki/CQRS.html) for improved scalability.

The read requests are not that problematic, but all write requests pass through a component/service that is called "SynchronousCommandProcessingService. As the name suggests the execution of business logic is synchronous (mostly) due to this part of the architecture. This is not necessarily a problem (not immediately at least), but it's nevertheless a central bottleneck in the system. Even more important: this service is responsible to route incoming commands to their respective handler classes which in turn execute functions on one or more business logic services. The payload of these commands are obviously not always the same... which is the main reason why we decided to use the lowest common denominator to be able to handle these various types and rendered all payloads as strings. This compromise bubbles now up in the REST API and the business logic layers (and actually everything in between).

Over the years we've also added additional features (e.g. idempotency guarantees for incoming write requests) that make it now very hard to reason about the execution flow. Testing the performance impact of such additions to the critical execution path even can't be properly measured. Note: the current implementation of idempotency relies on database lookups (quite often, for each incoming request) and none of those queries are cached. If we wanted to store already processed requests (IDs) in a faster system (let's Redis) then this can't be done without major refactoring.

In conclusion, if we really want to fix those issues that are not only cosmetic and affect the performance and the developer experience equally then we urgently need to fix the way how we process write requests aka commands.

Target Personas

  • developers
  • integrators
  • end users
  • BaaS

Goals

  • new command processing will run independently next to the legacy mechanics
  • self contained
  • fully tested
  • ensure that the REST API is 100% backward compatible
  • try to contain the migration and make it as easy as possible for the community to integrate those changes
  • introduce types where needed and migrate the (old) JAX-RS REST resource classes to Spring Web MVC (better performance and better testability)
  • introduce DTOs if not already available and make sure if they exist that they are not outdated
  • assemble one DTO as command payload from all incoming REST API parameters (headers, query/path paramters, request bodies)
  • annotate attributes in the DTOs with Jakarta Validation annotations to enforce constraints on their values
  • wired REST API to the new command processing, one service at a time/pull request
  • take a non-critical service (like document management) and migrate it to the new command processing mechanics from top (REST API) to bottom (business logic service)
  • refactor command handlers to new internal API
  • make sure that the business service logic classes/functions take only one DTO request input parameter (aka don't let a function have 12 input parameters of type string...)
  • when all integration tests run successfully then remove all legacy boilerplate code that is not used anymore
  • make an ordered list of modules/features (easiest, lowest hanging fruit first)
  • maintain at least the same performance as the current implementation
  • optional: improve performance if it can be done in a reasonable time frame
  • optional: improve resilience if it can be done in a reasonable time frame

Non-Goals

  • current command processing will stay untouched, will run independently of new infrastructure
  • don't try cleaning up the storage layer; that's a separate effort for later (type safe queries, query peformance, clean entity classes)
  • maker-checker is tightly coupled in the current command processing implementation upstream; this is a separate concern for a separate proposal (domains: security, workflow)
  • doesn't need to be optimized for speed immediately
  • no changes in the integration tests

Proposed API Changes

Command Wrapper

Class contains some generic attributes like:

  • username
  • tenant ID
  • timestamp

The actual payload (aka command input parameters) are defined as a generic parameter "payload". It is expected that the modules implement classes that introduce the payload types and inherit from the abstract command class.

Command Processing Service

Three performance levels are configurable via application.properties

  • synchronously (required): this is pretty much as we do right now (use virtual threads optionally)
  • asynchronously (optional): with executor service and completable futures (use virtual threads optionally)
  • non-blocking (optional): high performance LMAX Disruptor non-blocking implementation

These different performance level implementations need to be absolute drop-in replacements (for each other). It is expected that more performant implementations need more testing due to increased complexity and possible unforeseen side effects (thread local variables, transactions). In case any problems show up we can always roll back to the required default implementation (synchronous).

NOTE: we should consider providing a command processing implementation based on Apache Camel once this concept is approved and we migrated already a couple of services. They are specialized for exactly this kind of use cases and have more dedicated people working on it's implementation. Could give more flexibility without us needing to maintain code.

Middlewares

TBD

Command Handlers

TBD

References to users (aka AppUser)

Keep things lightweight and only reference users by their user names.f

Risks

TBD

  • feature creep

ETA

The module has been created and merged upstream ("fineract-command"). You can try things out locally with these commands:

./gradlew :fineract-command:build
./gradlew :fineract-command:jmh

Diagrams

TBD

Related Jira Tickets

  • DTOs and JSON helper classes, and as a result just passed JSONObjects right to the business logic layer
  • now the business logic is unnecessarily aware of how Fineract communicates to the outside world and makes replacing/enhancing the communication protocol (e.g. with GRPC) pretty much impossible

The list doesn't end here, but in the end things boil down to two main points:

  • poor developer experience: boilerplate code and missing type safety cost more time
  • bugs: the more code the more likely errors get introduced, especially when type safety is missing and we have to rely on runtime errors (vs. compile time).

There has been already some preparatory work done concerning type safety, but until now we avoided dealing with the real source of this issue. Fineract's architectures devises read from write requests ("CQRS", https://martinfowler.com/bliki/CQRS.html) for improved scalability.

The read requests are not that problematic, but all write requests pass through a component/service that is called "SynchronousCommandProcessingService. As the name suggests the execution of business logic is synchronous (mostly) due to this part of the architecture. This is not necessarily a problem (not immediately at least), but it's nevertheless a central bottleneck in the system. Even more important: this service is responsible to route incoming commands to their respective handler classes which in turn execute functions on one or more business logic services. The payload of these commands are obviously not always the same... which is the main reason why we decided to use the lowest common denominator to be able to handle these various types and rendered all payloads as strings. This compromise bubbles now up in the REST API and the business logic layers (and actually everything in between).

Over the years we've also added additional features (e.g. idempotency guarantees for incoming write requests) that make it now very hard to reason about the execution flow. Testing the performance impact of such additions to the critical execution path even can't be properly measured. Note: the current implementation of idempotency relies on database lookups (quite often, for each incoming request) and none of those queries are cached. If we wanted to store already processed requests (IDs) in a faster system (let's Redis) then this can't be done without major refactoring.

In conclusion, if we really want to fix those issues that are not only cosmetic and affect the performance and the developer experience equally then we urgently need to fix the way how we process write requests aka commands.

Target Personas

  • developers
  • integrators
  • end users
  • BaaS

Goals

  • new command processing will run independently next to the legacy mechanics
  • self contained
  • fully tested
  • ensure that the REST API is 100% backward compatible
  • try to contain the migration and make it as easy as possible for the community to integrate those changes
  • introduce types where needed and migrate the (old) JAX-RS REST resource classes to Spring Web MVC (better performance and better testability)
  • introduce DTOs if not already available and make sure if they exist that they are not outdated
  • assemble one DTO as command payload from all incoming REST API parameters (headers, query/path paramters, request bodies)
  • annotate attributes in the DTOs with Jakarta Validation annotations to enforce constraints on their values
  • wired REST API to the new command processing, one service at a time/pull request
  • take a non-critical service (like document management) and migrate it to the new command processing mechanics from top (REST API) to bottom (business logic service)
  • refactor command handlers to new internal API
  • make sure that the business service logic classes/functions take only one DTO request input parameter (aka don't let a function have 12 input parameters of type string...)
  • when all integration tests run successfully then remove all legacy boilerplate code that is not used anymore
  • make an ordered list of modules/features (easiest, lowest hanging fruit first)
  • maintain at least the same performance as the current implementation
  • optional: improve performance if it can be done in a reasonable time frame
  • optional: improve resilience if it can be done in a reasonable time frame

Non-Goals

  • current command processing will stay untouched, will run independently of new infrastructure
  • don't try cleaning up the storage layer; that's a separate effort for later (type safe queries, query peformance, clean entity classes)
  • maker-checker is tightly coupled in the current command processing implementation upstream; this is a separate concern for a separate proposal (domains: security, workflow)
  • doesn't need to be optimized for speed immediately
  • no changes in the integration tests

Proposed API Changes

Command Wrapper

Class contains some generic attributes like:

  • username
  • tenant ID
  • timestamp

The actual payload (aka command input parameters) are defined as a generic parameter "payload". It is expected that the modules implement classes that introduce the payload types and inherit from the abstract command class.

Command Processing Service

Three performance levels are configurable via application.properties

  • synchronously (required): this is pretty much as we do right now (use virtual threads optionally)
  • asynchronously (optional): with executor service and completable futures (use virtual threads optionally)
  • non-blocking (optional): high performance LMAX Disruptor non-blocking implementation

These different performance level implementations need to be absolute drop-in replacements (for each other). It is expected that more performant implementations need more testing due to increased complexity and possible unforeseen side effects (thread local variables, transactions). In case any problems show up we can always roll back to the required default implementation (synchronous).

NOTE: we should consider providing a command processing implementation based on Apache Camel once this concept is approved and we migrated already a couple of services. They are specialized for exactly this kind of use cases and have more dedicated people working on it's implementation. Could give more flexibility without us needing to maintain code.

Middlewares

TBD

Command Handlers

TBD

References to users (aka AppUser)

Keep things lightweight and only reference users by their user names.f

Risks

TBD

  • feature creep

ETA

The module has been created and merged upstream ("fineract-command"). You can try things out locally with these commands:

./gradlew :fineract-command:build
./gradlew :fineract-command:jmh

Diagrams

TBD

Related Jira Tickets

Difficulty: Critical
Project size: ~350 hour (large)
Potential mentors:
Aleksandar Vidakovic, mail: aleks (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

Create a new backend for front end component POC

Note:  GSOC applicants - this is a "draft concept".   Do not work on your proposal until we kick off the process at Fineract for evaluating.  We may significantly edit this concept or create new ones to replace it.  

No one should work on this specific ticket unless assigned - the GSOC candidate we choose will be assigned this ticket.  

For more information, you should be reviewing emails on this subject and following the Wiki pages. 
https://lists.apache.org/list.html?dev@fineract.apache.org
https://cwiki.apache.org/confluence/display/FINERACT/GSOC+Program+at+Fineract 

Build a Self-Service API Component that Connects to Apache Fineract

When the project removed self-service APIs in 2025, it did so understanding that we would need an outside component to make that connection as part of an overall solution. 

This project is to create - as a Proof of Concept (POC) - a new dedicated Self-Service API component or service that integration with Fineract backend.  It will need to expose APIs to consumer facing applications for typical activities like viewing account balances, transaction initiation, loan application, etc.  

The idea is for GSOC candidates to propose a design and build the POC. 

Minimal criteria include testing, authentication methodology, documentation.  

Not included in this GSOC would be the end consumer APP, although that may be undertaken by another project and coordination would be needed. 

Difficulty: Minor
Project size: ~350 hour (large)
Potential mentors:
James Dailey, mail: jdailey (at) apache.org
Project Devs, mail: dev (at) fineract.apache.org

Apache NuttX


Add support to ESP Hosted on NuttX

ESP Hosted is a firmware that allows ESP32xx modules shared WiFi and BLE with the host OS, like Linux, RTOS or even some baremetal MCU.

Add ESP Hosted support on NuttX will allow any platform supported by NuttX to WiFi and/or BLE from ESP32xx modules.

More info: https://github.com/espressif/esp-hosted

Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Alan Carvalho de Assis, mail: acassis (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

Dropbear port (or other SSH Server/Client) to NuttX

NuttX doesn't have a SSH Client/Server support yet.

Supporting a SSH server will open doors to let NuttX boards in the fields to be access remotely for maintenance

Adding support to SSH client will let low cost boards powered by NuttX and LVGL to become a remote console control for more advanced Linux server. 

Difficulty: Major
Project size: ~90 hour (small)
Potential mentors:
Alan Carvalho de Assis, mail: acassis
Difficulty: Critical
Project size: ~350 hour (large)
Potential mentors:
Aleksandar Vidakovic, mail: aleks (at) apache.org
Project Devs, mail: dev (at) fineractnuttx.apache.org

Apache NuttX

Create a NuttX Distribution with Dynamic Binary (ELF) Loading

NuttX is very Unix/Linux-like RTOS for microcontrollers and it supports dynamic loading of binaries and libraries. It makes perfect sense to have the possibilities to create a NuttX Distros similar to what exists for Linux.

In fact there is already a proposal here

Add support to ESP Hosted on NuttX

ESP Hosted is a firmware that allows ESP32xx modules shared WiFi and BLE with the host OS, like Linux, RTOS or even some baremetal MCU.

Add ESP Hosted support on NuttX will allow any platform supported by NuttX to WiFi and/or BLE from ESP32xx modules.

More info: https://github.com/espressif/esp-hosted/apache/nuttx/issues/17351

Goals:

1) Test ELF Loading in the current NuttX mainline
2) Create an application that will be downloaded and updated the existing version on the board
3) Add Library support on NuttX/NuttX-Apps (use Android Makefile Library building as reference)
 

Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Alan Carvalho de Assis, mail: acassis (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

Micro-ROS integration on NuttX

Micro-ROS (https://micro.ros.org) is a ROS2 support to Microcontrollers. Initially the project was developed over NuttX by Bosch and other EU organizations. Later on they added support to FreeRTOS and Zephyr. After that NuttX support started ageing and we didn't get anyone working to fix it (with few exceptions like Roberto Bucher work to test it with pysimCoder).

Dropbear port (or other SSH Server/Client) to NuttX

NuttX doesn't have a SSH Client/Server support yet.

Supporting a SSH server will open doors to let NuttX boards in the fields to be access remotely for maintenance

Adding support to SSH client will let low cost boards powered by NuttX and LVGL to become a remote console control for more advanced Linux server. 

Difficulty: Major
Project size: ~90 ~350 hour (smalllarge)
Potential mentors:
Alan Carvalho de Assis, mail: acassis (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

Add X11 graphic support on NuttX using NanoX

NanoX/Microwindows is a small graphic library what allow Unix/Linux X11 application to run on embedded systems that cannot support X-Server because it is too big. Add it to NuttX will allow many applications to be ported to NuttX. More importantly: it will allow FLTK 1.3 run on NuttX and that could big Dillo web browser.

Create a NuttX Distribution with Dynamic Binary (ELF) Loading

NuttX is very Unix/Linux-like RTOS for microcontrollers and it supports dynamic loading of binaries and libraries. It makes perfect sense to have the possibilities to create a NuttX Distros similar to what exists for Linux.

In fact there is already a proposal here: https://github.com/apache/nuttx/issues/17351

Goals:

1) Test ELF Loading in the current NuttX mainline
2) Create an application that will be downloaded and updated the existing version on the board
3) Add Library support on NuttX/NuttX-Apps (use Android Makefile Library building as reference)
 

Difficulty: Major
Project size: ~350 ~175 hour (largemedium)
Potential mentors:
Alan Carvalho de Assis, mail: acassis (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

Wireguard port to NuttX

Wireguard is a light VPN solution for Linux and microcontrollers.

Porting wireguard for NuttX will allow remote and secure access to NuttX devices.


Projects to be used as reference:

https://github.com/ciniml/WireGuard-ESP32-Arduino

https://github.com/smartalock/wireguard-lwip

Micro-ROS integration on NuttX

Micro-ROS (https://micro.ros.org) is a ROS2 support to Microcontrollers. Initially the project was developed over NuttX by Bosch and other EU organizations. Later on they added support to FreeRTOS and Zephyr. After that NuttX support started ageing and we didn't get anyone working to fix it (with few exceptions like Roberto Bucher work to test it with pysimCoder).

Difficulty: Major
Project size: ~350 ~175 hour (largemedium)
Potential mentors:
Alan Carvalho de Assis, mail: acassis (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org
Add X11 graphic

TinyGL support on NuttX

using NanoX

NanoX/Microwindows TinyGL is a small graphic library what allow Unix/Linux X11 application to run on embedded systems that cannot support X-Server because it is too big. Add it to NuttX will allow many applications to be ported to NuttX. More importantly: it will allow FLTK 1.3 run on NuttX and that could big Dillo web browser3D graphical library created by Fabrice Bellard (same creator of QEMU) designed for embedded system. Currently NuttX RTOS doesn´t have a 3D library and this could enable people to add more 3D programs on NuttX.

Difficulty: Major
Project size: ~175 hour (medium)
Potential mentors:
Alan Carvalho de Assis, mail: acassis (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

Add multi-user support for NuttX

Currently NuttX only support a single user. Also there is no file mode and file owner support.

In fact file mode is already defined in some places in the fs/ but it is not used.

This feature will make NuttX even yet more Unix/Linux-like.

Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Alan Carvalho de Assis, mail: acassis (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

Analog (ADC/DAC) interfaces unification and better API

Wireguard port to NuttX

Wireguard is a light VPN solution for Linux and microcontrollers.

Porting wireguard for NuttX will allow remote and secure access to NuttX devices.

Projects to be used as reference:

The issue was discussed and is tracked in a GitHub issue

https://github.com/ciniml/WireGuard-ESP32-Arduinohttps://github.com/smartalock/wireguard-lwipapache/nuttx/issues/16916

Difficulty: Major
Project size: ~175 ~350 hour (mediumlarge)
Potential mentors:
Alan Carvalho de AssisMichal Lenc, mail: acassis michallenc (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

NXBoot algorithm extension for two partitions

Currently NuttX bootloader NXBoot requires three partitions to function properly. This is a trade of between better update speed and higher external memory capacity requirements.

The algorithm isn't suited for devices with small or even none external memory. A different algorithm that uses just two partitions (primary which runs the image) and update (where the update is uploaded) could be used for devices that use only internal memory. It would result in slower update process, but save memory space

TinyGL support on NuttX

TinyGL is a small 3D graphical library created by Fabrice Bellard (same creator of QEMU) designed for embedded system. Currently NuttX RTOS doesn´t have a 3D library and this could enable people to add more 3D programs on NuttX.

Difficulty: Major
Project size: ~175 ~350 hour (mediumlarge)
Potential mentors:
Alan Carvalho de AssisMichal Lenc, mail: acassis michallenc (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

Add multi-user support for NuttX

Currently NuttX only support a single user. Also there is no file mode and file owner support.

In fact file mode is already defined in some places in the fs/ but it is not used.

This feature will make NuttX even yet more Unix/Linux-like.

Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Alan Carvalho de Assis, mail: acassis (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

Port NuttX to the Raspberry Pi 4B

This project tackles more of the port of NuttX to the Raspberry Pi 4B, like including networking support and more user demos. This will help NuttX demonstrate its scalability, provide a great target for regression testing multiple features at once, and unlock new RTOS applications that have not been previously tackled by NuttX (multimedia, large memory programs, etc.).

GitHub issue tracker here:

Analog (ADC/DAC) interfaces unification and better API

The issue was discussed and is tracked in a GitHub issue https://github.com/apache/nuttx/issues/1691618507

Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Michal LencMatteo Golin, mail: michallenc linguini (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

NXBoot algorithm extension for two partitions

Currently NuttX bootloader NXBoot requires three partitions to function properly. This is a trade of between better update speed and higher external memory capacity requirements.

The algorithm isn't suited for devices with small or even none external memory. A different algorithm that uses just two partitions (primary which runs the image) and update (where the update is uploaded) could be used for devices that use only internal memory. It would result in slower update process, but save memory space.

Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Michal Lenc, mail: michallenc (at) apache.org
Project Devs, mail: dev (at) nuttx.apache.org

Apache Wayang

Make Wayang more datalake-friendly

Background

Apache Wayang is a cross-platform data processing framework that allows users to execute analytics pipelines across multiple heterogeneous execution engines such as Apache Spark, Apache Flink, and relational database systems. Wayang’s optimizer automatically selects where to execute a pipeline and enables hybrid pipelines where part of it can be executed in one platform and part of it in another.

Wayang’s architecture is built around a pluggable backend model. Each execution engine is integrated via a dedicated backend implementation that translates Wayang’s logical operators into engine-specific physical operators.

Current execution engines (platforms) that Wayang supports include: JDBC-based databases, Spark, Flink, Tensorflow, Giraph. 

Project Goal

Design and implement one or more new execution engine backends to enable Apache Wayang to work in data lake environments.

Potential target engines include (depending on feasibility and community discussion):

  • Apache Datafusion
  • Trino / Presto
  • Dremio
  • BigQuery

The project includes:

  • Implementing the backend abstraction layer,
  • Mapping Wayang logical operators to the new engine’s execution model,
  • Integrating cost estimation for the optimizer.

Difficulty: Medium
Project size: Depends on the number of platforms. It can be 175 (medium) or ~350 hours (large)
Potential mentors:

  • Zoi Kaoudi — zkaoudi (at) apache.org
  • Juri Petersen — juri (at) apache.org
  • Community — dev (at) wayang.apache.org

Port NuttX to the Raspberry Pi 4B

This project tackles more of the port of NuttX to the Raspberry Pi 4B, like including networking support and more user demos. This will help NuttX demonstrate its scalability, provide a great target for regression testing multiple features at once, and unlock new RTOS applications that have not been previously tackled by NuttX (multimedia, large memory programs, etc.).

GitHub issue tracker here: https://github.com/apache/nuttx/issues/18507
Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Matteo GolinZoi Kaoudi, mail: linguini zkaoudi (at) apache.org
Project Devs, mail: dev (at) nuttxwayang.apache.org

Support for a Dataframes API

Background

Apache Wayang

...

is a cross-platform data processing framework that lets users write data analytics tasks once and execute them efficiently across diverse execution engines such as Apache Spark, Apache Flink, relational databases, and others. It abstracts heterogeneous backends and can enable efficient hybrid execution across different execution engines.

Currently, Wayang supports dataflow-style APIs in Java, Scala, and Python and an SQL API. However, there is no high-level DataFrame API — a programmatic abstraction widely used in modern data processing ecosystems (e.g., Spark DataFrames, Pandas, R DataFrames) — that lets users express relational transformations over structured datasets in a fluent, tabular style.

A DataFrame API for Wayang would dramatically improve usability for data engineers and scientists, making Wayang accessible to users familiar with DataFrame programming paradigms while preserving its powerful cross-platform optimization capabilities.

Project Goal

Implement a DataFrame API for Apache Wayang that:

  • Represents structured data in a tabular abstraction (rows & columns),
  • Supports common relational and analytical operations (select, filter, join, groupBy, aggregate, etc.),
  • Can compile DataFrame operations into Wayang plans executed across backends transparently,
  • Includes comprehensive documentation and examples.

Outcomes & Impact

By the end of GSoC, Wayang will have its first robust DataFrame API — a major usability milestone that bridges structured analytics with cross-platform execution. This will enhance adoption, unlock new classes of applications, and position Wayang as a friendly high-level programming environment in addition to its optimizer backend strengths

Make Wayang more datalake-friendly

Background

Apache Wayang is a cross-platform data processing framework that allows users to execute analytics pipelines across multiple heterogeneous execution engines such as Apache Spark, Apache Flink, and relational database systems. Wayang’s optimizer automatically selects where to execute a pipeline and enables hybrid pipelines where part of it can be executed in one platform and part of it in another.

Wayang’s architecture is built around a pluggable backend model. Each execution engine is integrated via a dedicated backend implementation that translates Wayang’s logical operators into engine-specific physical operators.

Current execution engines (platforms) that Wayang supports include: JDBC-based databases, Spark, Flink, Tensorflow, Giraph. 

Project Goal

Design and implement one or more new execution engine backends to enable Apache Wayang to work in data lake environments.

Potential target engines include (depending on feasibility and community discussion):

  • Apache Datafusion
  • Trino / Presto
  • Dremio
  • BigQuery

The project includes:

  • Implementing the backend abstraction layer,
  • Mapping Wayang logical operators to the new engine’s execution model,
Integrating cost estimation for the optimizer

.


Difficulty: Medium
Project size: Depends on the number of platforms. It can be 175 (medium) or ~350 hours (large)
Potential mentors:

  • Zoi Kaoudi — zkaoudi (at) apache.org

(Large)
Potential mentors:

  • Zoi Kaoudi — zkaoudi (at)
  • Juri Petersen — juri (at) apache.org
  • Community — dev (at) wayang. apache.org
Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Zoi Kaoudi, mail: zkaoudi (at) apache.org
Project Devs, mail: dev (at) wayang.apache.org
Support for a Dataframes API

Implement a JDBC driver for Wayang

Background

Apache Wayang is a cross-platform data processing framework that lets enables users to write data analytics tasks once and execute them efficiently across diverse multiple heterogeneous execution engines such as Apache (e.g., Spark, Apache Flink, relational databasesJava Streams, and others. It abstracts heterogeneous backends and can enable efficient hybrid execution across different execution engines). In addition, Wayang optimizes execution plans across platforms and can split pipelines to be executed among multiple backends to optimize performance.

Currently, Wayang supports dataflow-style APIs in Java, Scala, and Python and an SQL API. However, there is no high-level DataFrame API — a programmatic abstraction widely used in modern data processing ecosystems (e.g., Spark DataFrames, Pandas, R DataFrames) — that lets users express relational transformations over structured datasets in a fluent, tabular style.

A DataFrame API for Wayang would dramatically improve usability for data engineers and scientists, making Wayang accessible to users familiar with DataFrame programming paradigms while preserving its powerful cross-platform optimization capabilities.

Project Goal

Implement a DataFrame API for Apache Wayang that:

  • Represents structured data in a tabular abstraction (rows & columns),
  • Supports common relational and analytical operations (select, filter, join, groupBy, aggregate, etc.),
  • Can compile DataFrame operations into Wayang plans executed across backends transparently,
  • Includes comprehensive documentation and examples.

Outcomes & Impact

By the end of GSoC, Wayang will have its first robust DataFrame API — a major usability milestone that bridges structured analytics with cross-platform execution. This will enhance adoption, unlock new classes of applications, and position Wayang as a friendly high-level programming environment in addition to its optimizer backend strengths.

provides programmatic APIs (Java/Scala) and SQL support. However, it does not expose a standard JDBC interface that would allow external tools to connect to Wayang as if it were a relational database.

Many analytics tools rely on JDBC to communicate with query engines. Implementing a JDBC driver for Wayang would allow users to issue SQL queries to Wayang using standard database tooling.

Project Goal

Design and implement a JDBC driver for Apache Wayang that allows users to:

  • Establish a JDBC connection to a Wayang instance,
  • Submit SQL queries via standard JDBC interfaces,
  • Retrieve results using ResultSet,
  • Access metadata through DatabaseMetaData,
  • Integrate Wayang with existing SQL-based tools and BI platforms.

The driver should delegate incoming SQL queries to the SQL api provided by Wayang. 


Difficulty: Minor
Project size: ~90 hours (smallDifficulty: Medium
Project size: ~350 hours (Large)
Potential mentors:

  • Zoi Kaoudi — zkaoudi zoka (at) apache.org
Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Zoi Kaoudi, mail: zkaoudi (at) apache.org
Project Devs, mail: dev (at) wayang.apache.org
Implement a JDBC driver for Wayang

Instance-Aware Platform Registration and Optimization

Background: 
Apache Wayang is a cross-platform data processing framework that enables users to write data analytics tasks once and execute them execute analytics pipelines across multiple heterogeneous execution engines. Currently, Wayang’s architecture is "platform-type centric," treating each technology (e.g., Spark, Flink, Java Streams, and others). In addition, Wayang optimizes execution plans across platforms and can split pipelines to be executed among multiple backends to optimize performance.

Currently, Wayang provides programmatic APIs (Java/Scala) and SQL support. However, it does not expose a standard JDBC interface that would allow external tools to connect to Wayang as if it were a relational database.

Many analytics tools rely on JDBC to communicate with query engines. Implementing a JDBC driver for Wayang would allow users to issue SQL queries to Wayang using standard database tooling.

Project Goal

Design and implement a JDBC driver for Apache Wayang that allows users to:

  • Establish a JDBC connection to a Wayang instance,
  • Submit SQL queries via standard JDBC interfaces,
  • Retrieve results using ResultSet,
  • Access metadata through DatabaseMetaData,
  • Integrate Wayang with existing SQL-based tools and BI platforms.

The driver should delegate incoming SQL queries to the SQL api provided by Wayang. 

Difficulty: Minor
Project size: ~90 hours (small)
Potential mentors:

or RDBMS) as a single global entity.

In modern distributed environments, resources are often partitioned across multiple instances of the same technology—such as separate database clusters for different regions or compute clusters with varying hardware profiles. Currently, Wayang cannot natively distinguish between these instances, preventing the optimizer from routing tasks based on specific instance metadata or data proximity.

Project Goal:
The objective is to evolve Wayang from a "type-based" registration model to an instance-aware model. This allows Wayang to manage and differentiate between multiple deployments of the same execution engine within a single session.

Key Objectives:

Identity & Registration: Enhance the core registration service to support unique instance identifiers, allowing multiple deployments of the same platform type to coexist.

Scoped Configuration: Implement a hierarchical configuration mechanism to tie parameters (connection strings, resource limits, performance weights) to specific instance IDs.

Optimization Granularity: Update the cost-estimation logic to recognize these distinct instances, enabling the optimizer to make informed decisions based on the specific characteristics of individual backends.

Difficulty: Medium
Project size: 375 hours (Large)


Potential mentors:

  • Haralampos Gavriilidis — harryg (at) apache.org
  • Zoi Kaoudi — zkaoudi Zoi Kaoudi — zoka (at) apache.org
Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Zoi Kaoudi, mail: zkaoudi (at) apache.org
Project Devs, mail: dev (at) wayang.apache.org

Mahout

Add ZZFeatureMap Encoding for QDP

Backgroud

ZZFeatureMap is the most widely-used data encoding in quantum machine learning. It's the default in Qiskit and PennyLane for quantum kernel methods and variational classifiers.

QDP currently supports amplitude, angle, basis, and IQP encodings. Adding ZZFeatureMap completes our QML encoding suite.

What is ZZFeatureMap?

Maps classical features to quantum states using:
1. Hadamard gates (superposition)
2. RZ gates (single-qubit rotations)
3. ZZ interactions (two-qubit entanglement)
4. Repetition layers for expressivity

Tracked github issue

https://github.com/apache/mahout/issues/1008

Difficulty: Major
Project size: ~350 hour (large)
Potential mentors:
Ryan Huang, mail: hcr (at) apache.org
Project Devs, mail: dev (at) mahout.apache.org

...