Migrate COBOL-Examples to Spring Boot application#73
Migrate COBOL-Examples to Spring Boot application#73devin-ai-integration[bot] wants to merge 1 commit intomainfrom
Conversation
- 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 EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| private String wsTestItem1 = ""; | ||
| private String wsTestItem2 = ""; |
There was a problem hiding this comment.
🔴 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.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
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.
| writer.write(String.format("%d,%s,%s,%d,%s", | ||
| record.getCustomerId(), | ||
| record.getLastName(), | ||
| record.getFirstName(), | ||
| record.getContractId(), | ||
| record.getComment())); |
There was a problem hiding this comment.
🟡 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.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
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.
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:AccountJPA 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 fromcreate_test_db.sql.SearchService(binary search replacingSEARCH ALL),FileSortService(merge/sort replacing COBOLMERGE/SORT),StringUtils(trim, unstring, isNumeric, numval),ReportService(HTML/text output replacing Report Writer).XmlSerializationServiceandJsonSerializationServiceusing Jackson, replacingXML GENERATEandJSON GENERATE.SubProgramServicemapping COBOLCALL BY CONTENT/BY REFERENCE/CANCELto Spring service patterns.Review & Testing Checklist for Human
SubProgramServiceis 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. COBOLUNSTRINGsupports pointer tracking, tallying, multiple delimiters, delimiter-in/count-in — the Java version only does a basicString.split(). Verify this is acceptable for the migration scope.application.yml(password: password). Fine for a scaffold/demo, but should be externalized before any real deployment.SubProgramServiceorReportService. Phase 4 service logic is untested. Consider adding tests forcallByContent,callByReference, andresetbehavior.SerializationRecorddual-annotation behavior. The same model is used with bothXmlMapper(whereenabledis an XML attribute) andObjectMapper(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 testinspring-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
ReportControllerreturns 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 COBOLIS NUMERIC(which fails on trailing spaces). Tests document this difference.Link to Devin session: https://app.devin.ai/sessions/b848138e7931434cb4acc51e5b4864a3
Requested by: @jerryoliphant-cog