Convert COBOL applications to Java following mainframe modernization best practices. Handles CICS transactions, batch programs, VSAM files, DB2 integration, and data structure migrations.
Comprehensive guide for converting COBOL mainframe applications to modern Java, preserving business logic while adopting cloud-native architecture patterns. This skill is specifically tailored for the CardDemo application but provides general best practices applicable to any COBOL modernization effort.
mappings/*.md files for conversion patterns1.1 Analyze COBOL Program
# Run analysis script
./scripts/analyze-cobol.sh app/cbl/COSGN00C.cbl
Output includes:
1.2 Identify Conversion Pattern
Refer to decision tree:
| COBOL Type | Conversion Target | Reference |
|---|---|---|
| CICS Online Transaction | Spring Boot REST Controller | mappings/cics-to-spring.md |
| Batch Program (Sequential) | Spring Batch Job | examples/batch-conversion-example.md |
| Copybook | Java POJO / Record | examples/copybook-conversion-example.md |
| VSAM KSDS | SQL Table with Primary Key | mappings/vsam-to-sql.md |
| DB2 Embedded SQL | JPA Entity / JDBC Template | mappings/data-types.md |
1.3 Review Pre-Conversion Checklist
See checklists/pre-conversion.md for complete checklist.
2.1 Convert Copybooks to POJOs
COBOL Copybook Example:
01 CUSTOMER-RECORD.
05 CUST-ID PIC 9(10).
05 CUST-NAME PIC X(50).
05 CUST-BALANCE PIC S9(13)V99 COMP-3.
05 CUST-STATUS PIC X(01).
Java POJO (Converted):
@Entity
@Table(name = "CUSTOMER")
public class Customer {
@Id
private Long custId;
@Column(length = 50)
private String custName;
@Column(precision = 15, scale = 2)
private BigDecimal custBalance; // Note: BigDecimal for COMP-3
private String custStatus;
// Getters, setters, constructors
}
⚠️ Critical: Data Type Mappings
| COBOL Type | Java Type | Notes |
|---|---|---|
PIC 9(n) |
Long or Integer |
Use Long for n > 9 |
PIC X(n) |
String |
Trim trailing spaces |
PIC S9(n)V99 COMP-3 |
BigDecimal |
Always use BigDecimal for financial data |
PIC S9(n) COMP |
Integer or Long |
Binary integer |
PIC S9(n) COMP-5 |
Long |
Binary long |
See mappings/data-types.md for complete table.
3.1 CICS Transaction → Spring Boot REST Service
COBOL CICS Program Structure:
IDENTIFICATION DIVISION.
PROGRAM-ID. COSGN00C.
*> Signon transaction
WORKING-STORAGE SECTION.
01 WS-USER-ID PIC X(08).
01 WS-USER-PWD PIC X(08).
PROCEDURE DIVISION.
EXEC CICS RECEIVE MAP('COSGN0A')
MAPSET('COSGN00')
INTO(WS-MAP-AREA)
END-EXEC.
*> Validate user credentials
PERFORM VALIDATE-USER.
*> Read security file
EXEC CICS READ FILE('USRSEC')
RIDFLD(WS-USER-ID)
INTO(WS-USER-RECORD)
END-EXEC.
Converted Java Spring Boot:
@RestController
@RequestMapping("/api/auth")
public class AuthenticationController {
@Autowired
private UserSecurityService userSecurityService;
@PostMapping("/signin")
public ResponseEntity<SigninResponse> signin(
@RequestBody @Valid SigninRequest request) {
// Validate user credentials (business logic preserved)
User user = userSecurityService.validateUser(
request.getUserId(),
request.getPassword()
);
if (user == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(new SigninResponse("Invalid credentials"));
}
// Generate token (modern approach vs CICS commarea)
String token = jwtService.generateToken(user);
return ResponseEntity.ok(
new SigninResponse(token, user)
);
}
}
Key Conversion Patterns:
@PostMapping with @RequestBodyResponseEntity with JSONfindById()See mappings/cics-to-spring.md for complete patterns.
3.2 Batch Program → Spring Batch
COBOL Batch Structure:
PROGRAM-ID. CBTRN02C.
*> Post daily transactions
PROCEDURE DIVISION.
OPEN INPUT DALYTRAN-FILE
OPEN I-O TRANSACT-FILE
PERFORM UNTIL EOF
READ DALYTRAN-FILE
PERFORM PROCESS-TRANSACTION
WRITE TRANSACT-FILE FROM WS-TRANSACTION
END-PERFORM.
Java Spring Batch:
@Configuration
public class TransactionPostingBatchJob {
@Bean
public Job postTransactionsJob(JobRepository jobRepository,
Step processTransactionStep) {
return new JobBuilder("postTransactions", jobRepository)
.start(processTransactionStep)
.build();
}
@Bean
public Step processTransactionStep(
JobRepository jobRepository,
PlatformTransactionManager txManager,
ItemReader<DailyTransaction> reader,
ItemProcessor<DailyTransaction, Transaction> processor,
ItemWriter<Transaction> writer) {
return new StepBuilder("processTransactionStep", jobRepository)
.<DailyTransaction, Transaction>chunk(100, txManager)
.reader(reader) // Read from staging table
.processor(processor) // Business logic
.writer(writer) // Write to main table
.build();
}
}
See examples/batch-conversion-example.md for complete example.
4.1 VSAM to SQL
Refer to mappings/vsam-to-sql.md for:
4.2 Data Type Transformations
# Use provided script for data migration
python3 scripts/generate-test-data.py \
--copybook app/cpy/CUSTOMER.cpy \
--output test-data.json \
--records 1000
See checklists/data-migration.md for complete process.
5.1 Unit Testing
Create equivalent test cases:
@SpringBootTest
class SigninServiceTest {
@Test
void testValidCredentials_Success() {
// Given
String userId = "USER0001";
String password = "PASSWORD";
// When
User user = signinService.authenticate(userId, password);
// Then
assertNotNull(user);
assertEquals(userId, user.getUserId());
}
@Test
void testInvalidPassword_ThrowsException() {
// Test invalid credentials
assertThrows(InvalidCredentialsException.class, () -> {
signinService.authenticate("USER0001", "WRONG");
});
}
}
5.2 Integration Testing
Use TestRestTemplate to test full flow:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SigninControllerIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void testSigninFlow_EndToEnd() {
SigninRequest request = new SigninRequest("USER0001", "PASSWORD");
ResponseEntity<SigninResponse> response = restTemplate
.postForEntity("/api/auth/signin", request, SigninResponse.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody().getToken());
}
}
5.3 Data Validation
Compare COBOL output vs Java output using provided scripts.
See checklists/post-conversion-validation.md for complete checklist.
❌ Problem:
double balance = 123.45; // Can cause rounding errors
✅ Solution:
BigDecimal balance = new BigDecimal("123.45"); // Precise decimal
❌ Problem: Direct line-by-line translation
// Bad: Procedural COBOL-style Java
public class SignonProgram {
public void main() {
receiveMap();
if (eibaid == DFHENTER) {
processEnterKey();
}
}
}
✅ Solution: Object-oriented Java
@RestController
public class AuthenticationController {
@PostMapping("/signin")
public ResponseEntity<SigninResponse> signin(@RequestBody SigninRequest req) {
// Clean, RESTful design
}
}
⚠️ Remember: CICS is pseudo-conversational. Don't try to maintain server-side state.
✅ Solution: Use JWT tokens or session management
✅ Solution: Always specify encoding when reading mainframe data:
String data = new String(bytes, Charset.forName("IBM037")); // EBCDIC
See references/common-pitfalls.md for complete list.
# Upload COBOL source to S3
aws s3 cp app/cbl/ s3://my-bucket/cobol-source/ --recursive
# Start AWS Transform project
aws transform create-project \
--name carddemo-modernization \
--source-location s3://my-bucket/cobol-source/
# Get analysis results
aws transform get-analysis --project-id <project-id>
AWS Transform provides:
See references/aws-transform-guide.md for setup instructions.
Copybook Converters:
cobol2java-copybook-converter (GitHub: manuelscurti/cobol2java-copybook-converter)JRecord (GitHub: bmTas/JRecord)cb2java (Dynamic parser)Testing:
AWS Services:
Before marking conversion complete:
See examples/ directory for:
Recommended order for CardDemo conversion:
Each phase allows testing and validation before proceeding.
This skill should be updated as: