Skip to content

feat(poc): GSoC 2026 - Thrift to Spring direct injection migration PoC (Users module)#3984

Draft
Aman-Cool wants to merge 1 commit intoeclipse-sw360:mainfrom
Aman-Cool:gsoc/thrift-migration-poc
Draft

feat(poc): GSoC 2026 - Thrift to Spring direct injection migration PoC (Users module)#3984
Aman-Cool wants to merge 1 commit intoeclipse-sw360:mainfrom
Aman-Cool:gsoc/thrift-migration-poc

Conversation

@Aman-Cool
Copy link
Contributor

GSoC 2026 PoC: Remove Apache Thrift - Direct Spring Service Injection (Users Module)

What this does and why

Every call between SW360's REST server and a backend service currently goes through Apache Thrift over HTTP. For something as simple as getAllUsers(), the flow is:

Sw360UserService
  └─ getThriftUserClient()        <- new THttpClient per request, no pooling
       └─ TCompactProtocol binary <- serialize to binary wire format
            └─ HTTP POST to :8080 <- network hop on a single-machine deploy
                 └─ UserServlet   <- TServlet deserializes, dispatches
                      └─ UserHandler.getAllUsers()  <- one line of actual logic

The REST server and backend run on the same host in every deployment. None of that protocol machinery is earning its complexity. This project replaces it with direct Spring bean injection:

Sw360UserService
  └─ userHandler.getAllUsers()  <- in-process call, zero overhead
       └─ UserDatabaseHandler -> CouchDB

The external REST API is completely unchanged. This is an internal restructuring only.


What's in this PoC

I used the Users module to validate the pattern before proposing it for all 23 modules.

Sw360UserHandler - new plain Java interface

Replaces the Thrift-generated UserService.Iface. Method signatures are kept identical so UserHandler.java can adopt it with a one-word change, implements UserService.Iface becomes implements Sw360UserHandler. No business logic changes anywhere in the handler.

UserHandlerBean - the migrated handler

This is what backend/users/UserHandler.java looks like after migration. The entire diff is:

+@Service
-public class UserHandler implements UserService.Iface {
+public class UserHandler implements Sw360UserHandler {

-    public UserHandler() throws IOException {
-        db = new UserDatabaseHandler(DatabaseSettings.getConfiguredClient(), ...);
-    }
+    @Autowired
+    public UserHandler(UserDatabaseHandler db) {
+        this.db = db;
+    }

Every method body is untouched. UserServlet.java gets deleted, it only existed to wrap this handler in a TServlet.

DirectInjectionUserService - REST service layer after migration

Before:

public List<User> getAllUsers() {
    try {
        return getThriftUserClient().getAllUsers(); // new THttpClient every time
    } catch (TException e) {
        throw new RuntimeException(e);
    }
}

After:

public List<User> getAllUsers() {
    return userHandler.getAllUsers();
}

REST controllers are not touched at all.

Data model (User, RequestStatus, UserGroup)

Thrift-generated structs extend TBase and carry read(TProtocol) / write(TProtocol) / _Fields enum. After migration they become plain POJOs with Jackson annotations. All getter/setter names are preserved so the hundreds of existing call sites compile without changes. Enum ordinal values match the Thrift definition exactly so existing CouchDB data deserializes without a migration.

DirectInjectionTest - 7 passing @SpringBootTest tests

Full CRUD coverage through the injection chain. Runs in under 250ms; no network, no serialization, no Thrift binary on the classpath. Current tests mock ThriftClients via @MockBean; after migration they mock Sw360UserHandler directly, which is simpler and removes the Thrift dependency from the test classpath entirely.


How to run

cd poc/thrift-migration
mvn test
# Tests run: 7, Failures: 0, Errors: 0, Skipped: 0

No CouchDB, Docker, or Thrift binary required.


Open questions for mentors

  1. Module merge strategy - should the 23 backend WAR modules be restructured as library JARs imported by rest/resource-server, or should their source be physically moved into rest/resource-server/src/main/java/? Option A keeps each module's history clean and PRs reviewable in isolation. Option B reduces Maven module count significantly. This decision affects every pom.xml change in the migration so I'd like to confirm the preferred approach before starting.

  2. Data model package naming - the generated structs currently live in org.eclipse.sw360.datahandler.thrift.*. After migration they're plain POJOs. Should they stay in the thrift package to avoid touching ~500 import statements, or move to something like datahandler.model.* for clarity? Whichever direction is preferred, I want to set it in the first module PR so it's consistent throughout.

  3. SW360Exception - checked or unchecked? - currently it extends TException (checked). After migration it becomes a plain Java exception. Making it unchecked removes significant try-catch boilerplate from callers but requires touching method signatures across all 23 modules. The preference here shapes how the REST service layer handles errors throughout the entire migration.

  4. Long-running operations - the current Thrift read timeout is 10 minutes, presumably for things like FOSSology scan triggers and clearing report generation. After migration that timeout no longer exists at the protocol level. Are there SLAs or timeout requirements for those operations that need to be preserved via @Async with explicit task tracking, or is removing the timeout acceptable?

@Aman-Cool Aman-Cool force-pushed the gsoc/thrift-migration-poc branch from 23ed492 to 4ecd0bf Compare March 24, 2026 23:49
@Aman-Cool Aman-Cool force-pushed the gsoc/thrift-migration-poc branch from 4ecd0bf to c80f6ea Compare March 24, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant