Skip to content

Migrate COBOL-Examples to Spring Boot application#73

Open
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1774458480-spring-boot-migration
Open

Migrate COBOL-Examples to Spring Boot application#73
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1774458480-spring-boot-migration

Conversation

@devin-ai-integration
Copy link
Copy Markdown

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

Summary

Adds a new spring-boot-migration/ directory containing a complete Java Spring Boot 3.2 application that migrates the functionality from the existing COBOL programs. The original COBOL code is untouched. The migration covers 5 phases:

  • Phase 1 — Data Layer: Account JPA entity, Spring Data repository, service, and REST controller replacing the embedded SQL menu-driven program (sql/sql_example.cbl). SQL schema and seed data derived from create_test_db.sql.
  • Phase 2 — Business Logic: SearchService (binary search replacing SEARCH ALL), FileSortService (merge/sort replacing COBOL MERGE/SORT), StringUtils (trim, unstring, isNumeric, numval), ReportService (HTML/text output replacing Report Writer).
  • Phase 3 — Serialization: XmlSerializationService and JsonSerializationService using Jackson, replacing XML GENERATE and JSON GENERATE.
  • Phase 4 — Architecture: SubProgramService mapping COBOL CALL BY CONTENT/BY REFERENCE/CANCEL to Spring service patterns.
  • Phase 5 — Testing: 71 tests (unit + integration), all passing. Integration tests use H2 in PostgreSQL compatibility mode.

Review & Testing Checklist for Human

  • SubProgramService is not thread-safe. It stores mutable state in singleton bean fields to simulate COBOL WORKING-STORAGE. Concurrent HTTP requests would corrupt shared state. Decide whether this needs @Scope("prototype"), synchronization, or a different pattern before any production use.
  • StringUtils.unstring() is a simplification. COBOL UNSTRING supports pointer tracking, tallying, multiple delimiters, delimiter-in/count-in — the Java version only does a basic String.split(). Verify this is acceptable for the migration scope.
  • Hardcoded DB credentials in application.yml (password: password). Fine for a scaffold/demo, but should be externalized before any real deployment.
  • No unit tests for SubProgramService or ReportService. Phase 4 service logic is untested. Consider adding tests for callByContent, callByReference, and reset behavior.
  • Verify SerializationRecord dual-annotation behavior. The same model is used with both XmlMapper (where enabled is an XML attribute) and ObjectMapper (where it's a JSON field). Spot-check the actual XML/JSON output to confirm field names and structure match COBOL output.

Suggested test plan: Clone the branch, run mvn test in spring-boot-migration/, then stand up a local PostgreSQL with the seed data and hit the REST endpoints (/api/accounts, /api/accounts?search=John, /api/serialize/xml, /api/reports/customers) to verify end-to-end behavior.

Notes

  • The ReportController returns hardcoded sample data rather than reading from a file or DB — this is intentional as a demo endpoint matching the COBOL report writer example.
  • isNumeric() trims whitespace before checking, which diverges from raw COBOL IS NUMERIC (which fails on trailing spaces). Tests document this difference.
  • H2 PostgreSQL compatibility mode is used for integration tests; edge-case SQL behavior may differ from real PostgreSQL.

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


Open with Devin

- Phase 1: Data Layer - Account entity, repository, service, controller, SQL
- Phase 2: Business Logic - SearchService, FileSortService, StringUtils, ReportService
- Phase 3: Serialization - XmlSerializationService, JsonSerializationService
- Phase 4: Architecture - SubProgramService (CALL/CANCEL pattern migration)
- Phase 5: Testing - 71 unit and integration tests, all passing
- README with API docs and COBOL-to-Java mapping reference

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 2 potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines +26 to +27
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.

🔴 Race condition: mutable singleton state in SubProgramService without synchronization

The SubProgramService is a Spring @Service (singleton by default) with mutable instance fields wsTestItem1 and wsTestItem2 that are read and written by callByContent(), callByReference(), and reset() without any synchronization. In a Spring Boot web application, all singletons are shared across request-handling threads. If this service is injected and called concurrently (e.g., by a future controller), one thread's call to callByContent("A", "B") can overwrite the working-storage fields while another thread is building its SubProgramResult, returning corrupted data. The COBOL WORKING-STORAGE model is inherently single-threaded and cannot be directly mapped to a shared singleton bean.

Prompt for agents
The SubProgramService models COBOL WORKING-STORAGE (persistent per-process state) as Spring singleton fields. This is fundamentally thread-unsafe in a multi-threaded web server. Options to fix:

1. Make the bean request-scoped (@Scope("request")) so each HTTP request gets its own instance, closely matching COBOL's single-threaded model.
2. Add synchronization (synchronized methods or ReentrantLock) if the singleton pattern is kept.
3. Move the mutable state into a separate holder object managed per-call or per-session.

Option 1 is the most semantically correct migration of COBOL WORKING-STORAGE to a web application context. The affected methods are callByContent(), callByReference(), reset(), getWsTestItem1(), and getWsTestItem2() in SubProgramService.java.
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.

Acknowledged — this is already called out in the PR description's review checklist as item #1. The singleton + mutable state pattern is an intentional 1:1 mapping of COBOL WORKING-STORAGE for demonstration purposes. This service is not currently exposed via any REST endpoint. For production use, @Scope("request") would be the most semantically correct fix. Leaving as-is pending human reviewer decision on scope.

Comment on lines +110 to +115
writer.write(String.format("%d,%s,%s,%d,%s",
record.getCustomerId(),
record.getLastName(),
record.getFirstName(),
record.getContractId(),
record.getComment()));
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.

🟡 CSV write/read round-trip breaks when field values contain commas

writeToCsv at FileSortService.java:110 writes field values directly into comma-separated format without any quoting or escaping. If lastName, firstName, or comment contain a comma (e.g., "Smith, Jr."), the output line will have extra columns. When readFromCsv parses such a line with line.split(",", -1), the field boundaries are corrupted — string fields get truncated and Integer.parseInt() on parts[3] will throw a NumberFormatException because it receives a string fragment instead of the contractId integer.

Example of the data corruption

Write: CustomerRecord(customerId=1, lastName="Smith, Jr.", firstName="John", contractId=100, comment="test")
Produces: 1,Smith, Jr.,John,100,test
Read splits into: ["1", "Smith", " Jr.", "John", "100", "test"]
Result: parts[3] = "John"Integer.parseInt throws NumberFormatException

Prompt for agents
The writeToCsv method in FileSortService.java writes raw field values separated by commas, without quoting fields that may contain the delimiter. The readFromCsv method uses a naive split on comma. If any string field (lastName, firstName, comment) contains a comma, the round-trip is broken.

To fix this, either:
1. Use a proper CSV library (e.g., Apache Commons CSV or OpenCSV) for both reading and writing.
2. Implement basic CSV quoting: wrap each string field in double quotes and escape any internal double quotes by doubling them. Update readFromCsv to handle quoted fields accordingly.

The affected methods are writeToCsv() (lines 107-119) and readFromCsv() (lines 77-96) in FileSortService.java.
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.

Valid observation. The original COBOL used fixed-length records (no delimiters inside fields), so the naive CSV approach works for the migrated data. For robustness with arbitrary input, switching to Apache Commons CSV or adding RFC 4180 quoting would be the right fix. Leaving as-is pending human reviewer input on whether this edge case needs handling for the migration scope.

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