Skip to content

feat: Migrate COBOL codebase to Java Spring Boot application#75

Open
devin-ai-integration[bot] wants to merge 2 commits intomainfrom
devin/1774620209-cobol-to-springboot-migration
Open

feat: Migrate COBOL codebase to Java Spring Boot application#75
devin-ai-integration[bot] wants to merge 2 commits intomainfrom
devin/1774620209-cobol-to-springboot-migration

Conversation

@devin-ai-integration
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot commented Mar 27, 2026

Summary

Adds a complete Java Spring Boot 3.2.5 application under spring-boot-migration/ that reimplements all COBOL example programs from this repository. The migration covers database access (SQL/ODBC → JPA), serialization (JSON GENERATE/XML GENERATE → Jackson/JAXB), string processing, data structures, file operations, report generation, and the terminal menu UI (→ REST API + CLI runner).

46 files added, organized into standard Spring Boot layers: model/, repository/, service/, controller/, util/, cli/, exception/. 61 unit/integration tests pass against an in-memory H2 database. No existing files were modified.

Key COBOL → Java mappings:

  • sql/sql_example.cblAccount entity + AccountRepository (JPA) + AccountService + REST endpoints
  • json_generate/, xml_generate/SerializationService with Jackson/JAXB
  • trim/, unstring/, is_numeric/, numval_test/StringUtils static methods
  • search/SearchUtils (binary + sequential search)
  • merge_sort/FileSortService, report_writer/ReportService
  • sub_program/SubProgramService with by-content/by-reference semantics
  • COBOL terminal menu → CliRunner (CommandLineRunner) + AccountController REST API

Updates since last revision

Fixed 3 bugs identified by Devin Review:

  • CustomerRecord.toFixedWidthString(): Changed %-5d%05d for integer fields to match COBOL pic 9(5) zero-padded format
  • SubProgramService: Added synchronized to processByContent(), processByReference(), and reset() to prevent race conditions on shared mutable singleton state
  • application.yml: Aligned default DB_PASSWORD to changeme to match docker-compose.yml, so Quick Start works without extra env var setup

Review & Testing Checklist for Human

  • Verify behavioral fidelity of COBOL translations: The Java implementations are reasonable approximations but have NOT been validated by running the original COBOL programs and comparing outputs. Key areas to spot-check: StringUtils.unstring() edge cases (consecutive delimiters, empty fields), FileSortService.parseFixedWidthRecord() field widths, ReportService pagination constants (DETAILS_PER_PAGE=37, PAGE_LIMIT=66 — do these match the COBOL RD definition?).
  • Golden test fixtures are unused: Files exist in src/test/resources/golden/ but no test actually loads or asserts against them. These are placeholder files, not real verification. Consider whether tests should actually compare Java output against these fixtures.
  • No test coverage for UtilityController or SerializationController: Only AccountController has @WebMvcTest tests. The utility and serialization endpoints are untested at the controller layer.
  • Flyway migration vs. original schema: Verify V1__create_accounts_table.sql exactly matches sql/create_test_db.sql column types, defaults, and sample data. Tests bypass Flyway (H2 with ddl-auto: create-drop), so migration SQL is never exercised.

Recommended test plan:

  1. Run ./gradlew test to confirm all 61 tests pass
  2. Start PostgreSQL via docker-compose up -d, run ./gradlew bootRun, and hit a few REST endpoints (GET /api/accounts, POST /api/serialize/json) to verify end-to-end
  3. For highest confidence: run the original COBOL programs, capture their output, and compare against the Java equivalents (especially JSON/XML serialization, merge/sort output, and report format)

Notes

  • Database credentials are configured via environment variables (DB_URL, DB_USERNAME, DB_PASSWORD) with defaults that match docker-compose.yml
  • Tests use H2 in-memory DB, not PostgreSQL, so SQL dialect differences could mask issues with the production path
  • No CI is configured for this repository; validation is local ./gradlew build only
  • The docker-compose.yml version key is deprecated in newer Compose versions but still functional

Link to Devin session: https://app.devin.ai/sessions/09da5e8db11f41e9a303bf7109570d2f
Requested by: @jerryoliphant-cog


Open with Devin

Complete 4-phase migration of COBOL-Examples to Spring Boot 3.2.5:

Phase 0 - Foundation:
- Spring Boot 3.2.5 project with Gradle 8.7
- PostgreSQL/JPA/Flyway configuration via env vars
- Flyway migration mirroring create_test_db.sql

Phase 1 - Data Layer:
- Account JPA entity and repository (replaces sql_example.cbl)
- JSON serialization with Jackson (replaces json_generate.cbl)
- XML serialization with JAXB (replaces xml_generate.cbl)
- Custom exception handling with ControllerAdvice

Phase 2 - Business Logic:
- StringUtils: trim, unstring, isNumeric, numval
- SearchUtils: binary/sequential search (replaces SEARCH ALL)
- FileSortService: merge/sort (replaces MERGE/SORT)
- ReportService: report generation (replaces Report Writer)
- SubProgramService: subprogram pattern (replaces CALL/CANCEL)
- Model classes: Customer, CustomerRecord, SearchItem

Phase 3 - Interface Layer:
- REST API: AccountController, SerializationController, UtilityController
- CLI Runner with interactive menu (mirrors COBOL terminal UI)

Phase 4 - Testing:
- 61 tests across 8 test classes (all passing)
- AccountRepositoryTest, AccountControllerTest (WebMvcTest)
- SerializationServiceTest, StringUtilsTest, SearchUtilsTest
- FileSortServiceTest, ReportServiceTest
- Golden test fixtures for output verification
- docker-compose.yml for local PostgreSQL

Co-Authored-By: Jerry Oliphant <jerry.oliphant@cognition.ai>
@devin-ai-integration
Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 3 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

* Formats the record as a fixed-width string similar to COBOL DISPLAY output.
*/
public String toFixedWidthString() {
return String.format("%-5d%-50s%-50s%-5d%-25s",
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔴 toFixedWidthString uses space-padded format instead of zero-padded for integer fields

The String.format("%-5d", ...) format specifier in toFixedWidthString() produces left-justified, space-padded integers (e.g., "1 " for customerId=1 and "5423 " for contractId=5423). However, COBOL pic 9(5) fields are zero-padded, as confirmed by the golden file at spring-boot-migration/src/test/resources/golden/merge_sort_merged.txt which shows 00001 and 05423. The correct format specifier is %05d. This means any files written by this method are incompatible with the expected COBOL fixed-width record format. The round-trip test at FileSortServiceTest.java:88-103 doesn't catch this because parseFixedWidthRecord trims before parsing, which handles both space-padded and zero-padded formats.

Suggested change
return String.format("%-5d%-50s%-50s%-5d%-25s",
return String.format("%05d%-50s%-50s%05d%-25s",
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed — changed to %05d for zero-padded output matching COBOL pic 9(5) format.

Comment on lines +27 to +31
public class SubProgramService {

// Working-storage equivalents: persist between calls
private String wsTestItem1 = "";
private String wsTestItem2 = "";
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔴 SubProgramService singleton has unsynchronized mutable state causing race conditions

The @Service-annotated SubProgramService is a singleton bean by default in Spring. Its mutable instance fields wsTestItem1 and wsTestItem2 (lines 30-31) are read and written in processByContent() and processByReference() without any synchronization. In the web application mode, concurrent HTTP requests to /api/utility/subprogram from the embedded Tomcat thread pool will race on these fields, causing one request to observe another request's state. For example, thread A could set wsTestItem1 = "foo", then thread B sets wsTestItem1 = "bar", and thread A's result.wsItem1AtEnd would read "bar" instead of "foo".

Prompt for agents
The SubProgramService is a Spring singleton bean with mutable instance fields (wsTestItem1, wsTestItem2) that are read/written by concurrent HTTP request threads without synchronization. This causes race conditions between concurrent callers of processByContent(), processByReference(), and reset().

Possible fixes:
1. Add synchronized keyword to all three methods (processByContent, processByReference, reset).
2. Use @Scope("prototype") or @RequestScope to give each request its own instance.
3. Store working-storage state in a ConcurrentHashMap keyed by session/request ID if per-user state is needed.

Option 1 is the simplest and closest to the original COBOL single-threaded semantics, but serializes all subprogram requests. Option 2 or 3 would be better for concurrent access but changes the COBOL working-storage persistence semantics (state wouldn't persist between separate HTTP requests).
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed — added synchronized to all three methods (processByContent, processByReference, reset). This matches the original single-threaded COBOL semantics where subprogram calls are serialized.

datasource:
url: ${DB_URL:jdbc:postgresql://localhost:5432/cobol_db_example}
username: ${DB_USERNAME:postgres}
password: ${DB_PASSWORD:}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🟡 Default DB password mismatch between application.yml and docker-compose.yml breaks Quick Start

The application.yml defaults DB_PASSWORD to an empty string (password: ${DB_PASSWORD:}), while docker-compose.yml defaults POSTGRES_PASSWORD to changeme (${DB_PASSWORD:-changeme}). When a user follows the README Quick Start without setting the DB_PASSWORD environment variable, the PostgreSQL container will be configured with password changeme, but the Spring Boot application will attempt to connect with an empty password, resulting in a connection failure.

Suggested change
password: ${DB_PASSWORD:}
password: ${DB_PASSWORD:changeme}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed — aligned application.yml default to changeme to match docker-compose.yml.

- CustomerRecord: use zero-padded format (%05d) for integer fields to match COBOL pic 9(5) behavior
- SubProgramService: add synchronized to all methods to prevent race conditions on shared mutable state
- application.yml: align default DB_PASSWORD with docker-compose.yml default (changeme)

Co-Authored-By: Jerry Oliphant <jerry.oliphant@cognition.ai>
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.

0 participants